diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-02-19 22:31:04 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-02-19 22:31:04 +0400 |
commit | afc56a0b10b52d2c8bdfd44257624d776db72f79 (patch) | |
tree | 25d194e29b2708ac1143b3dde6dfbeec7ef1d876 | |
parent | d6deca4e9d6bc7faff98644286571c7934324a9d (diff) |
copying bmesh dir on its own from bmesh branch
54 files changed, 35884 insertions, 0 deletions
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt new file mode 100644 index 00000000000..d8349012a5f --- /dev/null +++ b/source/blender/bmesh/CMakeLists.txt @@ -0,0 +1,137 @@ +# $Id: CMakeLists.txt 31746 2010-09-04 05:31:25Z joeedh $ +# ***** 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) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Jacques Beaurain. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern + operators + ../avi + ../blenfont + ../blenkernel + ../blenlib + ../blenloader + ../editors/include + ../editors/mesh + ../gpu + ../ikplugin + ../imbuf + ../makesdna + ../makesrna + ../modifiers + ../nodes + ../render/extern/include + ../../../extern/glew/include + ../../../intern/audaspace/intern + ../../../intern/bsp/extern + ../../../intern/decimation/extern + ../../../intern/elbeem/extern + ../../../intern/guardedalloc + ../../../intern/iksolver/extern + ../../../intern/memutil + ../../../intern/mikktspace + ../../../intern/opennl/extern + ../../../intern/smoke/extern + # XXX - BAD LEVEL CALL WM_api.h + ../../../source/blender/windowmanager +) + +set(INC_SYS + ${ZLIB_INCLUDE_DIRS} +) + +set(SRC + operators/bmo_bevel.c + operators/bmo_connect.c + operators/bmo_create.c + operators/bmo_dissolve.c + operators/bmo_dupe.c + operators/bmo_edgesplit.c + operators/bmo_extrude.c + operators/bmo_join_triangles.c + operators/bmo_mesh_conv.c + operators/bmo_mirror.c + operators/bmo_primitive.c + operators/bmo_removedoubles.c + operators/bmo_subdivide.c + operators/bmo_subdivide.h + operators/bmo_triangulate.c + operators/bmo_utils.c + + intern/bmesh_newcore.c + intern/bmesh_interp.c + intern/bmesh_iterators.c + intern/bmesh_iterators_inline.c + intern/bmesh_marking.c + intern/bmesh_mesh.c + intern/bmesh_mods.c + intern/bmesh_structure.h + intern/bmesh_construct.c + intern/bmesh_operators_private.h + intern/bmesh_structure.c + intern/bmesh_polygon.c + intern/bmesh_queries.c + intern/bmesh_opdefines.c + intern/bmesh_eulers.c + intern/bmesh_operators.c + intern/bmesh_private.h + intern/bmesh_walkers.c + intern/bmesh_walkers_impl.c + intern/bmesh_walkers_private.h + intern/bmesh_inline.c + + tools/BME_bevel.c + bmesh.h + bmesh_class.h + bmesh_error.h + bmesh_iterators.h + bmesh_marking.h + bmesh_operator_api.h + bmesh_operators.h + bmesh_queries.h + bmesh_walkers.h +) + +add_definitions(-DGLEW_STATIC) + +if(WITH_LZO) + add_definitions(-DWITH_LZO) + list(APPEND INC_SYS + ../../../extern/lzo/minilzo + ) +endif() + +if(WITH_LZMA) + add_definitions(-DWITH_LZMA) + list(APPEND INC_SYS + ../../../extern/lzma + ) +endif() + +if(MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") +endif() + +blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/bmesh/SConscript b/source/blender/bmesh/SConscript new file mode 100644 index 00000000000..91b7a72bb1f --- /dev/null +++ b/source/blender/bmesh/SConscript @@ -0,0 +1,40 @@ +#!/usr/bin/python +Import ('env') + +cflags='' +""" +sources = ['intern/bmesh_eulers.c'] +sources.append('intern/bmesh_mesh.c') +sources.append('intern/bmesh_polygon.c') +sources.append('intern/bmesh_structure.c') +sources.append('intern/bmesh_marking.c') + +sources.append('intern/bmesh_construct.c') +sources.append('intern/bmesh_interp.c') +sources.append('intern/bmesh_filters.c') +sources.append('intern/bmesh_iterators.c') +sources.append('intern/bmesh_mods.c') +sources.append('intern/bmesh_queries.c') +sources.append('intern/bmesh_operators.c') +""" +#sources.append('api/BME_walkers.c') + + +sources = env.Glob('intern/*.c') +sources += env.Glob('operators/*.c') + +#sources += env.Glob('tools/*.c') + +incs = ['#/intern/guardedalloc'] +incs.append('../blenlib') +incs.append('../blenloader') +incs.append('../makesdna') +incs.append('../makesrna') +incs.append('../blenkernel') +incs.append('./') +incs.append('./intern') +incs.append('../editors/mesh') +incs.append('../editors/include') + +defs = [] +env.BlenderLib ( libname = 'bf_bmesh', sources = sources, includes = Split(incs), libtype = 'core', defines=defs, priority=100, compileflags=cflags ) diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h new file mode 100644 index 00000000000..ae85c40e270 --- /dev/null +++ b/source/blender/bmesh/bmesh.h @@ -0,0 +1,380 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_H__ +#define __BMESH_H__ + +/** \file blender/bmesh/bmesh.h + * \ingroup bmesh + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "DNA_listBase.h" +#include "DNA_customdata_types.h" + +#include "BLI_utildefines.h" + +#include "bmesh_class.h" + +/* + * short introduction: + * + * the bmesh structure is a boundary representation, supporting non-manifold + * locally modifiable topology. the API is designed to allow clean, maintainable + * code, that never (or almost never) directly inspects the underlying structure. + * + * The API includes iterators, including many useful topological iterators; + * walkers, which walk over a mesh, without the risk of hitting the recursion + * limit; operators, which are logical, reusable mesh modules; topological + * modification functions (like split face, join faces, etc), which are used for + * topological manipulations; and some (not yet finished) geometric utility + * functions. + * + * some definitions: + * + * tool flags: private flags for tools. each operator has it's own private + * tool flag "layer", which it can use to flag elements. + * tool flags are also used by various other parts of the api. + * header flags: stores persistent flags, such as selection state, hide state, + * etc. be careful of touching these. + */ + +/*forward declarations*/ +struct BMesh; +struct BMVert; +struct BMEdge; +struct BMFace; +struct BMLoop; +struct BMOperator; +struct Mesh; +struct EditMesh; + +/* + * BMHeader + * + * All mesh elements begin with a BMHeader. This structure + * hold several types of data + * + * 1: The type of the element (vert, edge, loop or face) + * 2: Persistant "header" flags/markings (sharp, seam, select, hidden, ect) + note that this is different from the "tool" flags. + * 3: Unique ID in the bmesh. + * 4: some elements for internal record keeping. + * +*/ + +/* BMHeader->htype (char) */ +#define BM_VERT 1 +#define BM_EDGE 2 +#define BM_LOOP 4 +#define BM_FACE 8 +#define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE) + +/* BMHeader->hflag (char) */ +#define BM_ELEM_SELECT (1 << 0) +#define BM_ELEM_HIDDEN (1 << 1) +#define BM_ELEM_SEAM (1 << 2) +#define BM_ELEM_SMOOTH (1 << 3) /* used for faces and edges, note from the user POV, + * this is a sharp edge when disabled */ + +#define BM_ELEM_TAG (1 << 4) /* internal flag, used for ensuring correct normals + * during multires interpolation, and any other time + * when temp tagging is handy. + * always assume dirty & clear before use. */ + +/* we have 3 spare flags which is awesome but since we're limited to 8 + * only add new flags with care! - campbell */ +/* #define BM_ELEM_SPARE (1<<5) */ +/* #define BM_ELEM_SPARE (1<<6) */ +/* #define BM_ELEM_NONORMCALC (1<<7) */ /* UNUSED */ + +/* stub */ +void bmesh_error(void); + +/* Mesh Level Ops */ +extern int bm_mesh_allocsize_default[4]; + +/* ob is needed by multires */ +BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4]); +BMesh *BM_mesh_copy(BMesh *bmold); +void BM_mesh_free(BMesh *bm); + +/* frees mesh, but not actual BMesh struct */ +void BM_mesh_data_free(BMesh *bm); +void BM_mesh_normals_update(BMesh *bm); + +/* Construction */ +BMVert *BM_vert_create(BMesh *bm, const float co[3], const BMVert *example); +BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble); +BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble); + +BMFace *BM_face_create_quad_tri_v(BMesh *bm, + BMVert **verts, int len, + const BMFace *example, const int nodouble); + +/* easier to use version of BM_face_create_quad_tri_v. + * creates edges if necassary. */ +BMFace *BM_face_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + const BMFace *example, const int nodouble); + +/* makes an ngon from an unordered list of edges. v1 and v2 must be the verts + * defining edges[0], and define the winding of the new face. */ +BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble); + +/* stuff for dealing with header flags */ +BM_INLINE char BM_elem_flag_test(const void *element, const char hflag); + +/* stuff for dealing with header flags */ +BM_INLINE void BM_elem_flag_enable(void *element, const char hflag); + +/* stuff for dealing with header flags */ +BM_INLINE void BM_elem_flag_disable(void *element, const char hflag); + +/* stuff for dealing BM_elem_flag_toggle header flags */ +BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag); +BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b); + +/* notes on BM_elem_index_set(...) usage, + * Set index is sometimes abused as temp storage, other times we cant be + * sure if the index values are valid because certain operations have modified + * the mesh structure. + * + * To set the elements to valid indicies 'BM_mesh_elem_index_ensure' should be used + * rather then adding inline loops, however there are cases where we still + * set the index directly + * + * In an attempt to manage this, here are 3 tags Im adding to uses of + * 'BM_elem_index_set' + * + * - 'set_inline' -- since the data is already being looped over set to a + * valid value inline. + * + * - 'set_dirty!' -- intentionally sets the index to an invalid value, + * flagging 'bm->elem_index_dirty' so we dont use it. + * + * - 'set_ok' -- this is valid use since the part of the code is low level. + * + * - 'set_ok_invalid' -- set to -1 on purpose since this should not be + * used without a full array re-index, do this on + * adding new vert/edge/faces since they may be added at + * the end of the array. + * + * - 'set_loop' -- currently loop index values are not used used much so + * assume each case they are dirty. + * - campbell */ + +BM_INLINE void BM_elem_index_set(void *element, const int index); +BM_INLINE int BM_elem_index_get(const void *element); + +/* todo */ +BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts); + +/* copies loop data from adjacent faces */ +void BM_face_copy_shared(BMesh *bm, BMFace *f); + +/* copies attributes, e.g. customdata, header flags, etc, from one element + * to another of the same type.*/ +void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target); + +/* Modification */ +/* join two adjacent faces together along an edge. note that + * the faces must only be joined by on edge. e is the edge you + * wish to dissolve.*/ +BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e); + +/* generic, flexible join faces function; note that most everything uses + * this, including BM_faces_join_pair */ +BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface); + +/* split a face along two vertices. returns the newly made face, and sets + * the nl member to a loop in the newly created edge.*/ +BMFace *BM_face_split(BMesh *bm, BMFace *f, + BMVert *v1, BMVert *v2, + struct BMLoop **nl, BMEdge *example); + +/* these 2 functions are very similar */ +BMEdge* BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces); +BMEdge* BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv); + + +/* splits an edge. ne is set to the new edge created. */ +BMVert *BM_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent); + +/* split an edge multiple times evenly */ +BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts); + +/* connect two verts together, through a face they share. this function may + * be removed in the future. */ +BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf); + +/* rotates an edge topologically, either clockwise (if ccw=0) or counterclockwise + * (if ccw is 1). */ +BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw); + +/* Rip a single face from a vertex fan */ +BMVert *BM_vert_rip(BMesh *bm, BMFace *sf, BMVert *sv); + +/*updates a face normal*/ +void BM_face_normal_update(BMesh *bm, BMFace *f); +void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3]); + +/*updates face and vertex normals incident on an edge*/ +void BM_edge_normals_update(BMesh *bm, BMEdge *e); + +/*update a vert normal (but not the faces incident on it)*/ +void BM_vert_normal_update(BMesh *bm, BMVert *v); +void BM_vert_normal_update_all(BMesh *bm, BMVert *v); + +void BM_face_normal_flip(BMesh *bm, BMFace *f); + +/*dissolves all faces around a vert, and removes it.*/ +int BM_disk_dissolve(BMesh *bm, BMVert *v); + +/* dissolves vert, in more situations then BM_disk_dissolve + * (e.g. if the vert is part of a wire edge, etc).*/ +int BM_vert_dissolve(BMesh *bm, BMVert *v); + +/* Projects co onto face f, and returns true if it is inside + * the face bounds. Note that this uses a best-axis projection + * test, instead of projecting co directly into f's orientation + * space, so there might be accuracy issues.*/ +int BM_face_point_inside_test(BMesh *bm, BMFace *f, const float co[3]); + +/* Interpolation */ + +/* projects target onto source for customdata interpolation. note: only + * does loop customdata. multires is handled. */ +void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source); + +/* projects a single loop, target, onto source for customdata interpolation. multires is handled. + * if do_vertex is true, target's vert data will also get interpolated.*/ +void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source, + int do_vertex, int do_multires); + +/* smoothes boundaries between multires grids, including some borders in adjacent faces */ +void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f); + +/* project the multires grid in target onto source's set of multires grids */ +void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source); +void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source); + +void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac); +void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, struct BMEdge *e1, const float fac); +void BM_data_layer_add(BMesh *em, CustomData *data, int type); +void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name); +void BM_data_layer_free(BMesh *em, CustomData *data, int type); +void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n); +float BM_elem_float_data_get(struct CustomData *cd, void *element, int type); +void BM_elem_float_data_set(struct CustomData *cd, void *element, int type, const float val); + +/* get the area of the face */ +float BM_face_area_calc(BMesh *bm, BMFace *f); +/* computes the centroid of a face, using the center of the bounding box */ +void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float center[3]); +/* computes the centroid of a face, using the mean average */ +void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float center[3]); + +void BM_mesh_select_mode_flush(BMesh *bm); + +/* mode independant flushing up/down */ +void BM_mesh_deselect_flush(BMesh *bm); +void BM_mesh_select_flush(BMesh *bm); + +/* flag conversion funcs */ +char BM_face_flag_from_mflag(const char mflag); +char BM_edge_flag_from_mflag(const short mflag); +char BM_vert_flag_from_mflag(const char mflag); +/* reverse */ +char BM_face_flag_to_mflag(BMFace *f); +short BM_edge_flag_to_mflag(BMEdge *e); +char BM_vert_flag_to_mflag(BMVert *v); + + +/* convert MLoop*** in a bmface to mtface and mcol in + * an MFace*/ +void BM_loops_to_corners(BMesh *bm, struct Mesh *me, int findex, + BMFace *f, int numTex, int numCol); + +void BM_loop_kill(BMesh *bm, BMLoop *l); +void BM_face_kill(BMesh *bm, BMFace *f); +void BM_edge_kill(BMesh *bm, BMEdge *e); +void BM_vert_kill(BMesh *bm, BMVert *v); + +/* kills all edges associated with f, along with any other faces containing + * those edges*/ +void BM_face_edges_kill(BMesh *bm, BMFace *f); + +/* kills all verts associated with f, along with any other faces containing + * those vertices*/ +void BM_face_verts_kill(BMesh *bm, BMFace *f); + +/*clear all data in bm*/ +void BM_mesh_clear(BMesh *bm); + +void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag); + +void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func, + const char *msg_a, const char *msg_b); + +BMVert *BM_vert_at_index(BMesh *bm, const int index); +BMEdge *BM_edge_at_index(BMesh *bm, const int index); +BMFace *BM_face_at_index(BMesh *bm, const int index); + +/*start/stop edit*/ +void bmesh_begin_edit(BMesh *bm, int flag); +void bmesh_end_edit(BMesh *bm, int flag); + + +#ifdef USE_BMESH_HOLES +# define BM_FACE_FIRST_LOOP(p) (((BMLoopList *)((p)->loops.first))->first) +#else +# define BM_FACE_FIRST_LOOP(p) ((p)->l_first) +#endif + +/* size to use for static arrays when dealing with NGons, + * alloc after this limit is reached. + * this value is rather arbitrary */ +#define BM_NGON_STACK_SIZE 32 + +/* avoid inf loop, this value is arbtrary + * but should not error on valid cases */ +#define BM_LOOP_RADIAL_MAX 10000 +#define BM_NGON_MAX 100000 + +/* include the rest of the API */ +#include "bmesh_marking.h" +#include "bmesh_operator_api.h" +#include "bmesh_operators.h" +#include "bmesh_error.h" +#include "bmesh_queries.h" +#include "bmesh_iterators.h" +#include "bmesh_walkers.h" +#include "intern/bmesh_inline.c" + +#ifdef __cplusplus +} +#endif + +#endif /* __BMESH_H__ */ diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h new file mode 100644 index 00000000000..3a62eaa2eeb --- /dev/null +++ b/source/blender/bmesh/bmesh_class.h @@ -0,0 +1,190 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Geoffrey Bantle, Levi Schooley, Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_CLASS_H__ +#define __BMESH_CLASS_H__ + +/** \file blender/bmesh/bmesh_class.h + * \ingroup bmesh + */ + +/* bmesh data structures */ + +/* dissable holes for now, these are ifdef'd because they use more memory and cant be saved in DNA currently */ +// define USE_BMESH_HOLES + +struct BMesh; +struct BMVert; +struct BMEdge; +struct BMLoop; +struct BMFace; +struct BMFlagLayer; +struct BMLayerType; +struct BMSubClassLayer; + +struct BLI_mempool; +struct Object; + +/*note: it is very important for BMHeader to start with two + pointers. this is a requirement of mempool's method of + iteration. +*/ +typedef struct BMHeader { + void *data; /* customdata layers */ + int index; /* notes: + * - Use BM_elem_index_get/SetIndex macros for index + * - Unitialized to -1 so we can easily tell its not set. + * - Used for edge/vert/face, check BMesh.elem_index_dirty for valid index values, + * this is abused by various tools which set it dirty. + * - For loops this is used for sorting during tesselation. */ + + char htype; /* element geometric type (verts/edges/loops/faces) */ + char hflag; /* this would be a CD layer, see below */ +} BMHeader; + +/* note: need some way to specify custom locations for custom data layers. so we can + * make them point directly into structs. and some way to make it only happen to the + * active layer, and properly update when switching active layers.*/ + +typedef struct BMVert { + BMHeader head; + struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */ + + float co[3]; + float no[3]; + struct BMEdge *e; +} BMVert; + +/* disk link structure, only used by edges */ +typedef struct BMDiskLink { + struct BMEdge *next, *prev; +} BMDiskLink; + +typedef struct BMEdge { + BMHeader head; + struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */ + + struct BMVert *v1, *v2; + struct BMLoop *l; + + /* disk cycle pointers */ + BMDiskLink v1_disk_link, v2_disk_link; +} BMEdge; + +typedef struct BMLoop { + BMHeader head; + /* notice no flags layer */ + + struct BMVert *v; + struct BMEdge *e; + struct BMFace *f; + + struct BMLoop *radial_next, *radial_prev; + + /* these were originally commented as private but are used all over the code */ + /* can't use ListBase API, due to head */ + struct BMLoop *next, *prev; +} BMLoop; + +/* can cast BMFace/BMEdge/BMVert, but NOT BMLoop, since these dont have a flag layer */ +typedef struct BMElemF { + BMHeader head; + struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */ +} BMElemF; + +#ifdef USE_BMESH_HOLES +/* eventually, this structure will be used for supporting holes in faces */ +typedef struct BMLoopList { + struct BMLoopList *next, *prev; + struct BMLoop *first, *last; +} BMLoopList; +#endif + +typedef struct BMFace { + BMHeader head; + struct BMFlagLayer *oflags; /* an array of flags, mostly used by the operator stack */ + + int len; /*includes all boundary loops*/ +#ifdef USE_BMESH_HOLES + int totbounds; /*total boundaries, is one plus the number of holes in the face*/ + ListBase loops; +#else + BMLoop *l_first; +#endif + float no[3]; /*yes, we do store this here*/ + short mat_nr; +} BMFace; + +typedef struct BMFlagLayer { + short f, pflag; /* flags */ +} BMFlagLayer; + +typedef struct BMesh { + int totvert, totedge, totloop, totface; + int totvertsel, totedgesel, totfacesel; + + /* flag index arrays as being dirty so we can check if they are clean and + * avoid looping over the entire vert/edge/face array in those cases. + * valid flags are - BM_VERT | BM_EDGE | BM_FACE. + * BM_LOOP isnt handled so far. */ + char elem_index_dirty; + + /*element pools*/ + struct BLI_mempool *vpool, *epool, *lpool, *fpool; + + /*operator api stuff*/ + struct BLI_mempool *toolflagpool; + int stackdepth; + struct BMOperator *currentop; + + CustomData vdata, edata, ldata, pdata; + +#ifdef USE_BMESH_HOLES + struct BLI_mempool *looplistpool; +#endif + + /* should be copy of scene select mode */ + /* stored in BMEditMesh too, this is a bit confusing, + * make sure the're in sync! + * Only use when the edit mesh cant be accessed - campbell */ + short selectmode; + + /*ID of the shape key this bmesh came from*/ + int shapenr; + + int walkers, totflags; + ListBase selected, error_stack; + + BMFace *act_face; + + ListBase errorstack; + struct Object *ob; /* owner object */ + + int opflag; /* current operator flag */ +} BMesh; + +#define BM_VERT 1 +#define BM_EDGE 2 +#define BM_LOOP 4 +#define BM_FACE 8 + +#endif /* __BMESH_CLASS_H__ */ diff --git a/source/blender/bmesh/bmesh_error.h b/source/blender/bmesh/bmesh_error.h new file mode 100644 index 00000000000..26f499afd22 --- /dev/null +++ b/source/blender/bmesh/bmesh_error.h @@ -0,0 +1,72 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_ERROR_H__ +#define __BMESH_ERROR_H__ + +/** \file blender/bmesh/bmesh_error.h + * \ingroup bmesh + */ + +/*----------- bmop error system ----------*/ + +/* pushes an error onto the bmesh error stack. + * if msg is null, then the default message for the errorcode is used.*/ +void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg); + +/* gets the topmost error from the stack. + * returns error code or 0 if no error.*/ +int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op); +int BMO_error_occurred(BMesh *bm); + +/* same as geterror, only pops the error off the stack as well */ +int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op); +void BMO_error_clear(BMesh *bm); + +#if 0 +//this is meant for handling errors, like self-intersection test failures. +//it's dangerous to handle errors in general though, so disabled for now. + +/* catches an error raised by the op pointed to by catchop. + * errorcode is either the errorcode, or BMERR_ALL for any + * error.*/ +int BMO_error_catch_op(BMesh *bm, BMOperator *catchop, int errorcode, char **msg); +#endif + +#define BM_ELEM_INDEX_VALIDATE(_bm, _msg_a, _msg_b) \ + BM_mesh_elem_index_validate(_bm, __FILE__ ":" STRINGIFY(__LINE__), __func__, _msg_a, _msg_b) + +/*------ error code defines -------*/ + +/*error messages*/ +#define BMERR_SELF_INTERSECTING 1 +#define BMERR_DISSOLVEDISK_FAILED 2 +#define BMERR_CONNECTVERT_FAILED 3 +#define BMERR_WALKER_FAILED 4 +#define BMERR_DISSOLVEFACES_FAILED 5 +#define BMERR_DISSOLVEVERTS_FAILED 6 +#define BMERR_TESSELATION 7 +#define BMERR_NONMANIFOLD 8 +#define BMERR_INVALID_SELECTION 9 +#define BMERR_MESH_ERROR 10 + +#endif /* __BMESH_ERROR_H__ */ diff --git a/source/blender/bmesh/bmesh_iterators.h b/source/blender/bmesh/bmesh_iterators.h new file mode 100644 index 00000000000..51df9460e8b --- /dev/null +++ b/source/blender/bmesh/bmesh_iterators.h @@ -0,0 +1,136 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_ITERATORS_H__ +#define __BMESH_ITERATORS_H__ + +/** \file blender/bmesh/bmesh_iterators.h + * \ingroup bmesh + */ + +/* + * BMESH ITERATORS + * + * The functions and structures in this file + * provide a unified method for iterating over + * the elements of a mesh and answering simple + * adjacency queries. Tool authors should use + * the iterators provided in this file instead + * of inspecting the structure directly. + * +*/ + +#include "BLI_mempool.h" + +/* Defines for passing to BM_iter_new. + * + * "OF" can be substituted for "around" + * so BM_VERTS_OF_FACE means "vertices + * around a face." + */ + +/* these iterator over all elements of a specific + * type in the mesh.*/ +#define BM_VERTS_OF_MESH 1 +#define BM_EDGES_OF_MESH 2 +#define BM_FACES_OF_MESH 3 + +/*these are topological iterators.*/ +#define BM_EDGES_OF_VERT 4 +#define BM_FACES_OF_VERT 5 +#define BM_LOOPS_OF_VERT 6 +#define BM_FACES_OF_EDGE 7 +#define BM_VERTS_OF_FACE 8 +#define BM_EDGES_OF_FACE 9 +#define BM_LOOPS_OF_FACE 10 +/* returns elements from all boundaries, and returns + * the first element at the end to flag that we're entering + * a different face hole boundary*/ +#define BM_ALL_LOOPS_OF_FACE 11 + +/* iterate through loops around this loop, which are fetched + * from the other faces in the radial cycle surrounding the + * input loop's edge.*/ +#define BM_LOOPS_OF_LOOP 12 +#define BM_LOOPS_OF_EDGE 13 + +#define BM_ITER(ele, iter, bm, itype, data) \ + ele = BM_iter_new(iter, bm, itype, data); \ + for ( ; ele; ele=BM_iter_step(iter)) + +#define BM_ITER_INDEX(ele, iter, bm, itype, data, indexvar) \ + ele = BM_iter_new(iter, bm, itype, data); \ + for (indexvar=0; ele; indexvar++, ele=BM_iter_step(iter)) + +/*Iterator Structure*/ +typedef struct BMIter { + BLI_mempool_iter pooliter; + + struct BMVert *firstvert, *nextvert, *vdata; + struct BMEdge *firstedge, *nextedge, *edata; + struct BMLoop *firstloop, *nextloop, *ldata, *l; + struct BMFace *firstpoly, *nextpoly, *pdata; + struct BMesh *bm; + void (*begin)(struct BMIter *iter); + void *(*step)(struct BMIter *iter); + union { + void *p; + int i; + long l; + float f; + } filter; + int count; + char itype; +} BMIter; + +void *BM_iter_at_index(struct BMesh *bm, const char htype, void *data, int index); +int BM_iter_as_array(struct BMesh *bm, const char htype, void *data, void **array, const int len); + +/* private for bmesh_iterators_inline.c */ +void bmiter__vert_of_mesh_begin(struct BMIter *iter); +void *bmiter__vert_of_mesh_step(struct BMIter *iter); +void bmiter__edge_of_mesh_begin(struct BMIter *iter); +void *bmiter__edge_of_mesh_step(struct BMIter *iter); +void bmiter__face_of_mesh_begin(struct BMIter *iter); +void *bmiter__face_of_mesh_step(struct BMIter *iter); +void bmiter__edge_of_vert_begin(struct BMIter *iter); +void *bmiter__edge_of_vert_step(struct BMIter *iter); +void bmiter__face_of_vert_begin(struct BMIter *iter); +void *bmiter__face_of_vert_step(struct BMIter *iter); +void bmiter__loop_of_vert_begin(struct BMIter *iter); +void *bmiter__loop_of_vert_step(struct BMIter *iter); +void bmiter__loops_of_edge_begin(struct BMIter *iter); +void *bmiter__loops_of_edge_step(struct BMIter *iter); +void bmiter__loops_of_loop_begin(struct BMIter *iter); +void *bmiter__loops_of_loop_step(struct BMIter *iter); +void bmiter__face_of_edge_begin(struct BMIter *iter); +void *bmiter__face_of_edge_step(struct BMIter *iter); +void bmiter__vert_of_face_begin(struct BMIter *iter); +void *bmiter__vert_of_face_step(struct BMIter *iter); +void bmiter__edge_of_face_begin(struct BMIter *iter); +void *bmiter__edge_of_face_step(struct BMIter *iter); +void bmiter__loop_of_face_begin(struct BMIter *iter); +void *bmiter__loop_of_face_step(struct BMIter *iter); + +#include "intern/bmesh_iterators_inline.c" + +#endif /* __BMESH_ITERATORS_H__ */ diff --git a/source/blender/bmesh/bmesh_marking.h b/source/blender/bmesh/bmesh_marking.h new file mode 100644 index 00000000000..9b45ac10394 --- /dev/null +++ b/source/blender/bmesh/bmesh_marking.h @@ -0,0 +1,75 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_MARKING_H__ +#define __BMESH_MARKING_H__ + +/** \file blender/bmesh/bmesh_marking.h + * \ingroup bmesh + */ + +typedef struct BMEditSelection +{ + struct BMEditSelection *next, *prev; + void *data; + char htype; +} BMEditSelection; + +/* geometry hiding code */ +void BM_elem_hide_set(BMesh *bm, void *element, int hide); +void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide); +void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide); +void BM_face_hide_set(BMesh *bm, BMFace *f, int hide); + +/* Selection code */ +void BM_elem_select_set(struct BMesh *bm, void *element, int select); + +/* use BM_elem_flag_test(ele, BM_ELEM_SELECT) to test selection */ + +void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag); +void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag); + +/* individual element select functions, BM_elem_select_set is a shortcut for these + * that automatically detects which one to use*/ +void BM_vert_select_set(struct BMesh *bm, struct BMVert *v, int select); +void BM_edge_select_set(struct BMesh *bm, struct BMEdge *e, int select); +void BM_face_select_set(struct BMesh *bm, struct BMFace *f, int select); + +void BM_select_mode_set(struct BMesh *bm, int selectmode); + +/* counts number of elements with flag set */ +int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide); + +/* edit selection stuff */ +void BM_active_face_set(BMesh *em, BMFace *f); +BMFace *BM_active_face_get(BMesh *bm, int sloppy); +void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese); +void BM_editselection_normal(float r_normal[3], BMEditSelection *ese); +void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese); + +int BM_select_history_check(BMesh *bm, void *data); +void BM_select_history_remove(BMesh *bm, void *data); +void BM_select_history_store(BMesh *bm, void *data); +void BM_select_history_validate(BMesh *bm); +void BM_select_history_clear(BMesh *em); + +#endif /* __BMESH_MARKING_H__ */ diff --git a/source/blender/bmesh/bmesh_operator_api.h b/source/blender/bmesh/bmesh_operator_api.h new file mode 100644 index 00000000000..7bbb579685d --- /dev/null +++ b/source/blender/bmesh/bmesh_operator_api.h @@ -0,0 +1,555 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_OPERATOR_API_H__ +#define __BMESH_OPERATOR_API_H__ + +/** \file blender/bmesh/bmesh_operator_api.h + * \ingroup bmesh + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_memarena.h" +#include "BLI_ghash.h" + +#include "BKE_utildefines.h" + +#include <stdarg.h> +#include <string.h> /* for memcpy */ + +/* + * operators represent logical, executable mesh modules. all topological + * operations involving a bmesh has to go through them. + * + * operators are nested, as are tool flags, which are private to an operator + * when it's executed. tool flags are allocated in layers, one per operator + * execution, and are used for all internal flagging a tool needs to do. + * + * each operator has a series of "slots," which can be of the following types: + * - simple numerical types + * - arrays of elements (e.g. arrays of faces). + * - hash mappings. + * + * each slot is identified by a slot code, as are each operator. + * operators, and their slots, are defined in bmesh_opdefines.c (with their + * execution functions prototyped in bmesh_operators_private.h), with all their + * operator code and slot codes defined in bmesh_operators.h. see + * bmesh_opdefines.c and the BMOpDefine struct for how to define new operators. + * + * in general, operators are fed arrays of elements, created using either + * BM_HeaderFlag_To_Slot or BM_Flag_To_Slot (or through one of the format + * specifyers in BMO_op_callf or BMO_op_initf). Note that multiple element + * types (e.g. faces and edges) can be fed to the same slot array. Operators + * act on this data, and possibly spit out data into output slots. + * + * some notes: + * - operators should never read from header flags (e.g. element->head.flag). for + * example, if you want an operator to only operate on selected faces, you + * should use BM_HeaderFlag_To_Slot to put the selected elements into a slot. + * - when you read from an element slot array or mapping, you can either tool-flag + * all the elements in it, or read them using an iterator APi (which is + * semantically similar to the iterator api in bmesh_iterators.h). + */ + +struct GHashIterator; + +/* slot type arrays are terminated by the last member + * having a slot type of 0.*/ +#define BMO_OP_SLOT_SENTINEL 0 +#define BMO_OP_SLOT_INT 1 +#define BMO_OP_SLOT_FLT 2 +#define BMO_OP_SLOT_PNT 3 +#define BMO_OP_SLOT_MAT 4 +#define BMO_OP_SLOT_VEC 7 + +/* after BMO_OP_SLOT_VEC, everything is + + * dynamically allocated arrays. we + * leave a space in the identifiers + * for future growth. + */ +//it's very important this remain a power of two +#define BMO_OP_SLOT_ELEMENT_BUF 8 +#define BMO_OP_SLOT_MAPPING 9 +/* #define BMO_OP_SLOT_TOTAL_TYPES 10 */ /* not used yet */ + +/* please ignore all these structures, don't touch them in tool code, except + * for when your defining an operator with BMOpDefine.*/ + +typedef struct BMOpSlot{ + int slottype; + int len; + int flag; + int index; /* index within slot array */ + union { + int i; + float f; + void *p; + float vec[3]; + void *buf; + GHash *ghash; + } data; +} BMOpSlot; + +#define BMO_OP_MAX_SLOTS 16 /* way more than probably needed */ + +#ifdef slots +#undef slots +#endif + +typedef struct BMOperator { + int type; + int slottype; + int needflag; + int flag; + struct BMOpSlot slots[BMO_OP_MAX_SLOTS]; + void (*exec)(struct BMesh *bm, struct BMOperator *op); + MemArena *arena; +} BMOperator; + +#define MAX_SLOTNAME 32 + +typedef struct BMOSlotType { + int type; + char name[MAX_SLOTNAME]; +} BMOSlotType; + +typedef struct BMOpDefine { + const char *name; + BMOSlotType slottypes[BMO_OP_MAX_SLOTS]; + void (*exec)(BMesh *bm, BMOperator *op); + int flag; +} BMOpDefine; + +/* BMOpDefine->flag */ +#define BMO_OP_FLAG_UNTAN_MULTIRES 1 /*switch from multires tangent space to absolute coordinates*/ + +/* ensures consistent normals before operator execution, + * restoring the original ones windings/normals afterwards. + * keep in mind, this won't work if the input mesh isn't + * manifold.*/ +#define BMO_OP_FLAG_RATIONALIZE_NORMALS 2 + +/*------------- Operator API --------------*/ + +/* data types that use pointers (arrays, etc) should never + * have it set directly. and never use BMO_slot_ptr_set to + * pass in a list of edges or any arrays, really.*/ + +void BMO_op_init(struct BMesh *bm, struct BMOperator *op, const char *opname); + +/* executes an operator, pushing and popping a new tool flag + * layer as appropriate.*/ +void BMO_op_exec(struct BMesh *bm, struct BMOperator *op); + +/* finishes an operator (though note the operator's tool flag is removed + * after it finishes executing in BMO_op_exec).*/ +void BMO_op_finish(struct BMesh *bm, struct BMOperator *op); + + +/* tool flag API. never, ever ever should tool code put junk in + * header flags (element->head.flag), nor should they use + * element->head.eflag1/eflag2. instead, use this api to set + * flags. + * + * if you need to store a value per element, use a + * ghash or a mapping slot to do it. */ + +/* flags 15 and 16 (1<<14 and 1<<15) are reserved for bmesh api use */ +#define BMO_elem_flag_test(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f & (oflag)) +#define BMO_elem_flag_enable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f |= (oflag)) +#define BMO_elem_flag_disable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f &= ~(oflag)) +#define BMO_elem_flag_toggle(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f ^= (oflag)) + +/* profiling showed a significant amount of time spent in BMO_elem_flag_test */ +#if 0 +void BMO_elem_flag_enable(struct BMesh *bm, void *element, const short oflag); +void BMO_elem_flag_disable(struct BMesh *bm, void *element, const short oflag); +int BMO_elem_flag_test(struct BMesh *bm, void *element, const short oflag); +#endif + +/* count the number of elements with a specific flag. + * type can be a bitmask of BM_FACE, BM_EDGE, or BM_FACE. */ +int BMO_mesh_flag_count(struct BMesh *bm, const short oflag, const char htype); + +/*---------formatted operator initialization/execution-----------*/ +/* + * this system is used to execute or initialize an operator, + * using a formatted-string system. + * + * for example, BMO_op_callf(bm, "del geom=%hf context=%d", BM_ELEM_SELECT, DEL_FACES); + * . . .will execute the delete operator, feeding in selected faces, deleting them. + * + * the basic format for the format string is: + * [operatorname] [slotname]=%[code] [slotname]=%[code] + * + * as in printf, you pass in one additional argument to the function + * for every code. + * + * the formatting codes are: + * %d - put int in slot + * %f - put float in slot + * %p - put pointer in slot + * %h[f/e/v] - put elements with a header flag in slot. + * the letters after %h define which element types to use, + * so e.g. %hf will do faces, %hfe will do faces and edges, + * %hv will do verts, etc. must pass in at least one + * element type letter. + * %f[f/e/v] - same as %h, except it deals with tool flags instead of + * header flags. + * %a[f/e/v] - pass all elements (of types specified by f/e/v) to the + * slot. + * %e - pass in a single element. + * %v - pointer to a float vector of length 3. + * %m[3/4] - matrix, 3/4 refers to the matrix size, 3 or 4. the + * corrusponding argument must be a pointer to + * a float matrix. + * %s - copy a slot from another op, instead of mapping to one + * argument, it maps to two, a pointer to an operator and + * a slot name. + */ +void BMO_push(BMesh *bm, BMOperator *op); +void BMO_pop(BMesh *bm); + +/*executes an operator*/ +int BMO_op_callf(BMesh *bm, const char *fmt, ...); + +/* initializes, but doesn't execute an operator. this is so you can + * gain access to the outputs of the operator. note that you have + * to execute/finitsh (BMO_op_exec and BMO_op_finish) yourself. */ +int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...); + +/* va_list version, used to implement the above two functions, + * plus EDBM_CallOpf in bmeshutils.c. */ +int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *fmt, va_list vlist); + +/* test whether a named slot exists */ +int BMO_slot_exists(struct BMOperator *op, const char *slotname); + +/* get a pointer to a slot. this may be removed layer on from the public API. */ +BMOpSlot *BMO_slot_get(struct BMOperator *op, const char *slotname); + +/* copies the data of a slot from one operator to another. src and dst are the + * source/destination slot codes, respectively. */ +void BMO_slot_copy(struct BMOperator *source_op, struct BMOperator *dest_op, + const char *src, const char *dst); + +/* remove tool flagged elements */ +void BMO_remove_tagged_faces(struct BMesh *bm, const short oflag); +void BMO_remove_tagged_edges(struct BMesh *bm, const short oflag); +void BMO_remove_tagged_verts(struct BMesh *bm, const short oflag); + +/* take care, uses operator flag DEL_WIREVERT */ +void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type); + +/* del "context" slot values, used for operator too */ +enum { + DEL_VERTS = 1, + DEL_EDGES, + DEL_ONLYFACES, + DEL_EDGESFACES, + DEL_FACES, + DEL_ALL , + DEL_ONLYTAGGED +}; + +void BMO_op_flag_enable(struct BMesh *bm, struct BMOperator *op, const int op_flag); +void BMO_op_flag_disable(struct BMesh *bm, struct BMOperator *op, const int op_flag); + +void BMO_slot_float_set(struct BMOperator *op, const char *slotname, const float f); +float BMO_slot_float_get(BMOperator *op, const char *slotname); +void BMO_slot_int_set(struct BMOperator *op, const char *slotname, const int i); +int BMO_slot_int_get(BMOperator *op, const char *slotname); + +/* don't pass in arrays that are supposed to map to elements this way. + * + * so, e.g. passing in list of floats per element in another slot is bad. + * passing in, e.g. pointer to an editmesh for the conversion operator is fine + * though. */ +void BMO_slot_ptr_set(struct BMOperator *op, const char *slotname, void *p); +void *BMO_slot_ptr_get(BMOperator *op, const char *slotname); +void BMO_slot_vec_set(struct BMOperator *op, const char *slotname, const float vec[3]); +void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3]); + +/* only supports square mats */ +/* size must be 3 or 4; this api is meant only for transformation matrices. + * note that internally the matrix is stored in 4x4 form, and it's safe to + * call whichever BMO_Get_Mat* function you want. */ +void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size); +void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4]); +void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3]); + +void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *op, const char htype, const short oflag); + +/* puts every element of type type (which is a bitmask) with tool flag flag, + * into a slot. */ +void BMO_slot_from_flag(struct BMesh *bm, struct BMOperator *op, const char *slotname, + const short oflag, const char htype); + +/* tool-flags all elements inside an element slot array with flag flag. */ +void BMO_slot_buffer_flag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname, + const short oflag, const char htype); +/* clears tool-flag flag from all elements inside a slot array. */ +void BMO_slot_buffer_flag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname, + const short oflag, const char htype); + +/* tool-flags all elements inside an element slot array with flag flag. */ +void BMO_slot_buffer_hflag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname, + const char hflag, const char htype); +/* clears tool-flag flag from all elements inside a slot array. */ +void BMO_slot_buffer_hflag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname, + const char hflag, const char htype); + +/* puts every element of type type (which is a bitmask) with header flag + * flag, into a slot. note: ignores hidden elements (e.g. elements with + * header flag BM_ELEM_HIDDEN set).*/ +void BMO_slot_from_hflag(struct BMesh *bm, struct BMOperator *op, const char *slotname, + const char hflag, const char htype); + +/* counts number of elements inside a slot array. */ +int BMO_slot_buf_count(struct BMesh *bm, struct BMOperator *op, const char *slotname); +int BMO_slot_map_count(struct BMesh *bm, struct BMOperator *op, const char *slotname); + +/* Counts the number of edges with tool flag toolflag around + */ +int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag); + +/* inserts a key/value mapping into a mapping slot. note that it copies the + * value, it doesn't store a reference to it. */ + +#if 0 + +BM_INLINE void BMO_slot_map_insert(BMesh *bm, BMOperator *op, const char *slotname, + void *element, void *data, int len); + +/* inserts a key/float mapping pair into a mapping slot. */ +BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname, + void *element, float val); + +/* returns 1 if the specified pointer is in the map. */ +BM_INLINE int BMO_slot_map_contains(BMesh *bm, BMOperator *op, const char *slotname, void *element); + +/* returns a point to the value of a specific key. */ +BM_INLINE void *BMO_slot_map_data_get(BMesh *bm, BMOperator *op, const char *slotname, void *element); + +/* returns the float part of a key/float pair. */ +BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname, void *element); + +#endif + +/* flags all elements in a mapping. note that the mapping must only have + * bmesh elements in it.*/ +void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op, + const char *slotname, const short oflag); + +/* pointer versoins of BMO_slot_map_float_get and BMO_slot_map_float_insert. + * + * do NOT use these for non-operator-api-allocated memory! instead + * use BMO_slot_map_data_get and BMO_slot_map_insert, which copies the data. */ + +#if 0 +BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname, void *key, void *val); +BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname, void *key); +#endif + +/* this part of the API is used to iterate over element buffer or + * mapping slots. + * + * for example, iterating over the faces in a slot is: + * + * BMOIter oiter; + * BMFace *f; + * + * f = BMO_iter_new(&oiter, bm, some_operator, "slotname", BM_FACE); + * for (; f; f=BMO_iter_step(&oiter)) { + * /do something with the face + * } + * + * another example, iterating over a mapping: + * BMOIter oiter; + * void *key; + * void *val; + * + * key = BMO_iter_new(&oiter, bm, some_operator, "slotname", 0); + * for (; key; key=BMO_iter_step(&oiter)) { + * val = BMO_iter_map_value(&oiter); + * //do something with the key/val pair + * //note that val is a pointer to the val data, + * //whether it's a float, pointer, whatever. + * // + * // so to get a pointer, for example, use: + * // *((void**)BMO_iter_map_value(&oiter)); + * //or something like that. + * } + */ + +/* contents of this structure are private, + * don't directly access. */ +typedef struct BMOIter { + BMOpSlot *slot; + int cur; //for arrays + struct GHashIterator giter; + void *val; + char restrictmask; /* bitwise '&' with BMHeader.htype */ +} BMOIter; + +void *BMO_slot_elem_first(BMOperator *op, const char *slotname); + +/* restrictmask restricts the iteration to certain element types + * (e.g. combination of BM_VERT, BM_EDGE, BM_FACE), if iterating + * over an element buffer (not a mapping).*/ +void *BMO_iter_new(BMOIter *iter, BMesh *bm, BMOperator *op, + const char *slotname, const char restrictmask); +void *BMO_iter_step(BMOIter *iter); + +/* returns a pointer to the key value when iterating over mappings. + * remember for pointer maps this will be a pointer to a pointer.*/ +void *BMO_iter_map_value(BMOIter *iter); + +/* use this for pointer mappings */ +void *BMO_iter_map_value_p(BMOIter *iter); + +/* use this for float mappings */ +float BMO_iter_map_value_f(BMOIter *iter); + +#define BMO_ITER(ele, iter, bm, op, slotname, restrict) \ + ele = BMO_iter_new(iter, bm, op, slotname, restrict); \ + for ( ; ele; ele=BMO_iter_step(iter)) + +/******************* Inlined Functions********************/ +typedef void (*opexec)(struct BMesh *bm, struct BMOperator *op); + +/* mappings map elements to data, which + * follows the mapping struct in memory. */ +typedef struct BMOElemMapping { + BMHeader *element; + int len; +} BMOElemMapping; + +extern const int BMO_OPSLOT_TYPEINFO[]; + +BM_INLINE void BMO_slot_map_insert(BMesh *UNUSED(bm), BMOperator *op, const char *slotname, + void *element, void *data, int len) +{ + BMOElemMapping *mapping; + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /*sanity check*/ + if (slot->slottype != BMO_OP_SLOT_MAPPING) { + return; + } + + mapping = (BMOElemMapping *) BLI_memarena_alloc(op->arena, sizeof(*mapping) + len); + + mapping->element = (BMHeader*) element; + mapping->len = len; + memcpy(mapping + 1, data, len); + + if (!slot->data.ghash) { + slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash, + BLI_ghashutil_ptrcmp, "bmesh op"); + } + + BLI_ghash_insert(slot->data.ghash, element, mapping); +} + +BM_INLINE void BMO_slot_map_int_insert(BMesh *bm, BMOperator *op, const char *slotname, + void *element, int val) +{ + BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(int)); +} + +BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname, + void *element, float val) +{ + BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(float)); +} + +BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname, + void *element, void *val) +{ + BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(void*)); +} + +BM_INLINE int BMO_slot_map_contains(BMesh *UNUSED(bm), BMOperator *op, const char *slotname, void *element) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /*sanity check*/ + if (slot->slottype != BMO_OP_SLOT_MAPPING) return 0; + if (!slot->data.ghash) return 0; + + return BLI_ghash_haskey(slot->data.ghash, element); +} + +BM_INLINE void *BMO_slot_map_data_get(BMesh *UNUSED(bm), BMOperator *op, const char *slotname, + void *element) +{ + BMOElemMapping *mapping; + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /*sanity check*/ + if (slot->slottype != BMO_OP_SLOT_MAPPING) return NULL; + if (!slot->data.ghash) return NULL; + + mapping = (BMOElemMapping *)BLI_ghash_lookup(slot->data.ghash, element); + + if (!mapping) return NULL; + + return mapping + 1; +} + +BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname, + void *element) +{ + float *val = (float*) BMO_slot_map_data_get(bm, op, slotname, element); + if (val) return *val; + + return 0.0f; +} + +BM_INLINE int BMO_slot_map_int_get(BMesh *bm, BMOperator *op, const char *slotname, + void *element) +{ + int *val = (int*) BMO_slot_map_data_get(bm, op, slotname, element); + if (val) return *val; + + return 0; +} + +BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname, + void *element) +{ + void **val = (void**) BMO_slot_map_data_get(bm, op, slotname, element); + if (val) return *val; + + return NULL; +} + +#ifdef __cplusplus +} +#endif + +#endif /* __BMESH_OPERATOR_API_H__ */ diff --git a/source/blender/bmesh/bmesh_operators.h b/source/blender/bmesh/bmesh_operators.h new file mode 100644 index 00000000000..de92f57bc23 --- /dev/null +++ b/source/blender/bmesh/bmesh_operators.h @@ -0,0 +1,105 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_OPERATORS_H__ +#define __BMESH_OPERATORS_H__ + +/** \file blender/bmesh/bmesh_operators.h + * \ingroup bmesh + */ + +/*see comments in intern/bmesh_opdefines.c for documentation of specific operators*/ + +/*--------defines/enumerations for specific operators-------*/ + +/*quad innervert values*/ +enum { + SUBD_INNERVERT, + SUBD_PATH, + SUBD_FAN, + SUBD_STRAIGHT_CUT +}; + +/* similar face selection slot values */ +enum { + SIMFACE_MATERIAL = 201, + SIMFACE_IMAGE, + SIMFACE_AREA, + SIMFACE_PERIMETER, + SIMFACE_NORMAL, + SIMFACE_COPLANAR +}; + +/* similar edge selection slot values */ +enum { + SIMEDGE_LENGTH = 101, + SIMEDGE_DIR, + SIMEDGE_FACE, + SIMEDGE_FACE_ANGLE, + SIMEDGE_CREASE, + SIMEDGE_SEAM, + SIMEDGE_SHARP +}; + +/* similar vertex selection slot values */ +enum { + SIMVERT_NORMAL = 0, + SIMVERT_FACE, + SIMVERT_VGROUP +}; + +enum { + OPUVC_AXIS_X = 1, + OPUVC_AXIS_Y +}; + +enum { + DIRECTION_CW = 1, + DIRECTION_CCW +}; + +/* vertex path selection values */ +enum { + VPATH_SELECT_EDGE_LENGTH = 0, + VPATH_SELECT_TOPOLOGICAL +}; + +extern BMOpDefine *opdefines[]; +extern int bmesh_total_ops; + +/*------specific operator helper functions-------*/ + +/* executes the duplicate operation, feeding elements of + * type flag etypeflag and header flag flag to it. note, + * to get more useful information (such as the mapping from + * original to new elements) you should run the dupe op manually.*/ +struct Object; +struct EditMesh; + +#if 0 +void BMO_dupe_from_flag(struct BMesh *bm, int etypeflag, const char hflag); +#endif +void BM_mesh_esubdivideflag(struct Object *obedit, BMesh *bm, int flag, float smooth, + float fractal, int beauty, int numcuts, int seltype, + int cornertype, int singleedge, int gridfill, int seed); + +#endif /* __BMESH_OPERATORS_H__ */ diff --git a/source/blender/bmesh/bmesh_queries.h b/source/blender/bmesh/bmesh_queries.h new file mode 100644 index 00000000000..1ae469eb663 --- /dev/null +++ b/source/blender/bmesh/bmesh_queries.h @@ -0,0 +1,123 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_QUERIES_H__ +#define __BMESH_QUERIES_H__ + +/** \file blender/bmesh/bmesh_queries.h + * \ingroup bmesh + */ + +#include <stdio.h> + +/* Queries */ + +/* counts number of elements of type type are in the mesh. */ +int BM_mesh_elem_count(struct BMesh *bm, const char htype); + +/*returns true if v is in f*/ +int BM_vert_in_face(struct BMFace *f, struct BMVert *v); + +// int BM_verts_in_face(struct BMFace *f, struct BMVert **varr, int len); +int BM_verts_in_face(struct BMesh *bm, struct BMFace *f, struct BMVert **varr, int len); + +int BM_edge_in_face(struct BMFace *f, struct BMEdge *e); + +int BM_vert_in_edge(struct BMEdge *e, struct BMVert *v); + +int BM_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e); + +/*get opposing vert from v in edge e.*/ +struct BMVert *BM_edge_other_vert(struct BMEdge *e, struct BMVert *v); + +/*finds other loop that shares v with e's loop in f.*/ +struct BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v); + +/*returns the edge existing between v1 and v2, or NULL if there isn't one.*/ +struct BMEdge *BM_edge_exists(struct BMVert *v1, struct BMVert *v2); + + +/*returns number of edges aroudn a vert*/ +int BM_vert_edge_count(struct BMVert *v); + +/*returns number of faces around an edge*/ +int BM_edge_face_count(struct BMEdge *e); + +/*returns number of faces around a vert.*/ +int BM_vert_face_count(struct BMVert *v); + + +/*returns true if v is a wire vert*/ +int BM_vert_is_wire(struct BMesh *bm, struct BMVert *v); + +/*returns true if e is a wire edge*/ +int BM_edge_is_wire(struct BMesh *bm, struct BMEdge *e); + +/* returns FALSE if v is part of a non-manifold edge in the mesh, + * I believe this includes if it's part of both a wire edge and + * a face.*/ +int BM_vert_is_manifold(struct BMesh *bm, struct BMVert *v); + +/* returns FALSE if e is shared by more then two faces. */ +int BM_edge_is_manifold(struct BMesh *bm, struct BMEdge *e); + +/* returns true if e is a boundary edge, e.g. has only 1 face bordering it. */ +int BM_edge_is_boundry(struct BMEdge *e); + + +/* returns angle of two faces surrounding an edge. note there must be + * exactly two faces sharing the edge.*/ +float BM_edge_face_angle(struct BMesh *bm, struct BMEdge *e); + +/* returns angle of two faces surrounding edges. note there must be + * exactly two edges sharing the vertex.*/ +float BM_vert_edge_angle(struct BMesh *bm, struct BMVert *v); + +/* checks overlapping of existing faces with the verts in varr. */ +int BM_face_exists_overlap(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface); + +/* checks if a face defined by varr already exists. */ +int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface); + + +/* returns number of edges f1 and f2 share. */ +int BM_face_share_edges(struct BMFace *f1, struct BMFace *f2); + +/* returns number of faces e1 and e2 share. */ +int BM_edge_share_faces(struct BMEdge *e1, struct BMEdge *e2); + +/* returns bool 1/0 if the edges share a vertex */ +int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2); + +/* edge verts in winding order from face */ +void BM_edge_ordered_verts(struct BMEdge *edge, struct BMVert **r_v1, struct BMVert **r_v2); + +/* checks if a face is valid in the data structure */ +int BM_face_validate(BMesh *bm, BMFace *face, FILE *err); + +/* each pair of loops defines a new edge, a split. this function goes + * through and sets pairs that are geometrically invalid to null. a + * split is invalid, if it forms a concave angle or it intersects other + * edges in the face.*/ +void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len); + +#endif /* __BMESH_QUERIES_H__ */ diff --git a/source/blender/bmesh/bmesh_walkers.h b/source/blender/bmesh/bmesh_walkers.h new file mode 100644 index 00000000000..a9515dbd3e7 --- /dev/null +++ b/source/blender/bmesh/bmesh_walkers.h @@ -0,0 +1,139 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_WALKERS_H__ +#define __BMESH_WALKERS_H__ + +/** \file blender/bmesh/bmesh_walkers.h + * \ingroup bmesh + */ + +#include "BLI_ghash.h" + +/* + NOTE: do NOT modify topology while walking a mesh! +*/ + +typedef enum { + BMW_DEPTH_FIRST, + BMW_BREADTH_FIRST +} BMWOrder; + +/*Walkers*/ +typedef struct BMWalker { + void (*begin) (struct BMWalker *walker, void *start); + void *(*step) (struct BMWalker *walker); + void *(*yield)(struct BMWalker *walker); + int structsize; + BMWOrder order; + int valid_mask; + + /* runtime */ + int layer; + + BMesh *bm; + BLI_mempool *worklist; + ListBase states; + + short mask_vert; + short mask_edge; + short mask_loop; + short mask_face; + + GHash *visithash; + int depth; +} BMWalker; + +/* define to make BMW_init more clear */ +#define BMW_MASK_NOP 0 + +/* initialize a walker. searchmask restricts some (not all) walkers to + * elements with a specific tool flag set. flags is specific to each walker.*/ +void BMW_init(struct BMWalker *walker, BMesh *bm, int type, + short mask_vert, short mask_edge, short mask_loop, short mask_face, + int layer); +void *BMW_begin(BMWalker *walker, void *start); +void *BMW_step(struct BMWalker *walker); +void BMW_end(struct BMWalker *walker); +int BMW_current_depth(BMWalker *walker); + +/*these are used by custom walkers*/ +void *BMW_current_state(BMWalker *walker); +void *BMW_state_add(BMWalker *walker); +void BMW_state_remove(BMWalker *walker); +void *BMW_walk(BMWalker *walker); +void BMW_reset(BMWalker *walker); + +/* +example of usage, walking over an island of tool flagged faces: + +BMWalker walker; +BMFace *f; + +BMW_init(&walker, bm, BMW_ISLAND, SOME_OP_FLAG); +f = BMW_begin(&walker, some_start_face); +for (; f; f=BMW_step(&walker)) { + //do something with f +} +BMW_end(&walker); +*/ + +enum { + /* walk over connected geometry. can restrict to a search flag, + * or not, it's optional. + * + * takes a vert as an arugment, and spits out edges, restrict flag acts + * on the edges as well. */ + BMW_SHELL, + /*walk over an edge loop. search flag doesn't do anything.*/ + BMW_LOOP, + BMW_FACELOOP, + BMW_EDGERING, + /* #define BMW_RING 2 */ + /* walk over uv islands; takes a loop as input. restrict flag + * restricts the walking to loops whose vert has restrict flag set as a + * tool flag. + * + * the flag parameter to BMW_init maps to a loop customdata layer index. + */ + BMW_LOOPDATA_ISLAND, + /* walk over an island of flagged faces. note, that this doesn't work on + * non-manifold geometry. it might be better to rewrite this to extract + * boundary info from the island walker, rather then directly walking + * over the boundary. raises an error if it encouters nonmanifold + * geometry. */ + BMW_ISLANDBOUND, + /* walk over all faces in an island of tool flagged faces. */ + BMW_ISLAND, + /* walk from a vertex to all connected vertices. */ + BMW_CONNECTED_VERTEX, + /* end of array index enum vals */ + + /* do not intitialze function pointers and struct size in BMW_init */ + BMW_CUSTOM, + BMW_MAXWALKERS +}; + +/* use with BMW_init, so as not to confuse with restrict flags */ +#define BMW_NIL_LAY 0 + +#endif /* __BMESH_WALKERS_H__ */ diff --git a/source/blender/bmesh/editmesh_tools.c b/source/blender/bmesh/editmesh_tools.c new file mode 100644 index 00000000000..93be1ac5422 --- /dev/null +++ b/source/blender/bmesh/editmesh_tools.c @@ -0,0 +1,6384 @@ +#if 0 + +/* + * ***** 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 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Johnny Matthews, Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* + +editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c + +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BMF_Api.h" +#include "DNA_mesh_types.h" +#include "DNA_material_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" +#include "DNA_key_types.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_heap.h" + +#include "BKE_depsgraph.h" +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" + +#ifdef WITH_VERSE +#include "BKE_verse.h" +#endif + +#include "BIF_cursors.h" +#include "BIF_editmesh.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" +#include "BIF_graphics.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_resources.h" +#include "BIF_toolbox.h" +#include "BIF_transform.h" +#include "transform.h" + +#ifdef WITH_VERSE +#include "BIF_verse.h" +#endif + +#include "BDR_drawobject.h" +#include "BDR_editobject.h" + +#include "BSE_view.h" +#include "BSE_edit.h" + +#include "blendef.h" +#include "multires.h" +#include "mydevice.h" + +#include "editmesh.h" + +#include "MTC_vectorops.h" + +#include "PIL_time.h" + +#include "BLO_sys_types.h" // for intptr_t support + +/* local prototypes ---------------*/ +void bevel_menu(void); +static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa); + +/********* qsort routines *********/ + + +typedef struct xvertsort { + float x; + EditVert *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 { + uintptr_t x; + struct EditFace *efa; +}; + + +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; +} + + +/* *********************************** */ + +void convert_to_triface(int direction) +{ + EditMesh *em = G.editMesh; + EditFace *efa, *efan, *next; + float fac; + + if(multires_test()) return; + + efa= em->faces.last; + while(efa) { + next= efa->prev; + if(efa->v4) { + if(efa->f & SELECT) { + /* choose shortest diagonal for split */ + fac= len_v3v3(efa->v1->co, efa->v3->co) - len_v3v3(efa->v2->co, efa->v4->co); + /* this makes sure exact squares get split different in both cases */ + if( (direction==0 && fac<FLT_EPSILON) || (direction && fac>0.0f) ) { + efan= EM_face_from_faces(efa, NULL, 0, 1, 2, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + efan= EM_face_from_faces(efa, NULL, 0, 2, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + } + else { + efan= EM_face_from_faces(efa, NULL, 0, 1, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + efan= EM_face_from_faces(efa, NULL, 1, 2, 3, -1); + if(efa->f & SELECT) EM_select_face(efan, 1); + } + + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + } + efa= next; + } + + EM_fgon_flags(); // redo flags and indices for fgons + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Convert Quads to Triangles"); + +} + +int removedoublesflag(short flag, short automerge, float limit) /* return amount */ +{ + /* + flag - Test with vert->flags + automerge - Alternative operation, merge unselected into selected. + Used for "Auto Weld" mode. warning. + limit - Quick manhattan distance between verts. + */ + + EditMesh *em = G.editMesh; + /* all verts with (flag & 'flag') are being evaluated */ + EditVert *eve, *v1, *nextve; + EditEdge *eed, *e1, *nexted; + EditFace *efa, *nextvl; + xvertsort *sortblock, *sb, *sb1; + struct facesort *vlsortblock, *vsb, *vsb1; + int a, b, test, amount; + + if(multires_test()) return 0; + + + /* flag 128 is cleared, count */ + + /* Normal non weld operation */ + eve= em->verts.first; + amount= 0; + while(eve) { + eve->f &= ~128; + if(eve->h==0 && (automerge || (eve->f & flag))) amount++; + eve= eve->next; + } + if(amount==0) return 0; + + /* allocate memory and qsort */ + sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub"); + eve= em->verts.first; + while(eve) { + if(eve->h==0 && (automerge || (eve->f & flag))) { + sb->x= eve->co[0]+eve->co[1]+eve->co[2]; + sb->v1= eve; + sb++; + } + eve= eve->next; + } + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + + /* test for doubles */ + sb= sortblock; + if (automerge) { + for(a=0; a<amount; a++, sb++) { + eve= sb->v1; + if( (eve->f & 128)==0 ) { + sb1= sb+1; + for(b=a+1; b<amount && (eve->f & 128)==0; b++, sb1++) { + if(sb1->x - sb->x > limit) break; + + /* when automarge, only allow unselected->selected */ + v1= sb1->v1; + if( (v1->f & 128)==0 ) { + if ((eve->f & flag)==0 && (v1->f & flag)==1) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { /* unique bit */ + eve->f|= 128; + eve->tmp.v = v1; + } + } else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { /* unique bit */ + v1->f|= 128; + v1->tmp.v = eve; + } + } + } + } + } + } + } else { + for(a=0; a<amount; a++, sb++) { + eve= sb->v1; + if( (eve->f & 128)==0 ) { + sb1= sb+1; + for(b=a+1; b<amount; b++, sb1++) { + /* first test: simpel dist */ + if(sb1->x - sb->x > limit) break; + v1= sb1->v1; + + /* second test: is vertex allowed */ + if( (v1->f & 128)==0 ) { + if( (float)fabs(v1->co[0]-eve->co[0])<=limit && + (float)fabs(v1->co[1]-eve->co[1])<=limit && + (float)fabs(v1->co[2]-eve->co[2])<=limit) + { + v1->f|= 128; + v1->tmp.v = eve; + } + } + } + } + } + } + MEM_freeN(sortblock); + + if (!automerge) + 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); + + /* test edges and insert again */ + eed= em->edges.first; + while(eed) { + eed->f2= 0; + eed= eed->next; + } + eed= em->edges.last; + while(eed) { + nexted= eed->prev; + + if(eed->f2==0) { + if( (eed->v1->f & 128) || (eed->v2->f & 128) ) { + remedge(eed); + + if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v; + if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v; + e1= addedgelist(eed->v1, eed->v2, eed); + + if(e1) { + e1->f2= 1; + if(eed->f & SELECT) + e1->f |= SELECT; + } + if(e1!=eed) free_editedge(eed); + } + } + eed= nexted; + } + + /* first count amount of test faces */ + efa= (struct EditFace *)em->faces.first; + amount= 0; + while(efa) { + efa->f1= 0; + if(efa->v1->f & 128) efa->f1= 1; + else if(efa->v2->f & 128) efa->f1= 1; + else if(efa->v3->f & 128) efa->f1= 1; + else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1; + + if(efa->f1==1) amount++; + efa= efa->next; + } + + /* test faces for double vertices, and if needed remove them */ + efa= (struct EditFace *)em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1==1) { + + if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v; + if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v; + if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v; + if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v; + + test= 0; + if(efa->v1==efa->v2) test+=1; + if(efa->v2==efa->v3) test+=2; + if(efa->v3==efa->v1) test+=4; + if(efa->v4==efa->v1) test+=8; + if(efa->v3==efa->v4) test+=16; + if(efa->v2==efa->v4) test+=32; + + if(test) { + if(efa->v4) { + if(test==1 || test==2) { + efa->v2= efa->v3; + efa->v3= efa->v4; + efa->v4= 0; + + EM_data_interp_from_faces(efa, NULL, efa, 0, 2, 3, 3); + + test= 0; + } + else if(test==8 || test==16) { + efa->v4= 0; + test= 0; + } + else { + BLI_remlink(&em->faces, efa); + free_editface(efa); + amount--; + } + } + else { + BLI_remlink(&em->faces, efa); + free_editface(efa); + amount--; + } + } + + if(test==0) { + /* set edge pointers */ + efa->e1= findedgelist(efa->v1, efa->v2); + efa->e2= findedgelist(efa->v2, efa->v3); + if(efa->v4==0) { + efa->e3= findedgelist(efa->v3, efa->v1); + efa->e4= 0; + } + else { + efa->e3= findedgelist(efa->v3, efa->v4); + efa->e4= findedgelist(efa->v4, efa->v1); + } + } + } + efa= nextvl; + } + + /* double faces: sort block */ + /* count again, now all selected faces */ + amount= 0; + efa= em->faces.first; + while(efa) { + efa->f1= 0; + if(faceselectedOR(efa, 1)) { + efa->f1= 1; + amount++; + } + efa= efa->next; + } + + if(amount) { + /* double faces: sort block */ + vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub"); + efa= em->faces.first; + while(efa) { + if(efa->f1 & 1) { + if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4); + else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3); + + vsb->efa= efa; + vsb++; + } + efa= efa->next; + } + + qsort(vlsortblock, amount, sizeof(struct facesort), vergface); + + vsb= vlsortblock; + for(a=0; a<amount; a++) { + efa= vsb->efa; + if( (efa->f1 & 128)==0 ) { + vsb1= vsb+1; + + for(b=a+1; b<amount; b++) { + + /* first test: same pointer? */ + if(vsb->x != vsb1->x) break; + + /* second test: is test permitted? */ + efa= vsb1->efa; + if( (efa->f1 & 128)==0 ) { + if( compareface(efa, vsb->efa)) efa->f1 |= 128; + + } + vsb1++; + } + } + vsb++; + } + + MEM_freeN(vlsortblock); + + /* remove double faces */ + efa= (struct EditFace *)em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f1 & 128) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + } + + /* remove double vertices */ + a= 0; + eve= (struct EditVert *)em->verts.first; + while(eve) { + nextve= eve->next; + if(automerge || eve->f & flag) { + if(eve->f & 128) { + a++; + BLI_remlink(&em->verts, eve); + free_editvert(eve); + } + } + eve= nextve; + } + +#ifdef WITH_VERSE + if((a>0) && (G.editMesh->vnode)) { + sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode); + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + } +#endif + + return a; /* amount */ +} + +/* called from buttons */ +static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index) +{ + xvertsort *sortblock = userData; + + sortblock[index].x = x; +} +void xsortvert_flag(int flag) +{ + EditMesh *em = G.editMesh; + /* all verts with (flag & 'flag') are sorted */ + EditVert *eve; + xvertsort *sortblock; + ListBase tbase; + int i, amount = BLI_countlist(&em->verts); + + if(multires_test()) return; + + sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort"); + for (i=0,eve=em->verts.first; eve; i++,eve=eve->next) + if(eve->f & flag) + sortblock[i].v1 = eve; + mesh_foreachScreenVert(xsortvert_flag__doSetX, sortblock, V3D_CLIP_TEST_OFF); + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + /* make temporal listbase */ + tbase.first= tbase.last= 0; + for (i=0; i<amount; i++) { + eve = sortblock[i].v1; + + if (eve) { + BLI_remlink(&em->verts, eve); + BLI_addtail(&tbase, eve); + } + } + + addlisttolist(&em->verts, &tbase); + + MEM_freeN(sortblock); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Xsort"); + +} + +/* called from buttons */ +void hashvert_flag(int flag) +{ + /* switch vertex order using hash table */ + EditMesh *em = G.editMesh; + EditVert *eve; + struct xvertsort *sortblock, *sb, onth, *newsort; + ListBase tbase; + int amount, a, b; + + if(multires_test()) return; + + /* count */ + eve= em->verts.first; + amount= 0; + while(eve) { + if(eve->f & flag) amount++; + eve= eve->next; + } + if(amount==0) return; + + /* allocate memory */ + sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub"); + eve= em->verts.first; + while(eve) { + if(eve->f & flag) { + sb->v1= eve; + sb++; + } + eve= eve->next; + } + + BLI_srand(1); + + sb= sortblock; + for(a=0; a<amount; a++, sb++) { + b= (int)(amount*BLI_drand()); + if(b>=0 && b<amount) { + newsort= sortblock+b; + onth= *sb; + *sb= *newsort; + *newsort= onth; + } + } + + /* make temporal listbase */ + tbase.first= tbase.last= 0; + sb= sortblock; + while(amount--) { + eve= sb->v1; + BLI_remlink(&em->verts, eve); + BLI_addtail(&tbase, eve); + sb++; + } + + addlisttolist(&em->verts, &tbase); + + MEM_freeN(sortblock); +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Hash"); + +} + +/* generic extern called extruder */ +void extrude_mesh(void) +{ + float nor[3]= {0.0, 0.0, 0.0}; + short nr, transmode= 0; + + TEST_EDITMESH + if(multires_test()) return; + + if(G.scene->selectmode & SCE_SELECT_VERTEX) { + if(G.totvertsel==0) nr= 0; + else if(G.totvertsel==1) nr= 4; + else if(G.totedgesel==0) nr= 4; + else if(G.totfacesel==0) + nr= pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4"); + else if(G.totfacesel==1) + nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4"); + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4"); + } + else if(G.scene->selectmode & SCE_SELECT_EDGE) { + if (G.totedgesel==0) nr = 0; + else if (G.totedgesel==1) nr = 3; + else if(G.totfacesel==0) nr = 3; + else if(G.totfacesel==1) + nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3"); + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3"); + } + else { + if (G.totfacesel == 0) nr = 0; + else if (G.totfacesel == 1) nr = 1; + else + nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2"); + } + + if(nr<1) return; + + if(nr==1) transmode= extrudeflag(SELECT, nor); + else if(nr==4) transmode= extrudeflag_verts_indiv(SELECT, nor); + else if(nr==3) transmode= extrudeflag_edges_indiv(SELECT, nor); + else transmode= extrudeflag_face_indiv(SELECT, nor); + + if(transmode==0) { + error("No valid selection"); + } + else { + EM_fgon_flags(); + countall(); + + /* We need to force immediate calculation here because + * transform may use derived objects (which are now stale). + * + * This shouldn't be necessary, derived queries should be + * automatically building this data if invalid. Or something. + */ + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + object_handle_update(G.obedit); + + /* individual faces? */ + BIF_TransformSetUndo("Extrude"); + if(nr==2) { + initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR); + Transform(); + } + else { + initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR); + if(transmode=='n') { + mul_m4_v3(G.obedit->obmat, nor); + sub_v3_v3v3(nor, nor, G.obedit->obmat[3]); + BIF_setSingleAxisConstraint(nor, "along normal"); + } + Transform(); + } + } + +} + +void split_mesh(void) +{ + + TEST_EDITMESH + if(multires_test()) return; + + if(okee(" Split ")==0) return; + + waitcursor(1); + + /* make duplicate first */ + adduplicateflag(SELECT); + /* old faces have flag 128 set, delete them */ + delfaceflag(128); + recalc_editnormals(); + + waitcursor(0); + + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Split"); + +} + +void extrude_repeat_mesh(int steps, float offs) +{ + float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0}; + short a; + + TEST_EDITMESH + if(multires_test()) return; + + /* dvec */ + dvec[0]= G.vd->persinv[2][0]; + dvec[1]= G.vd->persinv[2][1]; + dvec[2]= G.vd->persinv[2][2]; + normalize_v3(dvec); + dvec[0]*= offs; + dvec[1]*= offs; + dvec[2]*= offs; + + /* base correction */ + copy_m3_m4(bmat, G.obedit->obmat); + invert_m3_m3(tmat, bmat); + mul_m3_v3(tmat, dvec); + + for(a=0; a<steps; a++) { + extrudeflag(SELECT, nor); + translateflag(SELECT, dvec); + } + + recalc_editnormals(); + + EM_fgon_flags(); + countall(); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + BIF_undo_push("Extrude Repeat"); +} + +void spin_mesh(int steps, float degr, float *dvec, int mode) +{ + EditMesh *em = G.editMesh; + EditVert *eve,*nextve; + float nor[3]= {0.0, 0.0, 0.0}; + float *curs, si,n[3],q[4],cmat[3][3],imat[3][3], tmat[3][3]; + float cent[3],bmat[3][3]; + float phi; + short a,ok; + + TEST_EDITMESH + if(multires_test()) return; + + /* imat and center and size */ + copy_m3_m4(bmat, G.obedit->obmat); + invert_m3_m3(imat,bmat); + + curs= give_cursor(); + copy_v3_v3(cent, curs); + cent[0]-= G.obedit->obmat[3][0]; + cent[1]-= G.obedit->obmat[3][1]; + cent[2]-= G.obedit->obmat[3][2]; + mul_m3_v3(imat, cent); + + phi= degr*M_PI/360.0; + phi/= steps; + if(G.scene->toolsettings->editbutflag & B_CLOCKWISE) phi= -phi; + + if(dvec) { + n[0]= G.vd->viewinv[1][0]; + n[1]= G.vd->viewinv[1][1]; + n[2]= G.vd->viewinv[1][2]; + } else { + n[0]= G.vd->viewinv[2][0]; + n[1]= G.vd->viewinv[2][1]; + n[2]= G.vd->viewinv[2][2]; + } + normalize_v3(n); + + q[0]= (float)cos(phi); + si= (float)sin(phi); + q[1]= n[0]*si; + q[2]= n[1]*si; + q[3]= n[2]*si; + quat_to_mat3( cmat,q); + + mul_m3_m3m3(tmat,cmat,bmat); + mul_m3_m3m3(bmat,imat,tmat); + + if(mode==0) if(G.scene->toolsettings->editbutflag & B_KEEPORIG) adduplicateflag(1); + ok= 1; + + for(a=0;a<steps;a++) { + if(mode==0) ok= extrudeflag(SELECT, nor); + else adduplicateflag(SELECT); + if(ok==0) { + error("No valid vertices are selected"); + break; + } + rotateflag(SELECT, cent, bmat); + if(dvec) { + mul_m3_v3(bmat,dvec); + translateflag(SELECT, dvec); + } + } + + if(ok==0) { + /* no vertices or only loose ones selected, remove duplicates */ + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & SELECT) { + BLI_remlink(&em->verts,eve); + free_editvert(eve); + } + eve= nextve; + } + } + recalc_editnormals(); + + EM_fgon_flags(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + + if(dvec==NULL) BIF_undo_push("Spin"); +} + +void screw_mesh(int steps, int turns) +{ + EditMesh *em = G.editMesh; + EditVert *eve,*v1=0,*v2=0; + EditEdge *eed; + float dvec[3], nor[3]; + + TEST_EDITMESH + if(multires_test()) return; + + /* clear flags */ + eve= em->verts.first; + while(eve) { + eve->f1= 0; + eve= eve->next; + } + /* edges set flags in verts */ + eed= em->edges.first; + while(eed) { + if(eed->v1->f & SELECT) { + if(eed->v2->f & SELECT) { + /* watch: f1 is a byte */ + if(eed->v1->f1<2) eed->v1->f1++; + if(eed->v2->f1<2) eed->v2->f1++; + } + } + eed= eed->next; + } + /* find two vertices with eve->f1==1, more or less is wrong */ + eve= em->verts.first; + while(eve) { + if(eve->f1==1) { + if(v1==0) v1= eve; + else if(v2==0) v2= eve; + else { + v1=0; + break; + } + } + eve= eve->next; + } + if(v1==0 || v2==0) { + error("You have to select a string of connected vertices too"); + return; + } + + /* calculate dvec */ + dvec[0]= ( (v1->co[0]- v2->co[0]) )/(steps); + dvec[1]= ( (v1->co[1]- v2->co[1]) )/(steps); + dvec[2]= ( (v1->co[2]- v2->co[2]) )/(steps); + + copy_v3_v3(nor, G.obedit->obmat[2]); + + if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) { + dvec[0]= -dvec[0]; + dvec[1]= -dvec[1]; + dvec[2]= -dvec[2]; + } + + spin_mesh(turns*steps, turns*360, dvec, 0); + + BIF_undo_push("Spin"); +} + + +static void erase_edges(ListBase *l) +{ + EditEdge *ed, *nexted; + + ed = (EditEdge *) l->first; + while(ed) { + nexted= ed->next; + if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) { + remedge(ed); + free_editedge(ed); + } + ed= nexted; + } +} + +static void erase_faces(ListBase *l) +{ + EditFace *f, *nextf; + + f = (EditFace *) l->first; + + while(f) { + nextf= f->next; + if( faceselectedOR(f, SELECT) ) { + BLI_remlink(l, f); + free_editface(f); + } + f = nextf; + } +} + +static void erase_vertices(ListBase *l) +{ + EditVert *v, *nextv; + + v = (EditVert *) l->first; + while(v) { + nextv= v->next; + if(v->f & 1) { + BLI_remlink(l, v); + free_editvert(v); + } + v = nextv; + } +} + +void delete_mesh(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa, *nextvl; + EditVert *eve,*nextve; + EditEdge *eed,*nexted; + short event; + int count; + char *str="Erase"; + + TEST_EDITMESH + if(multires_test()) return; + + event= pupmenu("Erase %t|Vertices%x10|Edges%x1|Faces%x2|All%x3|Edges & Faces%x4|Only Faces%x5|Edge Loop%x6"); + if(event<1) return; + + if(event==10 ) { + str= "Erase Vertices"; + erase_edges(&em->edges); + erase_faces(&em->faces); + erase_vertices(&em->verts); + } + else if(event==6) { + if(!EdgeLoopDelete()) + return; + + str= "Erase Edge Loop"; + } + else if(event==4) { + str= "Erase Edges & Faces"; + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + /* delete only faces with 1 or more edges selected */ + count= 0; + if(efa->e1->f & SELECT) count++; + if(efa->e2->f & SELECT) count++; + if(efa->e3->f & SELECT) count++; + if(efa->e4 && (efa->e4->f & SELECT)) count++; + if(count) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + remedge(eed); + free_editedge(eed); + } + eed= nexted; + } + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + event=0; + if( efa->v1->f & SELECT) event++; + if( efa->v2->f & SELECT) event++; + if( efa->v3->f & SELECT) event++; + if(efa->v4 && (efa->v4->f & SELECT)) event++; + + if(event>1) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + } + else if(event==1) { + str= "Erase Edges"; + // faces first + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + event=0; + if( efa->e1->f & SELECT) event++; + if( efa->e2->f & SELECT) event++; + if( efa->e3->f & SELECT) event++; + if(efa->e4 && (efa->e4->f & SELECT)) event++; + + if(event) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + eed= em->edges.first; + while(eed) { + nexted= eed->next; + if(eed->f & SELECT) { + remedge(eed); + free_editedge(eed); + } + eed= nexted; + } + /* to remove loose vertices: */ + eed= em->edges.first; + while(eed) { + if( eed->v1->f & SELECT) eed->v1->f-=SELECT; + if( eed->v2->f & SELECT) eed->v2->f-=SELECT; + eed= eed->next; + } + eve= em->verts.first; + while(eve) { + nextve= eve->next; + if(eve->f & SELECT) { + BLI_remlink(&em->verts,eve); + free_editvert(eve); + } + eve= nextve; + } + + } + else if(event==2) { + str="Erase Faces"; + delfaceflag(SELECT); + } + else if(event==3) { + str= "Erase All"; + if(em->verts.first) free_vertlist(&em->verts); + if(em->edges.first) free_edgelist(&em->edges); + if(em->faces.first) free_facelist(&em->faces); + if(em->selected.first) BLI_freelistN(&(em->selected)); + } + else if(event==5) { + str= "Erase Only Faces"; + efa= em->faces.first; + while(efa) { + nextvl= efa->next; + if(efa->f & SELECT) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + efa= nextvl; + } + } + + EM_fgon_flags(); // redo flags and indices for fgons + + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + BIF_undo_push(str); +} + + +/* Got this from scanfill.c. You will need to juggle around the + * callbacks for the scanfill.c code a bit for this to work. */ +void fill_mesh(void) +{ + EditMesh *em = G.editMesh; + EditVert *eve,*v1; + EditEdge *eed,*e1,*nexted; + EditFace *efa,*nextvl, *efan; + short ok; + + if(G.obedit==0 || (G.obedit->type!=OB_MESH)) return; + if(multires_test()) return; + + waitcursor(1); + + /* copy all selected vertices */ + eve= em->verts.first; + while(eve) { + if(eve->f & SELECT) { + v1= BLI_addfillvert(eve->co); + eve->tmp.v= v1; + v1->tmp.v= eve; + v1->xs= 0; // used for counting edges + } + eve= eve->next; + } + /* copy all selected edges */ + eed= em->edges.first; + while(eed) { + if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) { + e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v); + e1->v1->xs++; + e1->v2->xs++; + } + eed= eed->next; + } + /* from all selected faces: remove vertices and edges to prevent doubles */ + /* all edges add values, faces subtract, + then remove edges with vertices ->xs<2 */ + efa= em->faces.first; + ok= 0; + while(efa) { + nextvl= efa->next; + if( faceselectedAND(efa, 1) ) { + efa->v1->tmp.v->xs--; + efa->v2->tmp.v->xs--; + efa->v3->tmp.v->xs--; + if(efa->v4) efa->v4->tmp.v->xs--; + ok= 1; + + } + efa= nextvl; + } + if(ok) { /* there are faces selected */ + eed= filledgebase.first; + while(eed) { + nexted= eed->next; + if(eed->v1->xs<2 || eed->v2->xs<2) { + BLI_remlink(&filledgebase,eed); + } + eed= nexted; + } + } + + if(BLI_edgefill(0, (G.obedit && G.obedit->actcol)?(G.obedit->actcol-1):0)) { + efa= fillfacebase.first; + while(efa) { + /* normals default pointing up */ + efan= addfacelist(efa->v3->tmp.v, efa->v2->tmp.v, + efa->v1->tmp.v, 0, NULL, NULL); + if(efan) EM_select_face(efan, 1); + efa= efa->next; + } + } + + BLI_end_edgefill(); + + waitcursor(0); + EM_select_flush(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Fill"); +} +/*GB*/ +/*-------------------------------------------------------------------------------*/ +/*--------------------------- Edge Based Subdivide ------------------------------*/ + +#define EDGENEW 2 +#define FACENEW 2 +#define EDGEINNER 4 +#define EDGEOLD 8 + +/*used by faceloop cut to select only edges valid for edge slide*/ +#define DOUBLEOPFILL 16 + +/* calculates offset for co, based on fractal, sphere or smooth settings */ +static void alter_co(float *co, EditEdge *edge, float rad, int beauty, float perc) +{ + float vec1[3], fac; + + if(beauty & B_SMOOTH) { + /* we calculate an offset vector vec1[], to be added to *co */ + float len, fac, nor[3], nor1[3], nor2[3]; + + sub_v3_v3v3(nor, edge->v1->co, edge->v2->co); + len= 0.5f*normalize_v3(nor); + + copy_v3_v3(nor1, edge->v1->no); + copy_v3_v3(nor2, edge->v2->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_v3(co); + co[0]*= rad; + co[1]*= rad; + co[2]*= rad; + } + else if(rad< 0.0) { /* fractal subdivide */ + fac= rad* len_v3v3(edge->v1->co, edge->v2->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()); + add_v3_v3v3(co, co, vec1); + } + + } +} + +/* assumes in the edge is the correct interpolated vertices already */ +/* percent defines the interpolation, rad and beauty are for special options */ +/* results in new vertex with correct coordinate, vertex normal and weight group info */ +static EditVert *subdivide_edge_addvert(EditEdge *edge, float rad, int beauty, float percent) +{ + EditVert *ev; + float co[3]; + + co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0]; + co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1]; + co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2]; + + /* offset for smooth or sphere or fractal */ + alter_co(co, edge, rad, beauty, percent); + + /* 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; + } + } + + ev = addvertlist(co, NULL); + + /* vert data (vgroups, ..) */ + EM_data_interp_from_verts(edge->v1, edge->v2, ev, percent); + + /* normal */ + ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0]; + ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1]; + ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2]; + normalize_v3(ev->no); + + return ev; +} + +static void flipvertarray(EditVert** arr, short size) +{ + EditVert *hold; + int i; + + for(i=0; i<size/2; i++) { + hold = arr[i]; + arr[i] = arr[size-i-1]; + arr[size-i-1] = hold; + } +} + +static void facecopy(EditFace *source, EditFace *target) +{ + EditMesh *em= G.editMesh; + float *v1 = source->v1->co, *v2 = source->v2->co, *v3 = source->v3->co; + float *v4 = source->v4? source->v4->co: NULL; + float w[4][4]; + + CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data); + + target->mat_nr = source->mat_nr; + target->flag = source->flag; + target->h = source->h; + + interp_weights_face_v3( w[0],v1, v2, v3, v4, target->v1->co); + interp_weights_face_v3( w[1],v1, v2, v3, v4, target->v2->co); + interp_weights_face_v3( w[2],v1, v2, v3, v4, target->v3->co); + if (target->v4) + interp_weights_face_v3( w[3],v1, v2, v3, v4, target->v4->co); + + CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data); +} + +static void fill_quad_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype) +{ + EditEdge *cedge=NULL; + EditVert *v[4], **verts; + EditFace *hold; + short start=0, end, left, right, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + | | + | | + | | + ------------- + left right + + where start,end,left, right are indexes of EditFace->v1, etc (stored in v) + and 0,1,2... are the indexes of the new verts stored in verts + + We will fill this case like this or this depending on even or odd cuts + + |---*---*---| |---*---| + | / \ | | / \ | + | / \ | | / \ | + |/ \| |/ \| + ------------- --------- + */ + + // Make center face + if(vertsize % 2 == 0) { + hold = addfacelist(verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e4->f2 |= EDGEINNER; + }else{ + hold = addfacelist(verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL); + hold->e1->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + } + facecopy(efa,hold); + + // Make side faces + for(i=0;i<(vertsize-1)/2;i++) { + hold = addfacelist(verts[i],verts[i+1],v[right],NULL,NULL,NULL); + facecopy(efa,hold); + if(i+1 != (vertsize-1)/2) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e2->f2 |= EDGEINNER; + } + } + hold = addfacelist(verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL); + facecopy(efa,hold); + if(i+1 != (vertsize-1)/2) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e3->f2 |= EDGEINNER; + } + } + } +} + +static void fill_tri_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype) +{ + EditEdge *cedge=NULL; + EditVert *v[3], **verts; + EditFace *hold; + short start=0, end, op, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} + end = (start+1)%3; + op = (start+2)%3; + + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + \ | + \ | + \ | + \ | + \ | + \ | + |op + + where start,end,op are indexes of EditFace->v1, etc (stored in v) + and 0,1,2... are the indexes of the new verts stored in verts + + We will fill this case like this or this depending on even or odd cuts + + 3 2 1 0 + |---*---*---| + \ \ \ | + \ \ \ | + \ \ \ | + \ \ \| + \ \\| + \ | + |op + */ + + // Make side faces + for(i=0;i<(vertsize-1);i++) { + hold = addfacelist(verts[i],verts[i+1],v[op],NULL,NULL,NULL); + if(i+1 != vertsize-1) { + if(seltype == SUBDIV_SELECT_INNER) { + hold->e2->f2 |= EDGEINNER; + } + } + facecopy(efa,hold); + } +} + +static void fill_quad_double_op(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], **verts[2]; + EditFace *hold; + short start=0, end, left, right, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;} + else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + |---*---*---| + | | + | | + | | + |---*---*---| + 0 1 2 3 + left right + + We will fill this case like this or this depending on even or odd cuts + + |---*---*---| + | | | | + | | | | + | | | | + |---*---*---| + */ + + // Make side faces + for(i=0;i<vertsize-1;i++) { + hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-2-i],verts[1][vertsize-1-i],NULL,NULL); + if(i < vertsize-2) { + hold->e2->f2 |= EDGEINNER; + hold->e2->f2 |= DOUBLEOPFILL; + } + facecopy(efa,hold); + } +} + +static void fill_quad_double_adj_path(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------| + + We will fill this case like this or this depending on even or odd cuts + |---*---*---| + | / / / | + * / / | + | / / | + * / | + | / | + |-----------| + */ + + // Make outside tris + hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + /* when ctrl is depressed, only want verts on the cutline selected */ + if (G.qual != LR_CTRLKEY) + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + hold = addfacelist(verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL); + /* when ctrl is depressed, only want verts on the cutline selected */ + if (G.qual != LR_CTRLKEY) + hold->e1->f2 |= EDGEINNER; + facecopy(efa,hold); + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + // Make side faces + + for(i=0;i<numcuts;i++) { + hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + } + //EM_fgon_flags(); + +} + +static void fill_quad_double_adj_fan(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], *op=NULL, **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} + + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------|op + + We will fill this case like this or this (warning horrible ascii art follows) + |---*---*---| + | \ \ \ | + *---\ \ \ | + | \ \ \ \| + *---- \ \ \ | + | --- \\\| + |-----------| + */ + + for(i=0;i<=numcuts;i++) { + hold = addfacelist(op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL); + hold->e1->f2 |= EDGEINNER; + facecopy(efa,hold); + + hold = addfacelist(op,verts[0][i],verts[0][i+1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + } +} + +static void fill_quad_double_adj_inner(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[4], *op=NULL, **verts[2],**inner; + EditFace *hold; + short start=0, start2=0, vertsize,i; + float co[3]; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} + if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} + if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} + + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | | + 1* | + | | + 2* | + | | + end2 3|-----------|op + + We will fill this case like this or this (warning horrible ascii art follows) + |---*-----*---| + | * / | + * \ / | + | * | + | / \ | + * \ | + | \ | + |-------------| + */ + + // Add Inner Vert(s) + inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts"); + + for(i=0;i<numcuts;i++) { + co[0] = (verts[0][numcuts-i]->co[0] + verts[1][i+1]->co[0] ) / 2 ; + co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ; + co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ; + inner[i] = addvertlist(co, NULL); + inner[i]->f2 |= EDGEINNER; + + EM_data_interp_from_verts(verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f); + } + + // Add Corner Quad + hold = addfacelist(verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + // Add Bottom Quads + hold = addfacelist(verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + + hold = addfacelist(op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + // Add Fill Quads (if # cuts > 1) + + for(i=0;i<numcuts-1;i++) { + hold = addfacelist(inner[i],verts[1][i+1],verts[1][i+2],inner[i+1],NULL,NULL); + hold->e1->f2 |= EDGEINNER; + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + + hold = addfacelist(inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + hold->e4->f2 |= EDGEINNER; + facecopy(efa,hold); + + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e1->h |= EM_FGON; + //} + } + + //EM_fgon_flags(); + + MEM_freeN(inner); +} + +static void fill_tri_double(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[2]={NULL, NULL}; + EditVert *v[3], **verts[2]; + EditFace *hold; + short start=0, start2=0, vertsize,i; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + + if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} + if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} + if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;} + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + /* + We should have something like this now + + end start + 3 2 1 0 + start2 0|---*---*---| + | / + 1* / + | / + 2* / + | / + end2 3| + + We will fill this case like this or this depending on even or odd cuts + |---*---*---| + | / / / + * / / + | / / + * / + | / + | + */ + + // Make outside tri + hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + // Make side faces + + for(i=0;i<numcuts;i++) { + hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + } +} + +static void fill_quad_triple(EditFace *efa, struct GHash *gh, int numcuts) +{ + EditEdge *cedge[3]={0}; + EditVert *v[4], **verts[3]; + EditFace *hold; + short start=0, start2=0, start3=0, vertsize, i, repeats; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(!(efa->e1->f & SELECT)) { + cedge[0] = efa->e2; + cedge[1] = efa->e3; + cedge[2] = efa->e4; + start = 1;start2 = 2;start3 = 3; + } + if(!(efa->e2->f & SELECT)) { + cedge[0] = efa->e3; + cedge[1] = efa->e4; + cedge[2] = efa->e1; + start = 2;start2 = 3;start3 = 0; + } + if(!(efa->e3->f & SELECT)) { + cedge[0] = efa->e4; + cedge[1] = efa->e1; + cedge[2] = efa->e2; + start = 3;start2 = 0;start3 = 1; + } + if(!(efa->e4->f & SELECT)) { + cedge[0] = efa->e1; + cedge[1] = efa->e2; + cedge[2] = efa->e3; + start = 0;start2 = 1;start3 = 2; + } + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, cedge[0]); + verts[1] = BLI_ghash_lookup(gh, cedge[1]); + verts[2] = BLI_ghash_lookup(gh, cedge[2]); + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);} + /* + We should have something like this now + + start2 + 3 2 1 0 + start3 0|---*---*---|3 + | | + 1* *2 + | | + 2* *1 + | | + 3|-----------|0 start + + We will fill this case like this or this depending on even or odd cuts + there are a couple of differences. For odd cuts, there is a tri in the + middle as well as 1 quad at the bottom (not including the extra quads + for odd cuts > 1 + + For even cuts, there is a quad in the middle and 2 quads on the bottom + + they are numbered here for clarity + + 1 outer tris and bottom quads + 2 inner tri or quad + 3 repeating quads + + |---*---*---*---| + |1/ / \ \ 1| + |/ 3 / \ 3 \| + * / 2 \ * + | / \ | + |/ \ | + *---------------* + | 3 | + | | + *---------------* + | | + | 1 | + | | + |---------------| + + |---*---*---*---*---| + | 1/ / \ \ 1| + | / / \ \ | + |/ 3 / \ 3 \| + * / \ * + | / \ | + | / 2 \ | + |/ \| + *-------------------* + | | + | 3 | + | | + *-------------------* + | | + | 1 | + | | + *-------------------* + | | + | 1 | + | | + |-------------------| + + */ + + // Make outside tris + hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + hold = addfacelist(verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL); + hold->e3->f2 |= EDGEINNER; + facecopy(efa,hold); + // Make bottom quad + hold = addfacelist(verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + //If it is even cuts, add the 2nd lower quad + if(numcuts % 2 == 0) { + hold = addfacelist(verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + // Also Make inner quad + hold = addfacelist(verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL); + hold->e3->f2 |= EDGEINNER; + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e3->h |= EM_FGON; + //} + facecopy(efa,hold); + repeats = (numcuts / 2) -1; + } else { + // Make inner tri + hold = addfacelist(verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL); + hold->e2->f2 |= EDGEINNER; + //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) { + // hold->e2->h |= EM_FGON; + //} + facecopy(efa,hold); + repeats = ((numcuts+1) / 2)-1; + } + + // cuts for 1 and 2 do not have the repeating quads + if(numcuts < 3) {repeats = 0;} + for(i=0;i<repeats;i++) { + //Make side repeating Quads + hold = addfacelist(verts[1][i+1],verts[1][i+2],verts[0][vertsize-i-3],verts[0][vertsize-i-2],NULL,NULL); + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + hold = addfacelist(verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL); + hold->e4->f2 |= EDGEINNER; + facecopy(efa,hold); + } + // Do repeating bottom quads + for(i=0;i<repeats;i++) { + if(numcuts % 2 == 1) { + hold = addfacelist(verts[0][1+i],verts[0][2+i],verts[2][vertsize-3-i],verts[2][vertsize-2-i],NULL,NULL); + } else { + hold = addfacelist(verts[0][2+i],verts[0][3+i],verts[2][vertsize-4-i],verts[2][vertsize-3-i],NULL,NULL); + } + hold->e2->f2 |= EDGEINNER; + facecopy(efa,hold); + } + //EM_fgon_flags(); +} + +static void fill_quad_quadruple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty) +{ + EditVert **verts[4], ***innerverts; + EditFace *hold; + EditEdge temp; + short vertsize, i, j; + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, efa->e1); + verts[1] = BLI_ghash_lookup(gh, efa->e2); + verts[2] = BLI_ghash_lookup(gh, efa->e3); + verts[3] = BLI_ghash_lookup(gh, efa->e4); + + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);} + if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);} + /* + We should have something like this now + 1 + + 3 2 1 0 + 0|---*---*---|0 + | | + 1* *1 + 2 | | 4 + 2* *2 + | | + 3|---*---*---|3 + 3 2 1 0 + + 3 + // we will fill a 2 dim array of editvert*s to make filling easier + // the innervert order is shown + + 0 0---1---2---3 + | | | | + 1 0---1---2---3 + | | | | + 2 0---1---2---3 + | | | | + 3 0---1---2---3 + + */ + innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array"); + for(i=0;i<numcuts+2;i++) { + innerverts[i] = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts inner array"); + } + + // first row is e1 last row is e3 + for(i=0;i<numcuts+2;i++) { + innerverts[0][i] = verts[0][(numcuts+1)-i]; + innerverts[numcuts+1][i] = verts[2][(numcuts+1)-i]; + } + + for(i=1;i<=numcuts;i++) { + /* we create a fake edge for the next loop */ + temp.v2 = innerverts[i][0] = verts[1][i]; + temp.v1 = innerverts[i][numcuts+1] = verts[3][i]; + + for(j=1;j<=numcuts;j++) { + float percent= (float)j/(float)(numcuts+1); + + innerverts[i][(numcuts+1)-j]= subdivide_edge_addvert(&temp, rad, beauty, percent); + } + } + // Fill with faces + for(i=0;i<numcuts+1;i++) { + for(j=0;j<numcuts+1;j++) { + hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],innerverts[i+1][j+1],NULL,NULL); + hold->e1->f2 = EDGENEW; + hold->e2->f2 = EDGENEW; + hold->e3->f2 = EDGENEW; + hold->e4->f2 = EDGENEW; + + if(i != 0) { hold->e1->f2 |= EDGEINNER; } + if(j != 0) { hold->e2->f2 |= EDGEINNER; } + if(i != numcuts) { hold->e3->f2 |= EDGEINNER; } + if(j != numcuts) { hold->e4->f2 |= EDGEINNER; } + + facecopy(efa,hold); + } + } + // Clean up our dynamic multi-dim array + for(i=0;i<numcuts+2;i++) { + MEM_freeN(innerverts[i]); + } + MEM_freeN(innerverts); +} + +static void fill_tri_triple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty) +{ + EditVert **verts[3], ***innerverts; + short vertsize, i, j; + EditFace *hold; + EditEdge temp; + + // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] + verts[0] = BLI_ghash_lookup(gh, efa->e1); + verts[1] = BLI_ghash_lookup(gh, efa->e2); + verts[2] = BLI_ghash_lookup(gh, efa->e3); + + //This is the index size of the verts array + vertsize = numcuts+2; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} + if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} + if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);} + /* + We should have something like this now + 3 + + 3 2 1 0 + 0|---*---*---|3 + | / + 1 1* *2 + | / + 2* *1 2 + | / + 3|/ + 0 + + we will fill a 2 dim array of editvert*s to make filling easier + + 3 + + 0 0---1---2---3---4 + | / | / |/ | / + 1 0---1----2---3 + 1 | / | / | / + 2 0----1---2 2 + | / | / + |/ |/ + 3 0---1 + | / + |/ + 4 0 + + */ + + innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array"); + for(i=0;i<numcuts+2;i++) { + innerverts[i] = MEM_mallocN(sizeof(EditVert*)*((numcuts+2)-i),"tri-tri subdiv inner verts inner array"); + } + //top row is e3 backwards + for(i=0;i<numcuts+2;i++) { + innerverts[0][i] = verts[2][(numcuts+1)-i]; + } + + for(i=1;i<=numcuts+1;i++) { + //fake edge, first vert is from e1, last is from e2 + temp.v1= innerverts[i][0] = verts[0][i]; + temp.v2= innerverts[i][(numcuts+1)-i] = verts[1][(numcuts+1)-i]; + + for(j=1;j<(numcuts+1)-i;j++) { + float percent= (float)j/(float)((numcuts+1)-i); + + innerverts[i][((numcuts+1)-i)-j]= subdivide_edge_addvert(&temp, rad, beauty, 1-percent); + } + } + + // Now fill the verts with happy little tris :) + for(i=0;i<=numcuts+1;i++) { + for(j=0;j<(numcuts+1)-i;j++) { + //We always do the first tri + hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],NULL,NULL,NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + if(i != 0) { hold->e1->f2 |= EDGEINNER; } + if(j != 0) { hold->e2->f2 |= EDGEINNER; } + if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;} + + facecopy(efa,hold); + //if there are more to come, we do the 2nd + if(j+1 <= numcuts-i) { + hold = addfacelist(innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL); + facecopy(efa,hold); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + } + } + } + + // Clean up our dynamic multi-dim array + for(i=0;i<numcuts+2;i++) { + MEM_freeN(innerverts[i]); + } + MEM_freeN(innerverts); +} + +//Next two fill types are for knife exact only and are provided to allow for knifing through vertices +//This means there is no multicut! +static void fill_quad_doublevert(EditFace *efa, int v1, int v2) +{ + EditFace *hold; + /* + Depending on which two vertices have been knifed through (v1 and v2), we + triangulate like the patterns below. + X-------| |-------X + | \ | | / | + | \ | | / | + | \ | | / | + --------X X-------- + */ + + if(v1 == 1 && v2 == 3){ + hold= addfacelist(efa->v1, efa->v2, efa->v3, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(efa, hold); + + hold= addfacelist(efa->v1, efa->v3, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(efa, hold); + } + else{ + hold= addfacelist(efa->v1, efa->v2, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e2->f2 |= EDGEINNER; + facecopy(efa, hold); + + hold= addfacelist(efa->v2, efa->v3, efa->v4, 0, efa, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(efa, hold); + } +} + +static void fill_quad_singlevert(EditFace *efa, struct GHash *gh) +{ + EditEdge *cedge=NULL; + EditVert *v[4], **verts; + EditFace *hold; + short start=0, end, left, right, vertsize; + + v[0] = efa->v1; + v[1] = efa->v2; + v[2] = efa->v3; + v[3] = efa->v4; + + if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} + else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} + else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} + else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} + + // Point verts to the array of new verts for cedge + verts = BLI_ghash_lookup(gh, cedge); + //This is the index size of the verts array + vertsize = 3; + + // Is the original v1 the same as the first vert on the selected edge? + // if not, the edge is running the opposite direction in this face so flip + // the array to the correct direction + + if(verts[0] != v[start]) {flipvertarray(verts,3);} + end = (start+1)%4; + left = (start+2)%4; + right = (start+3)%4; + +/* + We should have something like this now + + end start + 2 1 0 + |-----*-----| + | | + | | + | | + ------------- + left right + + where start,end,left, right are indexes of EditFace->v1, etc (stored in v) + and 0,1,2 are the indexes of the new verts stored in verts. We fill like + this, depending on whether its vertex 'left' or vertex 'right' thats + been knifed through... + + |---*---| |---*---| + | / | | \ | + | / | | \ | + |/ | | \| + X-------- --------X +*/ + + if(v[left]->f1) { + //triangle is composed of cutvert, end and left + hold = addfacelist(verts[1],v[end],v[left],NULL, NULL,NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e3->f2 |= EDGEINNER; + facecopy(efa, hold); + + //quad is composed of cutvert, left, right and start + hold = addfacelist(verts[1],v[left],v[right],v[start], NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e4->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(efa, hold); + } + else if(v[right]->f1) { + //triangle is composed of cutvert, right and start + hold = addfacelist(verts[1],v[right],v[start], NULL, NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e1->f2 |= EDGEINNER; + facecopy(efa, hold); + //quad is composed of cutvert, end, left, right + hold = addfacelist(verts[1],v[end], v[left], v[right], NULL, NULL); + hold->e1->f2 |= EDGENEW; + hold->e2->f2 |= EDGENEW; + hold->e3->f2 |= EDGENEW; + hold->e4->f2 |= EDGENEW; + hold->e4->f2 |= EDGEINNER; + facecopy(efa, hold); + } + +} + +// This function takes an example edge, the current point to create and +// the total # of points to create, then creates the point and return the +// editvert pointer to it. +static EditVert *subdivideedgenum(EditEdge *edge, int curpoint, int totpoint, float rad, int beauty) +{ + EditVert *ev; + float percent; + + if (beauty & (B_PERCENTSUBD) && totpoint == 1) + //percent=(float)(edge->tmp.l)/32768.0f; + percent= edge->tmp.fp; + else + percent= (float)curpoint/(float)(totpoint+1); + + ev= subdivide_edge_addvert(edge, rad, beauty, percent); + ev->f = edge->v1->f; + + return ev; +} + +void esubdivideflag(int flag, float rad, int beauty, int numcuts, int seltype) +{ + EditMesh *em = G.editMesh; + EditFace *ef; + EditEdge *eed, *cedge, *sort[4]; + EditVert *eve, **templist; + struct GHash *gh; + float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3]; + int i, j, edgecount, touchcount, facetype,hold; + ModifierData *md= G.obedit->modifiers.first; + + if(multires_test()) return; + + //Set faces f1 to 0 cause we need it later + for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0; + for(eve=em->verts.first; eve; eve=eve->next) { + if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */ + eve->f1 = 0; + eve->f2 = 0; + } + + for (; md; md=md->next) { + if ((md->type==eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + for (eve= em->verts.first; eve; eve= eve->next) { + eve->f2= 0; + switch(mmd->axis) { + case 0: + if (fabs(eve->co[0]) < mmd->tolerance) + eve->f2 |= 1; + break; + case 1: + if (fabs(eve->co[1]) < mmd->tolerance) + eve->f2 |= 2; + break; + case 2: + if (fabs(eve->co[2]) < mmd->tolerance) + eve->f2 |= 4; + break; + } + } + } + } + } + + //Flush vertex flags upward to the edges + for(eed = em->edges.first;eed;eed = eed->next) { + //if(eed->f & flag && eed->v1->f == eed->v2->f) { + // eed->f |= eed->v1->f; + // } + eed->f2 = 0; + if(eed->f & flag) { + eed->f2 |= EDGEOLD; + } + } + + // We store an array of verts for each edge that is subdivided, + // we put this array as a value in a ghash which is keyed by the EditEdge* + + // Now for beauty subdivide deselect edges based on length + if(beauty & B_BEAUTY) { + for(ef = em->faces.first;ef;ef = ef->next) { + if(!ef->v4) { + continue; + } + if(ef->f & SELECT) { + copy_v3_v3(v1mat, ef->v1->co); + copy_v3_v3(v2mat, ef->v2->co); + copy_v3_v3(v3mat, ef->v3->co); + copy_v3_v3(v4mat, ef->v4->co); + mul_mat3_m4_v3(G.obedit->obmat, v1mat); + mul_mat3_m4_v3(G.obedit->obmat, v2mat); + mul_mat3_m4_v3(G.obedit->obmat, v3mat); + mul_mat3_m4_v3(G.obedit->obmat, v4mat); + + length[0] = len_v3v3(v1mat, v2mat); + length[1] = len_v3v3(v2mat, v3mat); + length[2] = len_v3v3(v3mat, v4mat); + length[3] = len_v3v3(v4mat, v1mat); + sort[0] = ef->e1; + sort[1] = ef->e2; + sort[2] = ef->e3; + sort[3] = ef->e4; + + + // Beauty Short Edges + if(beauty & B_BEAUTY_SHORT) { + for(j=0;j<2;j++) { + hold = -1; + for(i=0;i<4;i++) { + if(length[i] < 0) { + continue; + } else if(hold == -1) { + hold = i; + } else { + if(length[hold] < length[i]) { + hold = i; + } + } + } + sort[hold]->f &= ~SELECT; + sort[hold]->f2 |= EDGENEW; + length[hold] = -1; + } + } + + // Beauty Long Edges + else { + for(j=0;j<2;j++) { + hold = -1; + for(i=0;i<4;i++) { + if(length[i] < 0) { + continue; + } else if(hold == -1) { + hold = i; + } else { + if(length[hold] > length[i]) { + hold = i; + } + } + } + sort[hold]->f &= ~SELECT; + sort[hold]->f2 |= EDGENEW; + length[hold] = -1; + } + } + } + } + } + + gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + // If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut + if(beauty & B_KNIFE) { + for(eed= em->edges.first;eed;eed=eed->next) { + if( eed->tmp.fp == 0 ) { + EM_select_edge(eed,0); + } + } + } + // So for each edge, if it is selected, we allocate an array of size cuts+2 + // so we can have a place for the v1, the new verts and v2 + for(eed=em->edges.first;eed;eed = eed->next) { + if(eed->f & flag) { + templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist"); + templist[0] = eed->v1; + for(i=0;i<numcuts;i++) { + // This function creates the new vert and returns it back + // to the array + templist[i+1] = subdivideedgenum(eed, i+1, numcuts, rad, beauty); + //while we are here, we can copy edge info from the original edge + cedge = addedgelist(templist[i],templist[i+1],eed); + // Also set the edge f2 to EDGENEW so that we can use this info later + cedge->f2 = EDGENEW; + } + templist[i+1] = eed->v2; + //Do the last edge too + cedge = addedgelist(templist[i],templist[i+1],eed); + cedge->f2 = EDGENEW; + // Now that the edge is subdivided, we can put its verts in the ghash + BLI_ghash_insert(gh, eed, templist); + } + } + + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + // Now for each face in the mesh we need to figure out How many edges were cut + // and which filling method to use for that face + for(ef = em->faces.first;ef;ef = ef->next) { + edgecount = 0; + facetype = 3; + if(ef->e1->f & flag) {edgecount++;} + if(ef->e2->f & flag) {edgecount++;} + if(ef->e3->f & flag) {edgecount++;} + if(ef->v4) { + facetype = 4; + if(ef->e4->f & flag) {edgecount++;} + } + if(facetype == 4) { + switch(edgecount) { + case 0: + if(beauty & B_KNIFE && numcuts == 1){ + /*Test for when knifing through two opposite verts but no edges*/ + touchcount = 0; + if(ef->v1->f1) touchcount++; + if(ef->v2->f1) touchcount++; + if(ef->v3->f1) touchcount++; + if(ef->v4->f1) touchcount++; + if(touchcount == 2){ + if(ef->v1->f1 && ef->v3->f1){ + ef->f1 = SELECT; + fill_quad_doublevert(ef, 1, 3); + } + else if(ef->v2->f1 && ef->v4->f1){ + ef->f1 = SELECT; + fill_quad_doublevert(ef, 2, 4); + } + } + } + break; + + case 1: + if(beauty & B_KNIFE && numcuts == 1){ + /*Test for when knifing through an edge and one vert*/ + touchcount = 0; + if(ef->v1->f1) touchcount++; + if(ef->v2->f1) touchcount++; + if(ef->v3->f1) touchcount++; + if(ef->v4->f1) touchcount++; + + if(touchcount == 1){ + if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) || + (ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) || + (ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) || + (ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){ + + ef->f1 = SELECT; + fill_quad_singlevert(ef, gh); + } + else{ + ef->f1 = SELECT; + fill_quad_single(ef, gh, numcuts, seltype); + } + } + else{ + ef->f1 = SELECT; + fill_quad_single(ef, gh, numcuts, seltype); + } + } + else{ + ef->f1 = SELECT; + fill_quad_single(ef, gh, numcuts, seltype); + } + break; + case 2: ef->f1 = SELECT; + // if there are 2, we check if edge 1 and 3 are either both on or off that way + // we can tell if the selected pair is Adjacent or Opposite of each other + if((ef->e1->f & flag && ef->e3->f & flag) || + (ef->e2->f & flag && ef->e4->f & flag)) { + fill_quad_double_op(ef, gh, numcuts); + }else{ + switch(G.scene->toolsettings->cornertype) { + case 0: fill_quad_double_adj_path(ef, gh, numcuts); break; + case 1: fill_quad_double_adj_inner(ef, gh, numcuts); break; + case 2: fill_quad_double_adj_fan(ef, gh, numcuts); break; + } + + } + break; + case 3: ef->f1 = SELECT; + fill_quad_triple(ef, gh, numcuts); + break; + case 4: ef->f1 = SELECT; + fill_quad_quadruple(ef, gh, numcuts, rad, beauty); + break; + } + } else { + switch(edgecount) { + case 0: break; + case 1: ef->f1 = SELECT; + fill_tri_single(ef, gh, numcuts, seltype); + break; + case 2: ef->f1 = SELECT; + fill_tri_double(ef, gh, numcuts); + break; + case 3: ef->f1 = SELECT; + fill_tri_triple(ef, gh, numcuts, rad, beauty); + break; + } + } + } + + // Delete Old Edges and Faces + for(eed = em->edges.first;eed;eed = eed->next) { + if(BLI_ghash_haskey(gh,eed)) { + eed->f1 = SELECT; + } else { + eed->f1 = 0; + } + } + free_tagged_edges_faces(em->edges.first, em->faces.first); + + if(seltype == SUBDIV_SELECT_ORIG && G.qual != LR_CTRLKEY) { + /* bugfix: vertex could get flagged as "not-selected" + // solution: clear flags before, not at the same time as setting SELECT flag -dg + */ + for(eed = em->edges.first;eed;eed = eed->next) { + if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) { + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) { + eed->f |= flag; + EM_select_edge(eed,1); + } + } + } else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| G.qual == LR_CTRLKEY) { + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & EDGEINNER) { + eed->f |= flag; + EM_select_edge(eed,1); + if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT; + if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT; + }else{ + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + } else if(seltype == SUBDIV_SELECT_LOOPCUT){ + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f2 & DOUBLEOPFILL){ + eed->f |= flag; + EM_select_edge(eed,1); + }else{ + eed->f &= !flag; + EM_select_edge(eed,0); + } + } + } + if(G.scene->selectmode & SCE_SELECT_VERTEX) { + for(eed = em->edges.first;eed;eed = eed->next) { + if(eed->f & SELECT) { + eed->v1->f |= SELECT; + eed->v2->f |= SELECT; + } + } + } + + //fix hide flags for edges. First pass, hide edges of hidden faces + for(ef=em->faces.first; ef; ef=ef->next){ + if(ef->h){ + ef->e1->h |= 1; + ef->e2->h |= 1; + ef->e3->h |= 1; + if(ef->e4) ef->e4->h |= 1; + } + } + //second pass: unhide edges of visible faces adjacent to hidden faces + for(ef=em->faces.first; ef; ef=ef->next){ + if(ef->h == 0){ + ef->e1->h &= ~1; + ef->e2->h &= ~1; + ef->e3->h &= ~1; + if(ef->e4) ef->e4->h &= ~1; + } + } + + // Free the ghash and call MEM_freeN on all the value entries to return + // that memory + BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN); + + EM_selectmode_flush(); + for(ef=em->faces.first;ef;ef = ef->next) { + if(ef->e4) { + if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && + (ef->e3->f & SELECT && ef->e4->f & SELECT) ) { + ef->f |= SELECT; + } + } else { + if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) { + ef->f |= SELECT; + } + } + } + + recalc_editnormals(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +} + +static int count_selected_edges(EditEdge *ed) +{ + int totedge = 0; + while(ed) { + ed->tmp.p = 0; + if( ed->f & SELECT ) totedge++; + ed= ed->next; + } + return totedge; +} + +/* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */ +typedef EditFace *EVPtr; +typedef EVPtr EVPTuple[2]; + +/** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces) + sharing one edge. + arguments: selected edge list, face list. + Edges will also be tagged accordingly (see eed->f2) */ + +static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa) +{ + EditEdge *e1, *e2, *e3; + EVPtr *evp; + int i = 0; + + /* run through edges, if selected, set pointer edge-> facearray */ + while(eed) { + eed->f2= 0; + eed->f1= 0; + if( eed->f & SELECT ) { + eed->tmp.p = (EditVert *) (&efaa[i]); + i++; + } + else eed->tmp.p = NULL; + + eed= eed->next; + } + + + /* find edges pointing to 2 faces by procedure: + + - run through faces and their edges, increase + face counter e->f1 for each face + */ + + while(efa) { + efa->f1= 0; + if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */ + e1= efa->e1; + e2= efa->e2; + e3= efa->e3; + if(e1->f2<3 && e1->tmp.p) { + if(e1->f2<2) { + evp= (EVPtr *) e1->tmp.p; + evp[(int)e1->f2] = efa; + } + e1->f2+= 1; + } + if(e2->f2<3 && e2->tmp.p) { + if(e2->f2<2) { + evp= (EVPtr *) e2->tmp.p; + evp[(int)e2->f2]= efa; + } + e2->f2+= 1; + } + if(e3->f2<3 && e3->tmp.p) { + if(e3->f2<2) { + evp= (EVPtr *) e3->tmp.p; + evp[(int)e3->f2]= efa; + } + e3->f2+= 1; + } + } + else { + /* set to 3 to make sure these are not flipped or joined */ + efa->e1->f2= 3; + efa->e2->f2= 3; + efa->e3->f2= 3; + if (efa->e4) efa->e4->f2= 3; + } + + efa= efa->next; + } + return i; +} + + +/* returns vertices of two adjacent triangles forming a quad + - can be righthand or lefthand + + 4-----3 + |\ | + | \ 2 | <- efa1 + | \ | + efa-> | 1 \ | + | \| + 1-----2 + +*/ +#define VTEST(face, num, other) \ + (face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3) + +static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex) +{ + if VTEST(efa, 1, efa1) { + *v1= efa->v1; + *v2= efa->v2; + vindex[0]= 0; + vindex[1]= 1; + } + else if VTEST(efa, 2, efa1) { + *v1= efa->v2; + *v2= efa->v3; + vindex[0]= 1; + vindex[1]= 2; + } + else if VTEST(efa, 3, efa1) { + *v1= efa->v3; + *v2= efa->v1; + vindex[0]= 2; + vindex[1]= 0; + } + + if VTEST(efa1, 1, efa) { + *v3= efa1->v1; + *v4= efa1->v2; + vindex[2]= 0; + vindex[3]= 1; + } + else if VTEST(efa1, 2, efa) { + *v3= efa1->v2; + *v4= efa1->v3; + vindex[2]= 1; + vindex[3]= 2; + } + else if VTEST(efa1, 3, efa) { + *v3= efa1->v3; + *v4= efa1->v1; + vindex[2]= 2; + vindex[3]= 0; + } + else + *v3= *v4= NULL; +} + +/* Helper functions for edge/quad edit features*/ +static void untag_edges(EditFace *f) +{ + f->e1->f1 = 0; + f->e2->f1 = 0; + f->e3->f1 = 0; + if (f->e4) f->e4->f1 = 0; +} + +/** remove and free list of tagged edges and faces */ +static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa) +{ + EditMesh *em= G.editMesh; + EditEdge *nexted; + EditFace *nextvl; + + while(efa) { + nextvl= efa->next; + if(efa->f1) { + BLI_remlink(&em->faces, efa); + free_editface(efa); + } + else + /* avoid deleting edges that are still in use */ + untag_edges(efa); + efa= nextvl; + } + + while(eed) { + nexted= eed->next; + if(eed->f1) { + remedge(eed); + free_editedge(eed); + } + eed= nexted; + } +} + +/* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the + edge/face flags, with very mixed results.... */ +void beauty_fill(void) +{ + EditMesh *em = G.editMesh; + EditVert *v1, *v2, *v3, *v4; + EditEdge *eed, *nexted; + EditEdge dia1, dia2; + EditFace *efa, *w; + // void **efaar, **efaa; + EVPTuple *efaar; + EVPtr *efaa; + float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2; + int totedge, ok, notbeauty=8, onedone, vindex[4]; + + if(multires_test()) return; + + /* - all selected edges with two faces + * - find the faces: store them in edges (using datablock) + * - per edge: - test convex + * - test edge: flip? + * - if true: remedge, addedge, all edges at the edge get new face pointers + */ + + EM_selectmode_set(); // makes sure in selectmode 'face' the edges of selected faces are selected too + + totedge = count_selected_edges(em->edges.first); + if(totedge==0) return; + + /* temp block with face pointers */ + efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill"); + + while (notbeauty) { + notbeauty--; + + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + + /* there we go */ + onedone= 0; + + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + /* f2 is set in collect_quadedges() */ + if(eed->f2==2 && eed->h==0) { + + efaa = (EVPtr *) eed->tmp.p; + + /* none of the faces should be treated before, nor be part of fgon */ + ok= 1; + efa= efaa[0]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + if(efa->fgonf) ok= 0; + efa= efaa[1]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + if(efa->fgonf) ok= 0; + + if(ok) { + /* test convex */ + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if(v1 && v2 && v3 && v4) { + if( convex(v1->co, v2->co, v3->co, v4->co) ) { + + /* test edges */ + if( (v1) > (v3) ) { + dia1.v1= v3; + dia1.v2= v1; + } + else { + dia1.v1= v1; + dia1.v2= v3; + } + + if( (v2) > (v4) ) { + dia2.v1= v4; + dia2.v2= v2; + } + else { + dia2.v1= v2; + dia2.v2= v4; + } + + /* testing rule: + * the area divided by the total edge lengths + */ + + len1= len_v3v3(v1->co, v2->co); + len2= len_v3v3(v2->co, v3->co); + len3= len_v3v3(v3->co, v4->co); + len4= len_v3v3(v4->co, v1->co); + len5= len_v3v3(v1->co, v3->co); + len6= len_v3v3(v2->co, v4->co); + + opp1= area_tri_v3(v1->co, v2->co, v3->co); + opp2= area_tri_v3(v1->co, v3->co, v4->co); + + fac1= opp1/(len1+len2+len5) + opp2/(len3+len4+len5); + + opp1= area_tri_v3(v2->co, v3->co, v4->co); + opp2= area_tri_v3(v2->co, v4->co, v1->co); + + fac2= opp1/(len2+len3+len6) + opp2/(len4+len1+len6); + + ok= 0; + if(fac1 > fac2) { + if(dia2.v1==eed->v1 && dia2.v2==eed->v2) { + eed->f1= 1; + efa= efaa[0]; + efa->f1= 1; + efa= efaa[1]; + efa->f1= 1; + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[0], vindex[1], 4+vindex[2], -1); + w->f |= SELECT; + + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[0], 4+vindex[2], 4+vindex[3], -1); + w->f |= SELECT; + + onedone= 1; + } + } + else if(fac1 < fac2) { + if(dia1.v1==eed->v1 && dia1.v2==eed->v2) { + eed->f1= 1; + efa= efaa[0]; + efa->f1= 1; + efa= efaa[1]; + efa->f1= 1; + + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[1], 4+vindex[2], 4+vindex[3], -1); + w->f |= SELECT; + + + w= EM_face_from_faces(efaa[0], efaa[1], + vindex[0], 4+vindex[1], 4+vindex[3], -1); + w->f |= SELECT; + + onedone= 1; + } + } + } + } + } + + } + eed= nexted; + } + + free_tagged_edges_faces(em->edges.first, em->faces.first); + + if(onedone==0) break; + + EM_selectmode_set(); // new edges/faces were added + } + + MEM_freeN(efaar); + + EM_select_flush(); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Beauty Fill"); +} + + +/* ******************** BEGIN TRIANGLE TO QUAD ************************************* */ +static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit) +{ + + /*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/ + /*Note: this is more complicated than it needs to be and should be cleaned up...*/ + float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff, + edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff, + minarea, maxarea, areaA, areaB; + + /*First Test: Normal difference*/ + normal_tri_v3( noA1,v1->co, v2->co, v3->co); + normal_tri_v3( noA2,v1->co, v3->co, v4->co); + + if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0; + else normalADiff = angle_v2v2(noA1, noA2); + //if(!normalADiff) normalADiff = 179; + normal_tri_v3( noB1,v2->co, v3->co, v4->co); + normal_tri_v3( noB2,v4->co, v1->co, v2->co); + + if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0; + else normalBDiff = angle_v2v2(noB1, noB2); + //if(!normalBDiff) normalBDiff = 179; + + measure += (normalADiff/360) + (normalBDiff/360); + if(measure > limit) return measure; + + /*Second test: Colinearity*/ + sub_v3_v3v3(edgeVec1, v1->co, v2->co); + sub_v3_v3v3(edgeVec2, v2->co, v3->co); + sub_v3_v3v3(edgeVec3, v3->co, v4->co); + sub_v3_v3v3(edgeVec4, v4->co, v1->co); + + diff = 0.0; + + diff = ( + fabs(angle_v2v2(edgeVec1, edgeVec2) - 90) + + fabs(angle_v2v2(edgeVec2, edgeVec3) - 90) + + fabs(angle_v2v2(edgeVec3, edgeVec4) - 90) + + fabs(angle_v2v2(edgeVec4, edgeVec1) - 90)) / 360; + if(!diff) return 0.0; + + measure += diff; + if(measure > limit) return measure; + + /*Third test: Concavity*/ + areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co); + areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co); + + if(areaA <= areaB) minarea = areaA; + else minarea = areaB; + + if(areaA >= areaB) maxarea = areaA; + else maxarea = areaB; + + if(!maxarea) measure += 1; + else measure += (1 - (minarea / maxarea)); + + return measure; +} + +#define T2QUV_LIMIT 0.005 +#define T2QCOL_LIMIT 3 +static int compareFaceAttribs(EditFace *f1, EditFace *f2, EditEdge *eed) +{ + /*Test to see if the per-face attributes for the joining edge match within limit*/ + MTFace *tf1, *tf2; + unsigned int *col1, *col2; + short i,attrok=0, flag = G.scene->toolsettings->editbutflag, fe1[2], fe2[2]; + + tf1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MTFACE); + tf2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MTFACE); + + col1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MCOL); + col2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MCOL); + + /*store indices for faceedges*/ + f1->v1->f1 = 0; + f1->v2->f1 = 1; + f1->v3->f1 = 2; + + fe1[0] = eed->v1->f1; + fe1[1] = eed->v2->f1; + + f2->v1->f1 = 0; + f2->v2->f1 = 1; + f2->v3->f1 = 2; + + fe2[0] = eed->v1->f1; + fe2[1] = eed->v2->f1; + + /*compare faceedges for each face attribute. Additional per face attributes can be added later*/ + /*do UVs*/ + if(flag & B_JOINTRIA_UV){ + + if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV; + else if(tf1->tpage != tf2->tpage); /*do nothing*/ + else{ + for(i = 0; i < 2; i++){ + if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] && + tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV; + } + } + } + + /*do VCOLs*/ + if(flag & B_JOINTRIA_VCOL){ + if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL; + else{ + char *f1vcol, *f2vcol; + for(i = 0; i < 2; i++){ + f1vcol = (char *)&(col1[fe1[i]]); + f2vcol = (char *)&(col2[fe2[i]]); + + /*compare f1vcol with f2vcol*/ + if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] && + f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] && + f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL; + } + } + } + + if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1; + return 0; +} + +static int fplcmp(const void *v1, const void *v2) +{ + const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2); + + if( e1->crease > e2->crease) return 1; + else if( e1->crease < e2->crease) return -1; + + return 0; +} + +/*Bitflags for edges.*/ +#define T2QDELETE 1 +#define T2QCOMPLEX 2 +#define T2QJOIN 4 +void join_triangles(void) +{ + EditMesh *em=G.editMesh; + EditVert *v1, *v2, *v3, *v4, *eve; + EditEdge *eed, **edsortblock = NULL, **edb = NULL; + EditFace *efa; + EVPTuple *efaar = NULL; + EVPtr *efaa = NULL; + float *creases = NULL; + float measure; /*Used to set tolerance*/ + float limit = G.scene->toolsettings->jointrilimit; + int i, ok, totedge=0, totseledge=0, complexedges, vindex[4]; + + /*test for multi-resolution data*/ + if(multires_test()) return; + + /*if we take a long time on very dense meshes we want waitcursor to display*/ + waitcursor(1); + + totseledge = count_selected_edges(em->edges.first); + if(totseledge==0) return; + + /*abusing crease value to store weights for edge pairs. Nasty*/ + for(eed=em->edges.first; eed; eed=eed->next) totedge++; + if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array"); + for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){ + creases[i] = eed->crease; + eed->crease = 0.0; + } + + /*clear temp flags*/ + for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0; + for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0; + for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0; + + /*For every selected 2 manifold edge, create pointers to its two faces.*/ + efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad"); + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + complexedges = 0; + + if(ok){ + + + /*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/ + for(eed=em->edges.first; eed; eed=eed->next){ + /* eed->f2 is 2 only if this edge is part of exactly two + triangles, and both are selected, and it has EVPTuple assigned */ + if(eed->f2 == 2){ + efaa= (EVPtr *) eed->tmp.p; + efaa[0]->tmp.l++; + efaa[1]->tmp.l++; + } + } + + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f2 == 2){ + efaa= (EVPtr *) eed->tmp.p; + v1 = v2 = v3 = v4 = NULL; + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if(v1 && v2 && v3 && v4){ + /*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/ + if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){ + if( convex(v1->co, v2->co, v3->co, v4->co) ){ + eed->f1 |= T2QJOIN; + efaa[0]->f1 = 1; //mark for join + efaa[1]->f1 = 1; //mark for join + } + } + else{ + + /* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved. + Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria: + + 1: the two faces do not share the same material + 2: the edge joining the two faces is marked as sharp. + 3: the two faces UV's do not make a good match + 4: the two faces Vertex colors do not make a good match + + If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function. + This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user + the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the + same faces in the current pair later. + + This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of + the python scripts bundled with Blender releases. + */ + + if(G.scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/ + else if(G.scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/ + else if(((G.scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (G.scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) && + compareFaceAttribs(efaa[0], efaa[1], eed) == 0); /*do nothing*/ + else{ + measure = measure_facepair(v1, v2, v3, v4, limit); + if(measure < limit){ + complexedges++; + eed->f1 |= T2QCOMPLEX; + eed->crease = measure; /*we dont mark edges for join yet*/ + } + } + } + } + } + } + + /*Quicksort the complex edges according to their weighting*/ + if(complexedges){ + edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array"); + for(eed = em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QCOMPLEX){ + *edb = eed; + edb++; + } + } + qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp); + /*now go through and mark the edges who get the highest weighting*/ + for(edb=edsortblock, i=0; i < complexedges; edb++, i++){ + efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/ + if( !efaa[0]->f1 && !efaa[1]->f1){ + efaa[0]->f1 = 1; //mark for join + efaa[1]->f1 = 1; //mark for join + (*edb)->f1 |= T2QJOIN; + } + } + } + + /*finally go through all edges marked for join (simple and complex) and create new faces*/ + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QJOIN){ + efaa= (EVPtr *)eed->tmp.p; + v1 = v2 = v3 = v4 = NULL; + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + if((v1 && v2 && v3 && v4) && (exist_face(v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be adressed.*/ + /*flag for delete*/ + eed->f1 |= T2QDELETE; + /*create new quad and select*/ + efa = EM_face_from_faces(efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]); + EM_select_face(efa,1); + } + else{ + efaa[0]->f1 = 0; + efaa[1]->f1 = 0; + } + } + } + } + + /*free data and cleanup*/ + if(creases){ + for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i]; + MEM_freeN(creases); + } + for(eed=em->edges.first; eed; eed=eed->next){ + if(eed->f1 & T2QDELETE) eed->f1 = 1; + else eed->f1 = 0; + } + free_tagged_edges_faces(em->edges.first, em->faces.first); + if(efaar) MEM_freeN(efaar); + if(edsortblock) MEM_freeN(edsortblock); + + EM_selectmode_flush(); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + #ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + #endif + waitcursor(0); + BIF_undo_push("Convert Triangles to Quads"); +} +/* ******************** END TRIANGLE TO QUAD ************************************* */ + +#define FACE_MARKCLEAR(f) (f->f1 = 1) + +/* quick hack, basically a copy of beauty_fill */ +void edge_flip(void) +{ + EditMesh *em = G.editMesh; + EditVert *v1, *v2, *v3, *v4; + EditEdge *eed, *nexted; + EditFace *efa, *w; + //void **efaar, **efaa; + EVPTuple *efaar; + EVPtr *efaa; + int totedge, ok, vindex[4]; + + /* - all selected edges with two faces + * - find the faces: store them in edges (using datablock) + * - per edge: - test convex + * - test edge: flip? + - if true: remedge, addedge, all edges at the edge get new face pointers + */ + + EM_selectmode_flush(); // makes sure in selectmode 'face' the edges of selected faces are selected too + + totedge = count_selected_edges(em->edges.first); + if(totedge==0) return; + + /* temporary array for : edge -> face[1], face[2] */ + efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip"); + + ok = collect_quadedges(efaar, em->edges.first, em->faces.first); + + eed= em->edges.first; + while(eed) { + nexted= eed->next; + + if(eed->f2==2) { /* points to 2 faces */ + + efaa= (EVPtr *) eed->tmp.p; + + /* don't do it if flagged */ + + ok= 1; + efa= efaa[0]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + efa= efaa[1]; + if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; + + if(ok) { + /* test convex */ + givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); + +/* + 4-----3 4-----3 + |\ | | /| + | \ 1 | | 1 / | + | \ | -> | / | + | 0 \ | | / 0 | + | \| |/ | + 1-----2 1-----2 +*/ + /* make new faces */ + if (v1 && v2 && v3) { + if( convex(v1->co, v2->co, v3->co, v4->co) ) { + if(exist_face(v1, v2, v3, v4)==0) { + /* outch this may break seams */ + w= EM_face_from_faces(efaa[0], efaa[1], vindex[0], + vindex[1], 4+vindex[2], -1); + + EM_select_face(w, 1); + + /* outch this may break seams */ + w= EM_face_from_faces(efaa[0], efaa[1], vindex[0], + 4+vindex[2], 4+vindex[3], -1); + + EM_select_face(w, 1); + } + /* tag as to-be-removed */ + FACE_MARKCLEAR(efaa[1]); + FACE_MARKCLEAR(efaa[0]); + eed->f1 = 1; + + } /* endif test convex */ + } + } + } + eed= nexted; + } + + /* clear tagged edges and faces: */ + free_tagged_edges_faces(em->edges.first, em->faces.first); + + MEM_freeN(efaar); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + BIF_undo_push("Flip Triangle Edges"); + +} + +static void edge_rotate(EditEdge *eed,int dir) +{ + EditMesh *em = G.editMesh; + EditVert **verts[2]; + EditFace *face[2], *efa, *newFace[2]; + EditEdge **edges[2], **hiddenedges, *srchedge; + int facecount, p1, p2, p3, p4, fac1, fac2, i, j; + int numhidden, numshared, p[2][4]; + + /* check to make sure that the edge is only part of 2 faces */ + facecount = 0; + for(efa = em->faces.first;efa;efa = efa->next) { + if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) { + if(facecount >= 2) { + /* more than two faces with this edge */ + return; + } + else { + face[facecount] = efa; + facecount++; + } + } + } + + if(facecount < 2) + return; + + /* how many edges does each face have */ + if(face[0]->e4) fac1= 4; + else fac1= 3; + + if(face[1]->e4) fac2= 4; + else fac2= 3; + + /* make a handy array for verts and edges */ + verts[0]= &face[0]->v1; + edges[0]= &face[0]->e1; + verts[1]= &face[1]->v1; + edges[1]= &face[1]->e1; + + /* we don't want to rotate edges between faces that share more than one edge */ + numshared= 0; + for(i=0; i<fac1; i++) + for(j=0; j<fac2; j++) + if (edges[0][i] == edges[1][j]) + numshared++; + + if(numshared > 1) + return; + + /* coplaner faces only please */ + if(dot_v3v3(face[0]->n,face[1]->n) <= 0.000001) + return; + + /* we want to construct an array of vertex indicis in both faces, starting at + the last vertex of the edge being rotated. + - first we find the two vertices that lie on the rotating edge + - then we make sure they are ordered according to the face vertex order + - and then we construct the array */ + p1= p2= p3= p4= 0; + + for(i=0; i<4; i++) { + if(eed->v1 == verts[0][i]) p1 = i; + if(eed->v2 == verts[0][i]) p2 = i; + if(eed->v1 == verts[1][i]) p3 = i; + if(eed->v2 == verts[1][i]) p4 = i; + } + + if((p1+1)%fac1 == p2) + SWAP(int, p1, p2); + if((p3+1)%fac2 == p4) + SWAP(int, p3, p4); + + for (i = 0; i < 4; i++) { + p[0][i]= (p1 + i)%fac1; + p[1][i]= (p3 + i)%fac2; + } + + /* create an Array of the Edges who have h set prior to rotate */ + numhidden = 0; + for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next) + if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT))) + numhidden++; + + hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts"); + if(!hiddenedges) { + error("Malloc Was not happy!"); + return; + } + + numhidden = 0; + for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) + if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT)) + hiddenedges[numhidden++] = srchedge; + + /* create the 2 new faces */ + if(fac1 == 3 && fac2 == 3) { + /* no need of reverse setup */ + + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); + } + else if(fac1 == 4 && fac2 == 3) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]); + newFace[1]= EM_face_from_faces(face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1); + + verts[0][p[0][2]]->f |= SELECT; + verts[1][p[1][1]]->f |= SELECT; + } + } + else if(fac1 == 3 && fac2 == 4) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]); + + verts[0][p[0][1]]->f |= SELECT; + verts[1][p[1][2]]->f |= SELECT; + } + + } + else if(fac1 == 4 && fac2 == 4) { + if(dir == 1) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); + } else if (dir == 2) { + newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]); + newFace[1]= EM_face_from_faces(face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]); + + verts[0][p[0][2]]->f |= SELECT; + verts[1][p[1][2]]->f |= SELECT; + } + } + else + return; /* This should never happen */ + + if(dir == 1 || (fac1 == 3 && fac2 == 3)) { + verts[0][p[0][1]]->f |= SELECT; + verts[1][p[1][1]]->f |= SELECT; + } + + /* copy old edge's flags to new center edge*/ + for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) { + if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) { + srchedge->f = eed->f; + srchedge->h = eed->h; + srchedge->dir = eed->dir; + srchedge->seam = eed->seam; + srchedge->crease = eed->crease; + srchedge->bweight = eed->bweight; + } + } + + /* resetting hidden flag */ + for(numhidden--; numhidden>=0; numhidden--) + hiddenedges[numhidden]->h= 1; + + /* check for orhphan edges */ + for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) + srchedge->f1= -1; + + /* cleanup */ + MEM_freeN(hiddenedges); + + /* get rid of the old edge and faces*/ + remedge(eed); + free_editedge(eed); + BLI_remlink(&em->faces, face[0]); + free_editface(face[0]); + BLI_remlink(&em->faces, face[1]); + free_editface(face[1]); +} + +/* only accepts 1 selected edge, or 2 selected faces */ +void edge_rotate_selected(int dir) +{ + EditEdge *eed; + EditFace *efa; + short edgeCount = 0; + + /*clear new flag for new edges, count selected edges */ + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + eed->f1= 0; + eed->f2 &= ~2; + if(eed->f & SELECT) edgeCount++; + } + + if(edgeCount>1) { + /* more selected edges, check faces */ + for(efa= G.editMesh->faces.first; efa; efa= efa->next) { + if(efa->f & SELECT) { + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + } + edgeCount= 0; + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + if(eed->f1==2) edgeCount++; + } + if(edgeCount==1) { + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + if(eed->f1==2) { + edge_rotate(eed,dir); + break; + } + } + } + else error("Select one edge or two adjacent faces"); + } + else if(edgeCount==1) { + for(eed= G.editMesh->edges.first; eed; eed= eed->next) { + if(eed->f & SELECT) { + EM_select_edge(eed, 0); + edge_rotate(eed,dir); + break; + } + } + } + else error("Select one edge or two adjacent faces"); + + + /* flush selected vertices (again) to edges/faces */ + EM_select_flush(); + + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + +#ifdef WITH_VERSE + if(G.editMesh->vnode) + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); +#endif + + BIF_undo_push("Rotate Edge"); +} + +/******************* BEVEL CODE STARTS HERE ********************/ + +static void bevel_displace_vec(float *midvec, float *v1, float *v2, float *v3, float d, float no[3]) +{ + float a[3], c[3], n_a[3], n_c[3], mid[3], ac, ac2, fac; + + sub_v3_v3v3(a, v1, v2); + sub_v3_v3v3(c, v3, v2); + + cross_v3_v3v3(n_a, a, no); + normalize_v3(n_a); + cross_v3_v3v3(n_c, no, c); + normalize_v3(n_c); + + normalize_v3(a); + normalize_v3(c); + ac = dot_v3v3(a, c); + + if (ac == 1 || ac == -1) { + midvec[0] = midvec[1] = midvec[2] = 0; + return; + } + ac2 = ac * ac; + fac = (float)sqrt((ac2 + 2*ac + 1)/(1 - ac2) + 1); + add_v3_v3v3(mid, n_c, n_a); + normalize_v3(mid); + mul_v3_fl(mid, d * fac); + add_v3_v3v3(mid, mid, v2); + copy_v3_v3(midvec, mid); +} + +/* Finds the new point using the sinus law to extrapolate a triangle + Lots of sqrts which would not be good for a real time algo + Using the mid point of the extrapolation of both sides + Useless for coplanar quads, but that doesn't happen too often */ +static void fix_bevel_wrap(float *midvec, float *v1, float *v2, float *v3, float *v4, float d, float no[3]) +{ + float a[3], b[3], c[3], l_a, l_b, l_c, s_a, s_b, s_c, Pos1[3], Pos2[3], Dir[3]; + + sub_v3_v3v3(a, v3, v2); + l_a = normalize_v3(a); + sub_v3_v3v3(b, v4, v3); + normalize_v3(b); + sub_v3_v3v3(c, v1, v2); + normalize_v3(c); + + s_b = dot_v3v3(a, c); + s_b = (float)sqrt(1 - (s_b * s_b)); + s_a = dot_v3v3(b, c); + s_a = (float)sqrt(1 - (s_a * s_a)); + mul_v3_fl(a, -1); + s_c = dot_v3v3(a, b); + s_c = (float)sqrt(1 - (s_c * s_c)); + + l_b = s_b * l_a / s_a; + l_c = s_c * l_a / s_a; + + mul_v3_fl(b, l_b); + mul_v3_fl(c, l_c); + + add_v3_v3v3(Pos1, v2, c); + add_v3_v3v3(Pos2, v3, b); + + add_v3_v3v3(Dir, Pos1, Pos2); + mul_v3_fl(Dir, 0.5); + + bevel_displace_vec(midvec, v3, Dir, v2, d, no); + +} + + +static char detect_wrap(float *o_v1, float *o_v2, float *v1, float *v2, float *no) +{ + float o_a[3], a[3], o_c[3], c[3]; + + sub_v3_v3v3(o_a, o_v1, o_v2); + sub_v3_v3v3(a, v1, v2); + + cross_v3_v3v3(o_c, o_a, no); + cross_v3_v3v3(c, a, no); + + if (dot_v3v3(c, o_c) <= 0) + return 1; + else + return 0; +} + +// Detects and fix a quad wrapping after the resize +// Arguments are the orginal verts followed by the final verts and then the bevel size and the normal +static void fix_bevel_quad_wrap(float *o_v1, float *o_v2, float *o_v3, float *o_v4, float *v1, float *v2, float *v3, float *v4, float d, float *no) +{ + float vec[3]; + char wrap[4]; + + // Quads can wrap partially. Watch out + wrap[0] = detect_wrap(o_v1, o_v2, v1, v2, no); // Edge 1-2 + wrap[1] = detect_wrap(o_v2, o_v3, v2, v3, no); // Edge 2-3 + wrap[2] = detect_wrap(o_v3, o_v4, v3, v4, no); // Edge 3-4 + wrap[3] = detect_wrap(o_v4, o_v1, v4, v1, no); // Edge 4-1 + + // Edge 1 inverted + if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 0) { + fix_bevel_wrap(vec, o_v2, o_v3, o_v4, o_v1, d, no); + copy_v3_v3(v1, vec); + copy_v3_v3(v2, vec); + } + // Edge 2 inverted + else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 0) { + fix_bevel_wrap(vec, o_v3, o_v4, o_v1, o_v2, d, no); + copy_v3_v3(v2, vec); + copy_v3_v3(v3, vec); + } + // Edge 3 inverted + else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) { + fix_bevel_wrap(vec, o_v4, o_v1, o_v2, o_v3, d, no); + copy_v3_v3(v3, vec); + copy_v3_v3(v4, vec); + } + // Edge 4 inverted + else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 1) { + fix_bevel_wrap(vec, o_v1, o_v2, o_v3, o_v4, d, no); + copy_v3_v3(v4, vec); + copy_v3_v3(v1, vec); + } + // Edge 2 and 4 inverted + else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 1) { + add_v3_v3v3(vec, v2, v3); + mul_v3_fl(vec, 0.5); + copy_v3_v3(v2, vec); + copy_v3_v3(v3, vec); + add_v3_v3v3(vec, v1, v4); + mul_v3_fl(vec, 0.5); + copy_v3_v3(v1, vec); + copy_v3_v3(v4, vec); + } + // Edge 1 and 3 inverted + else if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) { + add_v3_v3v3(vec, v1, v2); + mul_v3_fl(vec, 0.5); + copy_v3_v3(v1, vec); + copy_v3_v3(v2, vec); + add_v3_v3v3(vec, v3, v4); + mul_v3_fl(vec, 0.5); + copy_v3_v3(v3, vec); + copy_v3_v3(v4, vec); + } + // Totally inverted + else if (wrap[0] == 1 && wrap[1] == 1 && wrap[2] == 1 && wrap[3] == 1) { + add_v3_v3v3(vec, v1, v2); + add_v3_v3v3(vec, vec, v3); + add_v3_v3v3(vec, vec, v4); + mul_v3_fl(vec, 0.25); + copy_v3_v3(v1, vec); + copy_v3_v3(v2, vec); + copy_v3_v3(v3, vec); + copy_v3_v3(v4, vec); + } + +} + +// Detects and fix a tri wrapping after the resize +// Arguments are the orginal verts followed by the final verts and the normal +// Triangles cannot wrap partially (not in this situation +static void fix_bevel_tri_wrap(float *o_v1, float *o_v2, float *o_v3, float *v1, float *v2, float *v3, float *no) +{ + if (detect_wrap(o_v1, o_v2, v1, v2, no)) { + float vec[3]; + add_v3_v3v3(vec, o_v1, o_v2); + add_v3_v3v3(vec, vec, o_v3); + mul_v3_fl(vec, 1.0f/3.0f); + copy_v3_v3(v1, vec); + copy_v3_v3(v2, vec); + copy_v3_v3(v3, vec); + } +} + +static void bevel_shrink_faces(float d, int flag) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + float vec[3], no[3], v1[3], v2[3], v3[3], v4[3]; + + /* move edges of all faces with efa->f1 & flag closer towards their centers */ + efa= em->faces.first; + while (efa) { + if (efa->f1 & flag) { + copy_v3_v3(v1, efa->v1->co); + copy_v3_v3(v2, efa->v2->co); + copy_v3_v3(v3, efa->v3->co); + copy_v3_v3(no, efa->n); + if (efa->v4 == NULL) { + bevel_displace_vec(vec, v1, v2, v3, d, no); + copy_v3_v3(efa->v2->co, vec); + bevel_displace_vec(vec, v2, v3, v1, d, no); + copy_v3_v3(efa->v3->co, vec); + bevel_displace_vec(vec, v3, v1, v2, d, no); + copy_v3_v3(efa->v1->co, vec); + + fix_bevel_tri_wrap(v1, v2, v3, efa->v1->co, efa->v2->co, efa->v3->co, no); + } else { + copy_v3_v3(v4, efa->v4->co); + bevel_displace_vec(vec, v1, v2, v3, d, no); + copy_v3_v3(efa->v2->co, vec); + bevel_displace_vec(vec, v2, v3, v4, d, no); + copy_v3_v3(efa->v3->co, vec); + bevel_displace_vec(vec, v3, v4, v1, d, no); + copy_v3_v3(efa->v4->co, vec); + bevel_displace_vec(vec, v4, v1, v2, d, no); + copy_v3_v3(efa->v1->co, vec); + + fix_bevel_quad_wrap(v1, v2, v3, v4, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, d, no); + } + } + efa= efa->next; + } +} + +static void bevel_shrink_draw(float d, int flag) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + float vec[3], no[3], v1[3], v2[3], v3[3], v4[3], fv1[3], fv2[3], fv3[3], fv4[3]; + + /* move edges of all faces with efa->f1 & flag closer towards their centers */ + efa= em->faces.first; + while (efa) { + copy_v3_v3(v1, efa->v1->co); + copy_v3_v3(v2, efa->v2->co); + copy_v3_v3(v3, efa->v3->co); + copy_v3_v3(no, efa->n); + if (efa->v4 == NULL) { + bevel_displace_vec(vec, v1, v2, v3, d, no); + copy_v3_v3(fv2, vec); + bevel_displace_vec(vec, v2, v3, v1, d, no); + copy_v3_v3(fv3, vec); + bevel_displace_vec(vec, v3, v1, v2, d, no); + copy_v3_v3(fv1, vec); + + fix_bevel_tri_wrap(v1, v2, v3, fv1, fv2, fv3, no); + + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv2); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv2); + glVertex3fv(fv3); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv3); + glEnd(); + } else { + copy_v3_v3(v4, efa->v4->co); + bevel_displace_vec(vec, v4, v1, v2, d, no); + copy_v3_v3(fv1, vec); + bevel_displace_vec(vec, v1, v2, v3, d, no); + copy_v3_v3(fv2, vec); + bevel_displace_vec(vec, v2, v3, v4, d, no); + copy_v3_v3(fv3, vec); + bevel_displace_vec(vec, v3, v4, v1, d, no); + copy_v3_v3(fv4, vec); + + fix_bevel_quad_wrap(v1, v2, v3, v4, fv1, fv2, fv3, fv4, d, no); + + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv2); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv2); + glVertex3fv(fv3); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv3); + glVertex3fv(fv4); + glEnd(); + glBegin(GL_LINES); + glVertex3fv(fv1); + glVertex3fv(fv4); + glEnd(); + } + efa= efa->next; + } +} + +static void bevel_mesh(float bsize, int allfaces) +{ + EditMesh *em = G.editMesh; +//#define BEV_DEBUG +/* Enables debug printfs and assigns material indices: */ +/* 2 = edge quad */ +/* 3 = fill polygon (vertex clusters) */ + + EditFace *efa, *example; //, *nextvl; + EditEdge *eed, *eed2; + EditVert *neweve[1024], *eve, *eve2, *eve3, *v1, *v2, *v3, *v4; //, *eve4; + //short found4, search; + //float f1, f2, f3, f4; + float cent[3], min[3], max[3]; + int a, b, c; + float limit= 0.001f; + + if(multires_test()) return; + + waitcursor(1); + + removedoublesflag(1, 0, limit); + + /* tag all original faces */ + efa= em->faces.first; + while (efa) { + efa->f1= 0; + if (faceselectedAND(efa, 1)||allfaces) { + efa->f1= 1; + efa->v1->f |= 128; + efa->v2->f |= 128; + efa->v3->f |= 128; + if (efa->v4) efa->v4->f |= 128; + } + efa->v1->f &= ~64; + efa->v2->f &= ~64; + efa->v3->f &= ~64; + if (efa->v4) efa->v4->f &= ~64; + + efa= efa->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: split\n"); +#endif + + efa= em->faces.first; + while (efa) { + if (efa->f1 & 1) { + efa->f1-= 1; + v1= addvertlist(efa->v1->co, efa->v1); + v1->f= efa->v1->f & ~128; + efa->v1->tmp.v = v1; + + v1= addvertlist(efa->v2->co, efa->v2); + v1->f= efa->v2->f & ~128; + efa->v2->tmp.v = v1; + + v1= addvertlist(efa->v3->co, efa->v3); + v1->f= efa->v3->f & ~128; + efa->v3->tmp.v = v1; + + if (efa->v4) { + v1= addvertlist(efa->v4->co, efa->v4); + v1->f= efa->v4->f & ~128; + efa->v4->tmp.v = v1; + } + + /* Needs better adaption of creases? */ + addedgelist(efa->e1->v1->tmp.v, + efa->e1->v2->tmp.v, + efa->e1); + addedgelist(efa->e2->v1->tmp.v, + efa->e2->v2->tmp.v, + efa->e2); + addedgelist(efa->e3->v1->tmp.v, + efa->e3->v2->tmp.v, + efa->e3); + if (efa->e4) addedgelist(efa->e4->v1->tmp.v, + efa->e4->v2->tmp.v, + efa->e4); + + if(efa->v4) { + v1 = efa->v1->tmp.v; + v2 = efa->v2->tmp.v; + v3 = efa->v3->tmp.v; + v4 = efa->v4->tmp.v; + addfacelist(v1, v2, v3, v4, efa,NULL); + } else { + v1= efa->v1->tmp.v; + v2= efa->v2->tmp.v; + v3= efa->v3->tmp.v; + addfacelist(v1, v2, v3, 0, efa,NULL); + } + + efa= efa-> next; + } else { + efa= efa->next; + } + } + + for(efa= em->faces.first; efa; efa= efa->next) { + if( (efa->v1->f & 128) && (efa->v2->f & 128) && (efa->v3->f & 128) ) { + if(efa->v4==NULL || (efa->v4->f & 128)) efa->f |= 128; + } + } + + delfaceflag(128); // works with face flag now + + /* tag all faces for shrink*/ + efa= em->faces.first; + while (efa) { + if (faceselectedAND(efa, 1)||allfaces) { + efa->f1= 2; + } + efa= efa->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: make edge quads\n"); +#endif + + /* find edges that are on each other and make quads between them */ + + eed= em->edges.first; + while(eed) { + eed->f2= eed->f1= 0; + if ( ((eed->v1->f & eed->v2->f) & 1) || allfaces) + eed->f1 |= 4; /* original edges */ + eed->tmp.v = 0; + eed= eed->next; + } + + eed= em->edges.first; + while (eed) { + if ( ((eed->f1 & 2)==0) && (eed->f1 & 4) ) { + eed2= em->edges.first; + while (eed2) { + if ( (eed2 != eed) && ((eed2->f1 & 2)==0) && (eed->f1 & 4) ) { + if ( + (eed->v1 != eed2->v1) && + (eed->v1 != eed2->v2) && + (eed->v2 != eed2->v1) && + (eed->v2 != eed2->v2) && ( + ( compare_v3v3(eed->v1->co, eed2->v1->co, limit) && + compare_v3v3(eed->v2->co, eed2->v2->co, limit) ) || + ( compare_v3v3(eed->v1->co, eed2->v2->co, limit) && + compare_v3v3(eed->v2->co, eed2->v1->co, limit) ) ) ) + { + +#ifdef BEV_DEBUG + fprintf(stderr, "bevel_mesh: edge quad\n"); +#endif + + eed->f1 |= 2; /* these edges are finished */ + eed2->f1 |= 2; + + example= NULL; + efa= em->faces.first; /* search example face (for mat_nr, ME_SMOOTH, ...) */ + while (efa) { + if ( (efa->e1 == eed) || + (efa->e2 == eed) || + (efa->e3 == eed) || + (efa->e4 && (efa->e4 == eed)) ) { + example= efa; + efa= NULL; + } + if (efa) efa= efa->next; + } + + neweve[0]= eed->v1; neweve[1]= eed->v2; + neweve[2]= eed2->v1; neweve[3]= eed2->v2; + + if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) { + efa= NULL; + + if (compare_v3v3(eed->v1->co, eed2->v2->co, limit)) { + efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], example,NULL); + } else { + efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], example,NULL); + } + + if(efa) { + float inp; + normal_tri_v3( efa->n,efa->v1->co, efa->v2->co, efa->v3->co); + inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2]; + if(inp < 0.0) flipface(efa); +#ifdef BEV_DEBUG + efa->mat_nr= 1; +#endif + } else fprintf(stderr,"bevel_mesh: error creating face\n"); + } + eed2= NULL; + } + } + if (eed2) eed2= eed2->next; + } + } + eed= eed->next; + } + + eed= em->edges.first; + while(eed) { + eed->f2= eed->f1= 0; + eed->f1= 0; + eed->v1->f1 &= ~1; + eed->v2->f1 &= ~1; + eed->tmp.v = 0; + eed= eed->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: find clusters\n"); +#endif + + /* Look for vertex clusters */ + + eve= em->verts.first; + while (eve) { + eve->f &= ~(64|128); + eve->tmp.v = NULL; + eve= eve->next; + } + + /* eve->f: 128: first vertex in a list (->tmp.v) */ + /* 64: vertex is in a list */ + + eve= em->verts.first; + while (eve) { + eve2= em->verts.first; + eve3= NULL; + while (eve2) { + if ((eve2 != eve) && ((eve2->f & (64|128))==0)) { + if (compare_v3v3(eve->co, eve2->co, limit)) { + if ((eve->f & (128|64)) == 0) { + /* fprintf(stderr,"Found vertex cluster:\n *\n *\n"); */ + eve->f |= 128; + eve->tmp.v = eve2; + eve3= eve2; + } else if ((eve->f & 64) == 0) { + /* fprintf(stderr," *\n"); */ + if (eve3) eve3->tmp.v = eve2; + eve2->f |= 64; + eve3= eve2; + } + } + } + eve2= eve2->next; + if (!eve2) { + if (eve3) eve3->tmp.v = NULL; + } + } + eve= eve->next; + } + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: shrink faces\n"); +#endif + + bevel_shrink_faces(bsize, 2); + +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: fill clusters\n"); +#endif + + /* Make former vertex clusters faces */ + + eve= em->verts.first; + while (eve) { + eve->f &= ~64; + eve= eve->next; + } + + eve= em->verts.first; + while (eve) { + if (eve->f & 128) { + eve->f &= ~128; + a= 0; + neweve[a]= eve; + eve2 = eve->tmp.v; + while (eve2) { + a++; + neweve[a]= eve2; + eve2 = eve2->tmp.v; + } + a++; + efa= NULL; + if (a>=3) { + example= NULL; + efa= em->faces.first; /* search example face */ + while (efa) { + if ( (efa->v1 == neweve[0]) || + (efa->v2 == neweve[0]) || + (efa->v3 == neweve[0]) || + (efa->v4 && (efa->v4 == neweve[0])) ) { + example= efa; + efa= NULL; + } + if (efa) efa= efa->next; + } +#ifdef BEV_DEBUG + fprintf(stderr,"bevel_mesh: Making %d-gon\n", a); +#endif + if (a>4) { + cent[0]= cent[1]= cent[2]= 0.0; + INIT_MINMAX(min, max); + for (b=0; b<a; b++) { + add_v3_v3v3(cent, cent, neweve[b]->co); + DO_MINMAX(neweve[b]->co, min, max); + } + cent[0]= (min[0]+max[0])/2; + cent[1]= (min[1]+max[1])/2; + cent[2]= (min[2]+max[2])/2; + eve2= addvertlist(cent, NULL); + eve2->f |= 1; + eed= em->edges.first; + while (eed) { + c= 0; + for (b=0; b<a; b++) + if ((neweve[b]==eed->v1) || (neweve[b]==eed->v2)) c++; + if (c==2) { + if(exist_face(eed->v1, eed->v2, eve2, 0)==0) { + efa= addfacelist(eed->v1, eed->v2, eve2, 0, example,NULL); +#ifdef BEV_DEBUG + efa->mat_nr= 2; +#endif + } + } + eed= eed->next; + } + } else if (a==4) { + if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) { + /* the order of vertices can be anything, three cases to check */ + if( convex(neweve[0]->co, neweve[1]->co, neweve[2]->co, neweve[3]->co) ) { + efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[2]->co, neweve[3]->co, neweve[1]->co) ) { + efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], NULL, NULL); + } + else if( convex(neweve[0]->co, neweve[2]->co, neweve[1]->co, neweve[3]->co) ) { + efa= addfacelist(neweve[0], neweve[2], neweve[1], neweve[3], NULL, NULL); + } + } + } + else if (a==3) { + if(exist_face(neweve[0], neweve[1], neweve[2], 0)==0) + efa= addfacelist(neweve[0], neweve[1], neweve[2], 0, example, NULL); + } + if(efa) { + float inp; + normal_tri_v3( efa->n,neweve[0]->co, neweve[1]->co, neweve[2]->co); + inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2]; + if(inp < 0.0) flipface(efa); +#ifdef BEV_DEBUG + efa->mat_nr= 2; +#endif + } + } + } + eve= eve->next; + } + + eve= em->verts.first; + while (eve) { + eve->f1= 0; + eve->f &= ~(128|64); + eve->tmp.v= NULL; + eve= eve->next; + } + + recalc_editnormals(); + waitcursor(0); + countall(); + allqueue(REDRAWVIEW3D, 0); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + + removedoublesflag(1, 0, limit); + + /* flush selected vertices to edges/faces */ + EM_select_flush(); + +#undef BEV_DEBUG +} + +static void bevel_mesh_recurs(float bsize, short recurs, int allfaces) +{ + float d; + short nr; + + d= bsize; + for (nr=0; nr<recurs; nr++) { + bevel_mesh(d, allfaces); + if (nr==0) d /= 3; else d /= 2; + } +} + +void bevel_menu(void) +{ + BME_Mesh *bm; + BME_TransData_Head *td; + TransInfo *t; + int options, res, gbm_free = 0; + + t = BIF_GetTransInfo(); + if (!G.editBMesh) { + G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh"); + gbm_free = 1; + } + + G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT; + G.editBMesh->res = 1; + + while(G.editBMesh->options & BME_BEVEL_RUNNING) { + options = G.editBMesh->options; + res = G.editBMesh->res; + bm = BME_editmesh_to_bmesh(G.editMesh); + BIF_undo_push("Pre-Bevel"); + free_editMesh(G.editMesh); + BME_bevel(bm,0.1f,res,options,0,0,&td); + BME_bmesh_to_editmesh(bm, td); + EM_selectmode_flush(); + G.editBMesh->bm = bm; + G.editBMesh->td = td; + initTransform(TFM_BEVEL,CTX_BMESH); + Transform(); + BME_free_transdata(td); + BME_free_mesh(bm); + if (t->state != TRANS_CONFIRM) { + BIF_undo(); + } + if (options == G.editBMesh->options) { + G.editBMesh->options &= ~BME_BEVEL_RUNNING; + } + } + + if (gbm_free) { + MEM_freeN(G.editBMesh); + G.editBMesh = NULL; + } +} + + +void bevel_menu_old() +{ + char Finished = 0, Canceled = 0, str[100], Recalc = 0; + short mval[2], oval[2], curval[2], event = 0, recurs = 1, nr; + float vec[3], d, drawd=0.0, center[3], fac = 1; + + getmouseco_areawin(mval); + oval[0] = mval[0]; oval[1] = mval[1]; + + // Silly hackish code to initialise the variable (warning if not done) + // while still drawing in the first iteration (and without using another variable) + curval[0] = mval[0] + 1; curval[1] = mval[1] + 1; + + // Init grabz for window to vec conversions + initgrabz(-G.vd->ofs[0], -G.vd->ofs[1], -G.vd->ofs[2]); + window_to_3d(center, mval[0], mval[1]); + + if(button(&recurs, 1, 4, "Recursion:")==0) return; + + for (nr=0; nr<recurs-1; nr++) { + if (nr==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * nr * 2.0f); + } + + EM_set_flag_all(SELECT); + + SetBlenderCursor(SYSCURSOR); + + while (Finished == 0) + { + getmouseco_areawin(mval); + if (mval[0] != curval[0] || mval[1] != curval[1] || (Recalc == 1)) + { + Recalc = 0; + curval[0] = mval[0]; + curval[1] = mval[1]; + + window_to_3d(vec, mval[0]-oval[0], mval[1]-oval[1]); + d = normalize_v3(vec) / 10; + + + drawd = d * fac; + if (G.qual & LR_CTRLKEY) + drawd = (float) floor(drawd * 10.0f)/10.0f; + if (G.qual & LR_SHIFTKEY) + drawd /= 10; + + /*------------- Preview lines--------------- */ + + /* uses callback mechanism to draw it all in current area */ + scrarea_do_windraw(curarea); + + /* set window matrix to perspective, default an area returns with buttons transform */ + persp(PERSP_VIEW); + /* make a copy, for safety */ + glPushMatrix(); + /* multiply with the object transformation */ + mymultmatrix(G.obedit->obmat); + + glColor3ub(255, 255, 0); + + // PREVIEW CODE GOES HERE + bevel_shrink_draw(drawd, 2); + + /* restore matrix transform */ + glPopMatrix(); + + sprintf(str, "Bevel Size: %.4f LMB to confirm, RMB to cancel, SPACE to input directly.", drawd); + headerprint(str); + + /* this also verifies other area/windows for clean swap */ + screen_swapbuffers(); + + persp(PERSP_WIN); + + glDrawBuffer(GL_FRONT); + + BIF_ThemeColor(TH_WIRE); + + setlinestyle(3); + glBegin(GL_LINE_STRIP); + glVertex2sv(mval); + glVertex2sv(oval); + glEnd(); + setlinestyle(0); + + persp(PERSP_VIEW); + bglFlush(); // flush display for frontbuffer + glDrawBuffer(GL_BACK); + } + while(qtest()) { + short val=0; + event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle + + /* val==0 on key-release event */ + if(val && (event==ESCKEY || event==RIGHTMOUSE || event==LEFTMOUSE || event==RETKEY || event==ESCKEY)) { + if (event==RIGHTMOUSE || event==ESCKEY) + Canceled = 1; + Finished = 1; + } + else if (val && event==SPACEKEY) { + if (fbutton(&d, 0.000, 10.000, 10, 0, "Width:")!=0) { + drawd = d * fac; + Finished = 1; + } + } + else if (val) { + /* On any other keyboard event, recalc */ + Recalc = 1; + } + + } + } + if (Canceled==0) { + SetBlenderCursor(BC_WAITCURSOR); + bevel_mesh_recurs(drawd/fac, recurs, 1); + righthandfaces(1); + SetBlenderCursor(SYSCURSOR); + BIF_undo_push("Bevel"); + } +} + +/* -------------------- More tools ------------------ */ + +void mesh_set_face_flags(short mode) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + MTFace *tface; + short m_tex=0, m_tiles=0, m_shared=0, + m_light=0, m_invis=0, m_collision=0, + m_twoside=0, m_obcolor=0, m_halo=0, + m_billboard=0, m_shadow=0, m_text=0, + m_sort=0; + short flag = 0, change = 0; + + if (!EM_texFaceCheck()) { + error("not a mesh with uv/image layers"); + return; + } + + add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL); + add_numbut(1, TOG|SHO, "Tiles", 0, 0, &m_tiles, NULL); + add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL); + add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL); + add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL); + add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL); + add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL); + add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL); + add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL); + add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL); + add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL); + add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL); + add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL); + + if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW)) + return; + + /* these 2 cant both be on */ + if (mode) /* are we seeting*/ + if (m_halo) + m_billboard = 0; + + if (m_tex) flag |= TF_TEX; + if (m_tiles) flag |= TF_TILES; + if (m_shared) flag |= TF_SHAREDCOL; + if (m_light) flag |= TF_LIGHT; + if (m_invis) flag |= TF_INVISIBLE; + if (m_collision) flag |= TF_DYNAMIC; + if (m_twoside) flag |= TF_TWOSIDE; + if (m_obcolor) flag |= TF_OBCOL; + if (m_halo) flag |= TF_BILLBOARD; + if (m_billboard) flag |= TF_BILLBOARD2; + if (m_shadow) flag |= TF_SHADOW; + if (m_text) flag |= TF_BMFONT; + if (m_sort) flag |= TF_ALPHASORT; + + if (flag==0) + return; + + efa= em->faces.first; + while(efa) { + if(efa->f & SELECT) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (mode) tface->mode |= flag; + else tface->mode &= ~flag; + change = 1; + } + efa= efa->next; + } + + if (change) { + BIF_undo_push((mode ? "Set Flags" : "Clear Flags")); + + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWVIEW3D, 0); + } +} + +void mesh_set_smooth_faces(short event) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + + if(G.obedit==0) return; + + if(G.obedit->type != OB_MESH) return; + + efa= em->faces.first; + while(efa) { + if(efa->f & SELECT) { + if(event==1) efa->flag |= ME_SMOOTH; + else if(event==0) efa->flag &= ~ME_SMOOTH; + } + efa= efa->next; + } + + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + + if(event==1) BIF_undo_push("Set Smooth"); + else if(event==0) BIF_undo_push("Set Solid"); +} + +/* helper to find edge for edge_rip */ +static float mesh_rip_edgedist(float mat[][4], float *co1, float *co2, short *mval) +{ + float vec1[3], vec2[3], mvalf[2]; + + view3d_project_float(curarea, co1, vec1, mat); + view3d_project_float(curarea, co2, vec2, mat); + mvalf[0]= (float)mval[0]; + mvalf[1]= (float)mval[1]; + + return dist_to_line_segment_v2(mvalf, vec1, vec2); +} + +/* helper for below */ +static void mesh_rip_setface(EditFace *sefa) +{ + /* put new vertices & edges in best face */ + if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v; + if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v; + if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v; + if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v; + + sefa->e1= addedgelist(sefa->v1, sefa->v2, sefa->e1); + sefa->e2= addedgelist(sefa->v2, sefa->v3, sefa->e2); + if(sefa->v4) { + sefa->e3= addedgelist(sefa->v3, sefa->v4, sefa->e3); + sefa->e4= addedgelist(sefa->v4, sefa->v1, sefa->e4); + } + else + sefa->e3= addedgelist(sefa->v3, sefa->v1, sefa->e3); + +} + +/* based on mouse cursor position, it defines how is being ripped */ +void mesh_rip(void) +{ + extern void faceloop_select(EditEdge *startedge, int select); + EditMesh *em = G.editMesh; + EditVert *eve, *nextve; + EditEdge *eed, *seed= NULL; + EditFace *efa, *sefa= NULL; + float projectMat[4][4], viewMat[4][4], vec[3], dist, mindist; + short doit= 1, mval[2],propmode,prop; + + propmode = G.scene->prop_mode; + G.scene->prop_mode = 0; + prop = G.scene->proportional; + G.scene->proportional = 0; + + /* select flush... vertices are important */ + EM_selectmode_set(); + + getmouseco_areawin(mval); + view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat); + + /* find best face, exclude triangles and break on face select or faces with 2 edges select */ + mindist= 1000000.0f; + for(efa= em->faces.first; efa; efa=efa->next) { + if( efa->f & 1) + break; + if(efa->v4 && faceselectedOR(efa, SELECT) ) { + int totsel=0; + + if(efa->e1->f & SELECT) totsel++; + if(efa->e2->f & SELECT) totsel++; + if(efa->e3->f & SELECT) totsel++; + if(efa->e4->f & SELECT) totsel++; + + if(totsel>1) + break; + view3d_project_float(curarea, efa->cent, vec, projectMat); + dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) ); + if(dist<mindist) { + mindist= dist; + sefa= efa; + } + } + } + + if(efa) { + error("Can't perform ripping with faces selected this way"); + return; + } + if(sefa==NULL) { + error("No proper selection or faces included"); + return; + } + + + /* duplicate vertices, new vertices get selected */ + for(eve = em->verts.last; eve; eve= eve->prev) { + eve->tmp.v = NULL; + if(eve->f & SELECT) { + eve->tmp.v = addvertlist(eve->co, eve); + eve->f &= ~SELECT; + eve->tmp.v->f |= SELECT; + } + } + + /* find the best candidate edge */ + /* or one of sefa edges is selected... */ + if(sefa->e1->f & SELECT) seed= sefa->e2; + if(sefa->e2->f & SELECT) seed= sefa->e1; + if(sefa->e3->f & SELECT) seed= sefa->e2; + if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3; + + /* or we do the distance trick */ + if(seed==NULL) { + mindist= 1000000.0f; + if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) { + dist = mesh_rip_edgedist(projectMat, + sefa->e1->v1->co, + sefa->e1->v2->co, mval); + if(dist<mindist) { + seed= sefa->e1; + mindist= dist; + } + } + if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) { + dist = mesh_rip_edgedist(projectMat, + sefa->e2->v1->co, + sefa->e2->v2->co, mval); + if(dist<mindist) { + seed= sefa->e2; + mindist= dist; + } + } + if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) { + dist= mesh_rip_edgedist(projectMat, + sefa->e3->v1->co, + sefa->e3->v2->co, mval); + if(dist<mindist) { + seed= sefa->e3; + mindist= dist; + } + } + if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) { + dist= mesh_rip_edgedist(projectMat, + sefa->e4->v1->co, + sefa->e4->v2->co, mval); + if(dist<mindist) { + seed= sefa->e4; + mindist= dist; + } + } + } + + if(seed==NULL) { // never happens? + error("No proper edge found to start"); + return; + } + + faceloop_select(seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1 + + /* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */ + for(eed = em->edges.last; eed; eed= eed->prev) { + eed->tmp.v = NULL; + if((eed->v1->tmp.v) || (eed->v2->tmp.v)) { + EditEdge *newed; + + newed= addedgelist(eed->v1->tmp.v?eed->v1->tmp.v:eed->v1, + eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed); + if(eed->f & SELECT) { + eed->f &= ~SELECT; + newed->f |= SELECT; + } + eed->tmp.v = (EditVert *)newed; + } + } + + /* first clear edges to help finding neighbours */ + for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; + + /* put new vertices & edges && flag in best face */ + mesh_rip_setface(sefa); + + /* starting with neighbours of best face, we loop over the seam */ + sefa->f1= 2; + doit= 1; + while(doit) { + doit= 0; + + for(efa= em->faces.first; efa; efa=efa->next) { + /* new vert in face */ + if (efa->v1->tmp.v || efa->v2->tmp.v || + efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) { + /* face is tagged with loop */ + if(efa->f1==1) { + mesh_rip_setface(efa); + efa->f1= 2; + doit= 1; + } + } + } + } + + /* remove loose edges, that were part of a ripped face */ + for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed = em->edges.last; eed; eed= eed->prev) 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; + } + + for(eed = em->edges.last; eed; eed= seed) { + seed= eed->prev; + if(eed->f1==0) { + if(eed->v1->tmp.v || eed->v2->tmp.v || + (eed->v1->f & SELECT) || (eed->v2->f & SELECT)) { + remedge(eed); + free_editedge(eed); + eed= NULL; + } + } + if(eed) { + eed->v1->f1= 1; + eed->v2->f1= 1; + } + } + + /* and remove loose selected vertices, that got duplicated accidentally */ + for(eve = em->verts.first; eve; eve= nextve) { + nextve= eve->next; + if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) { + BLI_remlink(&em->verts,eve); + free_editvert(eve); + } + } + + countall(); // apparently always needed when adding stuff, derived mesh + +#ifdef WITH_VERSE + if(G.editMesh->vnode) { + sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode); + sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode); + } +#endif + + BIF_TransformSetUndo("Rip"); + initTransform(TFM_TRANSLATION, 0); + Transform(); + + G.scene->prop_mode = propmode; + G.scene->proportional = prop; +} + +void shape_propagate(void) +{ + EditMesh *em = G.editMesh; + EditVert *ev = NULL; + Mesh* me = (Mesh*)G.obedit->data; + Key* ky = NULL; + KeyBlock* kb = NULL; + Base* base=NULL; + + + if(me->key){ + ky = me->key; + } else { + error("Object Has No Key"); + return; + } + + if(ky->block.first){ + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + for(kb=ky->block.first;kb;kb = kb->next){ + float *data; + data = kb->data; + copy_v3_v3(data+(ev->keyindex*3),ev->co); + } + } + } + } else { + error("Object Has No Blendshapes"); + return; + } + + //TAG Mesh Objects that share this data + for(base = G.scene->base.first; base; base = base->next){ + if(base->object && base->object->data == me){ + base->object->recalc = OB_RECALC_DATA; + } + } + + BIF_undo_push("Propagate Blendshape Verts"); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + return; +} + +void shape_copy_from_lerp(KeyBlock* thisBlock, KeyBlock* fromBlock) +{ + EditMesh *em = G.editMesh; + EditVert *ev = NULL; + short mval[2], curval[2], event = 0, finished = 0, canceled = 0, fullcopy=0 ; + float perc = 0; + char str[64]; + float *data, *odata; + + data = fromBlock->data; + odata = thisBlock->data; + + getmouseco_areawin(mval); + curval[0] = mval[0] + 1; curval[1] = mval[1] + 1; + + while (finished == 0) + { + getmouseco_areawin(mval); + if (mval[0] != curval[0] || mval[1] != curval[1]) + { + + if(mval[0] > curval[0]) + perc += 0.1; + else if(mval[0] < curval[0]) + perc -= 0.1; + + if(perc < 0) perc = 0; + if(perc > 1) perc = 1; + + curval[0] = mval[0]; + curval[1] = mval[1]; + + if(fullcopy == 1){ + perc = 1; + } + + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + interp_v3_v3v3(ev->co,odata+(ev->keyindex*3),data+(ev->keyindex*3),perc); + } + } + sprintf(str,"Blending at %d%c MMB to Copy at 100%c",(int)(perc*100),'%','%'); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + headerprint(str); + force_draw(0); + + if(fullcopy == 1){ + break; + } + + } else { + PIL_sleep_ms(10); + } + + while(qtest()) { + short val=0; + event= extern_qread(&val); + if(val){ + if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)){ + finished = 1; + } + else if (event == MIDDLEMOUSE){ + fullcopy = 1; + } + else if (ELEM3(event,ESCKEY,RIGHTMOUSE,RIGHTMOUSE)){ + canceled = 1; + finished = 1; + } + } + } + } + if(!canceled) + BIF_undo_push("Copy Blendshape Verts"); + else + for(ev = em->verts.first; ev ; ev = ev->next){ + if(ev->f & SELECT){ + copy_v3_v3(ev->co, odata+(ev->keyindex*3)); + } + } + return; +} + + + +void shape_copy_select_from() +{ + Mesh* me = (Mesh*)G.obedit->data; + EditMesh *em = G.editMesh; + EditVert *ev = NULL; + int totverts = 0,curshape = G.obedit->shapenr; + + Key* ky = NULL; + KeyBlock *kb = NULL,*thisBlock = NULL; + int maxlen=32, nr=0, a=0; + char *menu; + + if(me->key){ + ky = me->key; + } else { + error("Object Has No Key"); + return; + } + + if(ky->block.first){ + for(kb=ky->block.first;kb;kb = kb->next){ + maxlen += 40; // Size of a block name + if(a == curshape-1){ + thisBlock = kb; + } + + a++; + } + a=0; + menu = MEM_callocN(maxlen, "Copy Shape Menu Text"); + strcpy(menu, "Copy Vert Positions from Shape %t|"); + for(kb=ky->block.first;kb;kb = kb->next){ + if(a != curshape-1){ + sprintf(menu,"%s %s %cx%d|",menu,kb->name,'%',a); + } + a++; + } + nr = pupmenu_col(menu, 20); + MEM_freeN(menu); + } else { + error("Object Has No Blendshapes"); + return; + } + + a = 0; + + for(kb=ky->block.first;kb;kb = kb->next){ + if(a == nr){ + + for(ev = em->verts.first;ev;ev = ev->next){ + totverts++; + } + + if(me->totvert != totverts){ + error("Shape Has had Verts Added/Removed, please cycle editmode before copying"); + return; + } + shape_copy_from_lerp(thisBlock,kb); + + return; + } + a++; + } + return; +} + +/* Collection Routines|Currently used by the improved merge code*/ +/* buildEdge_collection() creates a list of lists*/ +/* these lists are filled with edges that are topologically connected.*/ +/* This whole tool needs to be redone, its rather poorly implemented...*/ + +typedef struct Collection{ + struct Collection *next, *prev; + int index; + ListBase collectionbase; +} Collection; + +typedef struct CollectedEdge{ + struct CollectedEdge *next, *prev; + EditEdge *eed; +} CollectedEdge; + +#define MERGELIMIT 0.000001 + +static void build_edgecollection(ListBase *allcollections) +{ + EditEdge *eed; + Collection *edgecollection, *newcollection; + CollectedEdge *newedge; + + int currtag = 1; + short ebalanced = 0; + short collectionfound = 0; + + for (eed=G.editMesh->edges.first; eed; eed = eed->next){ + eed->tmp.l = 0; + eed->v1->tmp.l = 0; + eed->v2->tmp.l = 0; + } + + /*1st pass*/ + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + eed->v1->tmp.l = currtag; + eed->v2->tmp.l = currtag; + currtag +=1; + } + } + + /*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */ + while(ebalanced == 0){ + ebalanced = 1; + for(eed=G.editMesh->edges.first; eed; eed = eed->next){ + if(eed->f&SELECT){ + if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{ + if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l; + else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l; + ebalanced = 0; + } + } + } + } + + /*3rd pass, set all the edge flags (unnessecary?)*/ + for(eed=G.editMesh->edges.first; eed; eed = eed->next){ + if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l; + } + + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(eed->f&SELECT){ + if(allcollections->first){ + for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){ + if(edgecollection->index == eed->tmp.l){ + newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); + newedge->eed = eed; + BLI_addtail(&(edgecollection->collectionbase), newedge); + collectionfound = 1; + break; + } + else collectionfound = 0; + } + } + if(allcollections->first == NULL || collectionfound == 0){ + newcollection = MEM_mallocN(sizeof(Collection), "element collection"); + newcollection->index = eed->tmp.l; + newcollection->collectionbase.first = 0; + newcollection->collectionbase.last = 0; + + newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); + newedge->eed = eed; + + BLI_addtail(&(newcollection->collectionbase), newedge); + BLI_addtail(allcollections, newcollection); + } + } + + } +} + +static void freecollections(ListBase *allcollections) +{ + struct Collection *curcollection; + + for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next) + BLI_freelistN(&(curcollection->collectionbase)); + BLI_freelistN(allcollections); +} + +/*Begin UV Edge Collapse Code + Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail + in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it. + The welded UV edges can then be sorted and collapsed. +*/ +typedef struct wUV{ + struct wUV *next, *prev; + ListBase nodes; + float u, v; /*cached copy of UV coordinates pointed to by nodes*/ + EditVert *eve; + int f; +} wUV; + +typedef struct wUVNode{ + struct wUVNode *next, *prev; + float *u; /*pointer to original tface data*/ + float *v; /*pointer to original tface data*/ +} wUVNode; + +typedef struct wUVEdge{ + struct wUVEdge *next, *prev; + float v1uv[2], v2uv[2]; /*nasty.*/ + struct wUV *v1, *v2; /*oriented same as editedge*/ + EditEdge *eed; + int f; +} wUVEdge; + +typedef struct wUVEdgeCollect{ /*used for grouping*/ + struct wUVEdgeCollect *next, *prev; + wUVEdge *uved; + int id; +} wUVEdgeCollect; + +static void append_weldedUV(EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts) +{ + wUV *curwvert, *newwvert; + wUVNode *newnode; + int found; + MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + found = 0; + + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){ + newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); + newnode->u = &(tf->uv[tfindex][0]); + newnode->v = &(tf->uv[tfindex][1]); + BLI_addtail(&(curwvert->nodes), newnode); + found = 1; + break; + } + } + + if(!found){ + newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); + newnode->u = &(tf->uv[tfindex][0]); + newnode->v = &(tf->uv[tfindex][1]); + + newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert"); + newwvert->u = *(newnode->u); + newwvert->v = *(newnode->v); + newwvert->eve = eve; + + BLI_addtail(&(newwvert->nodes), newnode); + BLI_addtail(uvverts, newwvert); + + } +} + +static void build_weldedUVs(ListBase *uvverts) +{ + EditFace *efa; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->v1->f1) append_weldedUV(efa, efa->v1, 0, uvverts); + if(efa->v2->f1) append_weldedUV(efa, efa->v2, 1, uvverts); + if(efa->v3->f1) append_weldedUV(efa, efa->v3, 2, uvverts); + if(efa->v4 && efa->v4->f1) append_weldedUV(efa, efa->v4, 3, uvverts); + } +} + +static void append_weldedUVEdge(EditFace *efa, EditEdge *eed, ListBase *uvedges) +{ + wUVEdge *curwedge, *newwedge; + int v1tfindex, v2tfindex, found; + MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + found = 0; + + if(eed->v1 == efa->v1) v1tfindex = 0; + else if(eed->v1 == efa->v2) v1tfindex = 1; + else if(eed->v1 == efa->v3) v1tfindex = 2; + else /* if(eed->v1 == efa->v4) */ v1tfindex = 3; + + if(eed->v2 == efa->v1) v2tfindex = 0; + else if(eed->v2 == efa->v2) v2tfindex = 1; + else if(eed->v2 == efa->v3) v2tfindex = 2; + else /* if(eed->v2 == efa->v4) */ v2tfindex = 3; + + for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ + if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){ + found = 1; + break; //do nothing, we don't need another welded uv edge + } + } + + if(!found){ + newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge"); + newwedge->v1uv[0] = tf->uv[v1tfindex][0]; + newwedge->v1uv[1] = tf->uv[v1tfindex][1]; + newwedge->v2uv[0] = tf->uv[v2tfindex][0]; + newwedge->v2uv[1] = tf->uv[v2tfindex][1]; + newwedge->eed = eed; + + BLI_addtail(uvedges, newwedge); + } +} + +static void build_weldedUVEdges(ListBase *uvedges, ListBase *uvverts) +{ + wUV *curwvert; + wUVEdge *curwedge; + EditFace *efa; + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->e1->f1) append_weldedUVEdge(efa, efa->e1, uvedges); + if(efa->e2->f1) append_weldedUVEdge(efa, efa->e2, uvedges); + if(efa->e3->f1) append_weldedUVEdge(efa, efa->e3, uvedges); + if(efa->e4 && efa->e4->f1) append_weldedUVEdge(efa, efa->e4, uvedges); + } + + + //link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers + for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){ + curwedge->v1 = curwvert; + break; + } + } + for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ + if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){ + curwedge->v2 = curwvert; + break; + } + } + } +} + +static void free_weldedUVs(ListBase *uvverts) +{ + wUV *curwvert; + for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes)); + BLI_freelistN(uvverts); +} + +static void collapse_edgeuvs(void) +{ + ListBase uvedges, uvverts, allcollections; + wUVEdge *curwedge; + wUVNode *curwnode; + wUVEdgeCollect *collectedwuve, *newcollectedwuve; + Collection *wuvecollection, *newcollection; + int curtag, balanced, collectionfound= 0, vcount; + float avg[2]; + + if (!EM_texFaceCheck()) + return; + + uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL; + + build_weldedUVs(&uvverts); + build_weldedUVEdges(&uvedges, &uvverts); + + curtag = 0; + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + curwedge->v1->f = curtag; + curwedge->v2->f = curtag; + curtag +=1; + } + + balanced = 0; + while(!balanced){ + balanced = 1; + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + if(curwedge->v1->f != curwedge->v2->f){ + if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f; + else curwedge->v2->f = curwedge->v1->f; + balanced = 0; + } + } + } + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f; + + + for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ + if(allcollections.first){ + for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ + if(wuvecollection->index == curwedge->f){ + newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); + newcollectedwuve->uved = curwedge; + BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve); + collectionfound = 1; + break; + } + + else collectionfound = 0; + } + } + if(allcollections.first == NULL || collectionfound == 0){ + newcollection = MEM_callocN(sizeof(Collection), "element collection"); + newcollection->index = curwedge->f; + newcollection->collectionbase.first = 0; + newcollection->collectionbase.last = 0; + + newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); + newcollectedwuve->uved = curwedge; + + BLI_addtail(&(newcollection->collectionbase), newcollectedwuve); + BLI_addtail(&allcollections, newcollection); + } + } + + for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ + + vcount = avg[0] = avg[1] = 0; + + for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ + avg[0] += collectedwuve->uved->v1uv[0]; + avg[1] += collectedwuve->uved->v1uv[1]; + + avg[0] += collectedwuve->uved->v2uv[0]; + avg[1] += collectedwuve->uved->v2uv[1]; + + vcount +=2; + + } + + avg[0] /= vcount; avg[1] /= vcount; + + for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ + for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){ + *(curwnode->u) = avg[0]; + *(curwnode->v) = avg[1]; + } + for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){ + *(curwnode->u) = avg[0]; + *(curwnode->v) = avg[1]; + } + } + } + + free_weldedUVs(&uvverts); + BLI_freelistN(&uvedges); + freecollections(&allcollections); +} + +/*End UV Edge collapse code*/ + +static void collapseuvs(EditVert *mergevert) +{ + EditFace *efa; + MTFace *tf; + int uvcount; + float uvav[2]; + + if (!EM_texFaceCheck()) + return; + + uvcount = 0; + uvav[0] = 0; + uvav[1] = 0; + + for(efa = G.editMesh->faces.first; efa; efa=efa->next){ + tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) { + uvav[0] += tf->uv[0][0]; + uvav[1] += tf->uv[0][1]; + uvcount += 1; + } + if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){ + uvav[0] += tf->uv[1][0]; + uvav[1] += tf->uv[1][1]; + uvcount += 1; + } + if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){ + uvav[0] += tf->uv[2][0]; + uvav[1] += tf->uv[2][1]; + uvcount += 1; + } + if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){ + uvav[0] += tf->uv[3][0]; + uvav[1] += tf->uv[3][1]; + uvcount += 1; + } + } + + if(uvcount > 0) { + uvav[0] /= uvcount; + uvav[1] /= uvcount; + + for(efa = G.editMesh->faces.first; efa; efa=efa->next){ + tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE); + + if(efa->v1->f1){ + tf->uv[0][0] = uvav[0]; + tf->uv[0][1] = uvav[1]; + } + if(efa->v2->f1){ + tf->uv[1][0] = uvav[0]; + tf->uv[1][1] = uvav[1]; + } + if(efa->v3->f1){ + tf->uv[2][0] = uvav[0]; + tf->uv[2][1] = uvav[1]; + } + if(efa->v4 && efa->v4->f1){ + tf->uv[3][0] = uvav[0]; + tf->uv[3][1] = uvav[1]; + } + } + } +} + +int collapseEdges(void) +{ + EditVert *eve; + EditEdge *eed; + + ListBase allcollections; + CollectedEdge *curredge; + Collection *edgecollection; + + int totedges, groupcount, mergecount,vcount; + float avgcount[3]; + + allcollections.first = 0; + allcollections.last = 0; + + mergecount = 0; + + if(multires_test()) return 0; + + build_edgecollection(&allcollections); + groupcount = BLI_countlist(&allcollections); + + + for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){ + totedges = BLI_countlist(&(edgecollection->collectionbase)); + mergecount += totedges; + avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0; + + vcount = 0; + + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0]; + avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1]; + avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2]; + + avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0]; + avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1]; + avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2]; + + vcount +=2; + } + + avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount; + + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + copy_v3_v3(((EditEdge*)curredge->eed)->v1->co,avgcount); + copy_v3_v3(((EditEdge*)curredge->eed)->v2->co,avgcount); + } + + if (EM_texFaceCheck()) { + /*uv collapse*/ + for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0; + for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ + curredge->eed->v1->f1 = 1; + curredge->eed->v2->f1 = 1; + curredge->eed->f1 = 1; + } + collapse_edgeuvs(); + } + + } + freecollections(&allcollections); + removedoublesflag(1, 0, MERGELIMIT); + /*get rid of this!*/ + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + return mergecount; +} + +int merge_firstlast(int first, int uvmerge) +{ + EditVert *eve,*mergevert; + EditSelection *ese; + + if(multires_test()) return 0; + + /* do sanity check in mergemenu in edit.c ?*/ + if(first == 0){ + ese = G.editMesh->selected.last; + mergevert= (EditVert*)ese->data; + } + else{ + ese = G.editMesh->selected.first; + mergevert = (EditVert*)ese->data; + } + + if(mergevert->f&SELECT){ + for (eve=G.editMesh->verts.first; eve; eve=eve->next){ + if (eve->f&SELECT) + copy_v3_v3(eve->co,mergevert->co); + } + } + + if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){ + + for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f&SELECT) eve->f1 = 1; + } + collapseuvs(mergevert); + } + + countall(); + return removedoublesflag(1, 0, MERGELIMIT); +} + +int merge_target(int target, int uvmerge) +{ + EditVert *eve; + + if(multires_test()) return 0; + + if(target) snap_sel_to_curs(); + else snap_to_center(); + + if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){ + for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0; + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f&SELECT) eve->f1 = 1; + } + collapseuvs(NULL); + } + + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + return removedoublesflag(1, 0, MERGELIMIT); + +} +#undef MERGELIMIT + +typedef struct PathNode{ + int u; + int visited; + ListBase edges; +} PathNode; + +typedef struct PathEdge{ + struct PathEdge *next, *prev; + int v; + float w; +} PathEdge; + +void pathselect(void) +{ + EditVert *eve, *s, *t; + EditEdge *eed; + EditSelection *ese; + PathEdge *newpe, *currpe; + PathNode *currpn; + PathNode *Q; + int v, *previous, pathvert, pnindex; /*pnindex redundant?*/ + int unbalanced, totnodes; + short physical; + float *cost; + Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/ + + s = t = NULL; + + countall(); /*paranoid?*/ + + ese = ((EditSelection*)G.editMesh->selected.last); + if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT){ + physical= pupmenu("Distance Method? %t|Edge Length%x1|Topological%x0"); + + t = (EditVert*)ese->data; + s = (EditVert*)ese->prev->data; + + /*need to find out if t is actually reachable by s....*/ + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + eve->f1 = 0; + } + + s->f1 = 1; + + unbalanced = 1; + totnodes = 1; + while(unbalanced){ + unbalanced = 0; + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(!eed->h){ + if(eed->v1->f1 && !eed->v2->f1){ + eed->v2->f1 = 1; + totnodes++; + unbalanced = 1; + } + else if(eed->v2->f1 && !eed->v1->f1){ + eed->v1->f1 = 1; + totnodes++; + unbalanced = 1; + } + } + } + } + + + + if(s->f1 && t->f1){ /*t can be reached by s*/ + Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes"); + totnodes = 0; + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f1){ + Q[totnodes].u = totnodes; + Q[totnodes].edges.first = 0; + Q[totnodes].edges.last = 0; + Q[totnodes].visited = 0; + eve->tmp.p = &(Q[totnodes]); + totnodes++; + } + else eve->tmp.p = NULL; + } + + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(!eed->h){ + if(eed->v1->f1){ + currpn = ((PathNode*)eed->v1->tmp.p); + + newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); + newpe->v = ((PathNode*)eed->v2->tmp.p)->u; + if(physical){ + newpe->w = len_v3v3(eed->v1->co, eed->v2->co); + } + else newpe->w = 1; + newpe->next = 0; + newpe->prev = 0; + BLI_addtail(&(currpn->edges), newpe); + } + if(eed->v2->f1){ + currpn = ((PathNode*)eed->v2->tmp.p); + newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); + newpe->v = ((PathNode*)eed->v1->tmp.p)->u; + if(physical){ + newpe->w = len_v3v3(eed->v1->co, eed->v2->co); + } + else newpe->w = 1; + newpe->next = 0; + newpe->prev = 0; + BLI_addtail(&(currpn->edges), newpe); + } + } + } + + heap = BLI_heap_new(); + cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs"); + previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices"); + + for(v=0; v < totnodes; v++){ + cost[v] = 1000000; + previous[v] = -1; /*array of indices*/ + } + + pnindex = ((PathNode*)s->tmp.p)->u; + cost[pnindex] = 0; + BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex)); + + while( !BLI_heap_empty(heap) ){ + + pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap)); + currpn = &(Q[pnindex]); + + if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/ + break; + + for(currpe=currpn->edges.first; currpe; currpe=currpe->next){ + if(!Q[currpe->v].visited){ + if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){ + cost[currpe->v] = cost[currpn->u] + currpe->w; + previous[currpe->v] = currpn->u; + Q[currpe->v].visited = 1; + BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v)); + } + } + } + } + + pathvert = ((PathNode*)t->tmp.p)->u; + while(pathvert != -1){ + for(eve=G.editMesh->verts.first; eve; eve=eve->next){ + if(eve->f1){ + if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT; + } + } + pathvert = previous[pathvert]; + } + + for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges)); + MEM_freeN(Q); + MEM_freeN(cost); + MEM_freeN(previous); + BLI_heap_free(heap, NULL); + EM_select_flush(); + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + } + } + else{ + error("Path Selection requires that exactly two vertices be selected"); + return; + } +} + +void region_to_loop(void) +{ + EditEdge *eed; + EditFace *efa; + + if(G.totfacesel){ + for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0; + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->f&SELECT){ + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) + efa->e4->f1++; + } + } + + EM_clear_flag_all(SELECT); + + for(eed=G.editMesh->edges.first; eed; eed=eed->next){ + if(eed->f1 == 1) EM_select_edge(eed, 1); + } + + G.scene->selectmode = SCE_SELECT_EDGE; + EM_selectmode_set(); + countall(); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + BIF_undo_push("Face Region to Edge Loop"); + + } +} + +static int validate_loop(Collection *edgecollection) +{ + EditEdge *eed; + EditFace *efa; + CollectedEdge *curredge; + + /*1st test*/ + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + curredge->eed->v1->f1 = 0; + curredge->eed->v2->f1 = 0; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + curredge->eed->v1->f1++; + curredge->eed->v2->f1++; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + if(curredge->eed->v1->f1 > 2) return(0); else + if(curredge->eed->v2->f1 > 2) return(0); + } + + /*2nd test*/ + for(eed = G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + efa->e1->f1++; + efa->e2->f1++; + efa->e3->f1++; + if(efa->e4) efa->e4->f1++; + } + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ + if(curredge->eed->f1 > 2) return(0); + } + return(1); +} + +static int loop_bisect(Collection *edgecollection) +{ + + EditFace *efa, *sf1, *sf2; + EditEdge *eed, *sed; + CollectedEdge *curredge; + int totsf1, totsf2, unbalanced,balancededges; + + for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next) efa->f1 = 0; + + for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1; + + sf1 = sf2 = NULL; + sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed; + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(sf2) break; + else if(sf1){ + if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa; + } + else{ + if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa; + } + } + + if(sf1==NULL || sf2==NULL) + return(-1); + + if(!(sf1->e1->f1)) sf1->e1->f2 = 1; + if(!(sf1->e2->f1)) sf1->e2->f2 = 1; + if(!(sf1->e3->f1)) sf1->e3->f2 = 1; + if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1; + sf1->f1 = 1; + totsf1 = 1; + + if(!(sf2->e1->f1)) sf2->e1->f2 = 2; + if(!(sf2->e2->f1)) sf2->e2->f2 = 2; + if(!(sf2->e3->f1)) sf2->e3->f2 = 2; + if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2; + sf2->f1 = 2; + totsf2 = 1; + + /*do sf1*/ + unbalanced = 1; + while(unbalanced){ + unbalanced = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + balancededges = 0; + if(efa->f1 == 0){ + if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){ + balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1; + balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1; + balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1; + if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1; + if(balancededges){ + unbalanced = 1; + efa->f1 = 1; + totsf1++; + } + } + } + } + } + + /*do sf2*/ + unbalanced = 1; + while(unbalanced){ + unbalanced = 0; + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + balancededges = 0; + if(efa->f1 == 0){ + if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){ + balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2; + balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2; + balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2; + if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2; + if(balancededges){ + unbalanced = 1; + efa->f1 = 2; + totsf2++; + } + } + } + } + } + + if(totsf1 < totsf2) return(1); + else return(2); +} + +void loop_to_region(void) +{ + EditFace *efa; + ListBase allcollections={NULL,NULL}; + Collection *edgecollection; + int testflag; + + build_edgecollection(&allcollections); + + for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){ + if(validate_loop(edgecollection)){ + testflag = loop_bisect(edgecollection); + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ + if(efa->f1 == testflag){ + if(efa->f&SELECT) EM_select_face(efa, 0); + else EM_select_face(efa,1); + } + } + } + } + + for(efa=G.editMesh->faces.first; efa; efa=efa->next){ /*fix this*/ + if(efa->f&SELECT) EM_select_face(efa,1); + } + + countall(); + freecollections(&allcollections); + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + if (EM_texFaceCheck()) + allqueue(REDRAWIMAGE, 0); + BIF_undo_push("Edge Loop to Face Region"); +} + + +/* texface and vertex color editmode tools for the face menu */ + +void mesh_rotate_uvs(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, ccw; + MTFace *tf; + float u1, v1; + + if (!EM_texFaceCheck()) { + error("mesh has no uv/image layers"); + return; + } + + ccw = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + u1= tf->uv[0][0]; + v1= tf->uv[0][1]; + + if (ccw) { + if(efa->v4) { + tf->uv[0][0]= tf->uv[3][0]; + tf->uv[0][1]= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[3][1]= tf->uv[2][1]; + } else { + tf->uv[0][0]= tf->uv[2][0]; + tf->uv[0][1]= tf->uv[2][1]; + } + + tf->uv[2][0]= tf->uv[1][0]; + tf->uv[2][1]= tf->uv[1][1]; + + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + } else { + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + + if(efa->v4) { + tf->uv[2][0]= tf->uv[3][0]; + tf->uv[2][1]= tf->uv[3][1]; + + tf->uv[3][0]= u1; + tf->uv[3][1]= v1; + } + else { + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Rotate UV face"); + } +} + +void mesh_mirror_uvs(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, altaxis; + MTFace *tf; + float u1, v1; + + if (!EM_texFaceCheck()) { + error("mesh has no uv/image layers"); + return; + } + + altaxis = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (altaxis) { + u1= tf->uv[1][0]; + v1= tf->uv[1][1]; + if(efa->v4) { + + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + + u1= tf->uv[3][0]; + v1= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[0][0]; + tf->uv[3][1]= tf->uv[0][1]; + + tf->uv[0][0]= u1; + tf->uv[0][1]= v1; + } + else { + tf->uv[1][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + + } else { + u1= tf->uv[0][0]; + v1= tf->uv[0][1]; + if(efa->v4) { + + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + + u1= tf->uv[3][0]; + v1= tf->uv[3][1]; + + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[3][1]= tf->uv[2][1]; + + tf->uv[2][0]= u1; + tf->uv[2][1]= v1; + } + else { + tf->uv[0][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + tf->uv[1][0]= u1; + tf->uv[1][1]= v1; + } + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Mirror UV face"); + } +} + +void mesh_rotate_colors(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, ccw; + MCol tmpcol, *mcol; + if (!EM_vertColorCheck()) { + error("mesh has no color layers"); + return; + } + + ccw = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + tmpcol= mcol[0]; + + if (ccw) { + if(efa->v4) { + mcol[0]= mcol[3]; + mcol[3]= mcol[2]; + } else { + mcol[0]= mcol[2]; + } + mcol[2]= mcol[1]; + mcol[1]= tmpcol; + } else { + mcol[0]= mcol[1]; + mcol[1]= mcol[2]; + + if(efa->v4) { + mcol[2]= mcol[3]; + mcol[3]= tmpcol; + } + else + mcol[2]= tmpcol; + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Rotate Color face"); + } +} + +void mesh_mirror_colors(void) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + short change = 0, altaxis; + MCol tmpcol, *mcol; + if (!EM_vertColorCheck()) { + error("mesh has no color layers"); + return; + } + + altaxis = (G.qual == LR_SHIFTKEY); + + for(efa=em->faces.first; efa; efa=efa->next) { + if (efa->f & SELECT) { + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + if (altaxis) { + tmpcol= mcol[1]; + mcol[1]= mcol[2]; + mcol[2]= tmpcol; + + if(efa->v4) { + tmpcol= mcol[0]; + mcol[0]= mcol[3]; + mcol[3]= tmpcol; + } + } else { + tmpcol= mcol[0]; + mcol[0]= mcol[1]; + mcol[1]= tmpcol; + + if(efa->v4) { + tmpcol= mcol[2]; + mcol[2]= mcol[3]; + mcol[3]= tmpcol; + } + } + change = 1; + } + } + + if (change) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); + allqueue(REDRAWVIEW3D, 0); + BIF_undo_push("Mirror Color face"); + } +} + +#endif diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c new file mode 100644 index 00000000000..ac37041b073 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -0,0 +1,773 @@ +/* + * ***** 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) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_construct.c + * \ingroup bmesh + * + * BM construction functions. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "BKE_customdata.h" + +#include "DNA_meshdata_types.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#define SELECT 1 + +/* prototypes */ +static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMLoop *source_loop, BMLoop *target_loop); + +/* + * BMESH MAKE QUADTRIANGLE + * + * Creates a new quad or triangle from + * a list of 3 or 4 vertices. If nodouble + * equals 1, then a check is done to see + * if a face with these vertices already + * exists and returns it instead. If a pointer + * to an example face is provided, it's custom + * data and properties will be copied to the new + * face. + * + * Note that the winding of the face is determined + * by the order of the vertices in the vertex array + */ + +BMFace *BM_face_create_quad_tri(BMesh *bm, + BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + const BMFace *example, const int nodouble) +{ + BMVert *vtar[4] = {v1, v2, v3, v4}; + return BM_face_create_quad_tri_v(bm, vtar, v4 ? 4 : 3, example, nodouble); +} + +/* remove the edge array bits from this. Its not really needed? */ +BMFace *BM_face_create_quad_tri_v(BMesh *bm, BMVert **verts, int len, const BMFace *example, const int nodouble) +{ + BMEdge *edar[4] = {NULL}; + BMFace *f = NULL; + int overlap = 0; + + edar[0] = BM_edge_exists(verts[0], verts[1]); + edar[1] = BM_edge_exists(verts[1], verts[2]); + if (len == 4) { + edar[2] = BM_edge_exists(verts[2], verts[3]); + edar[3] = BM_edge_exists(verts[3], verts[0]); + } + else { + edar[2] = BM_edge_exists(verts[2], verts[0]); + } + + if (nodouble) { + /* check if face exists or overlaps */ + if (len == 4) { + overlap = BM_face_exists_overlap(bm, verts, len, &f); + } + else { + overlap = BM_face_exists_overlap(bm, verts, len, &f); + } + } + + /* make new face */ + if ((!f) && (!overlap)) { + if (!edar[0]) edar[0] = BM_edge_create(bm, verts[0], verts[1], NULL, FALSE); + if (!edar[1]) edar[1] = BM_edge_create(bm, verts[1], verts[2], NULL, FALSE); + if (len == 4) { + if (!edar[2]) edar[2] = BM_edge_create(bm, verts[2], verts[3], NULL, FALSE); + if (!edar[3]) edar[3] = BM_edge_create(bm, verts[3], verts[0], NULL, FALSE); + } + else { + if (!edar[2]) edar[2] = BM_edge_create(bm, verts[2], verts[0], NULL, FALSE); + } + + f = BM_face_create(bm, verts, edar, len, FALSE); + + if (example && f) { + BM_elem_attrs_copy(bm, bm, example, f); + } + } + + return f; +} + + +/* copies face data from shared adjacent faces */ +void BM_face_copy_shared(BMesh *bm, BMFace *f) +{ + BMIter iter; + BMLoop *l, *l2; + + if (!f) return; + + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&iter)) { + l2 = l->radial_next; + + if (l2 && l2 != l) { + if (l2->v == l->v) { + bm_loop_attrs_copy(bm, bm, l2, l); + } + else { + l2 = l2->next; + bm_loop_attrs_copy(bm, bm, l2, l); + } + } + } +} + +/* + * BMESH MAKE NGON + * + * Attempts to make a new Ngon from a list of edges. + * If nodouble equals one, a check for overlaps or existing + * + * The edges are not required to be ordered, simply to to form + * a single closed loop as a whole + * + * Note that while this function will work fine when the edges + * are already sorted, if the edges are always going to be sorted, + * BM_face_create should be considered over this function as it + * avoids some unnecessary work. + */ +BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble) +{ + BMEdge **edges2 = NULL; + BLI_array_staticdeclare(edges2, BM_NGON_STACK_SIZE); + BMVert **verts = NULL, *v; + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + BMFace *f = NULL; + BMEdge *e; + BMVert *ev1, *ev2; + int i, /* j, */ v1found, reverse; + + /* this code is hideous, yeek. I'll have to think about ways of + * cleaning it up. basically, it now combines the old BM_face_create_ngon + * _and_ the old bmesh_mf functions, so its kindof smashed together + * - joeedh */ + + if (!len || !v1 || !v2 || !edges || !bm) + return NULL; + + /* put edges in correct order */ + for (i = 0; i < len; i++) { + BM_ELEM_API_FLAG_ENABLE(edges[i], _FLAG_MF); + } + + ev1 = edges[0]->v1; + ev2 = edges[0]->v2; + + if (v1 == ev2) { + /* Swapping here improves performance and consistency of face + * structure in the special case that the edges are already in + * the correct order and winding */ + SWAP(BMVert *, ev1, ev2); + } + + BLI_array_append(verts, ev1); + v = ev2; + e = edges[0]; + do { + BMEdge *e2 = e; + + BLI_array_append(verts, v); + BLI_array_append(edges2, e); + + do { + e2 = bmesh_disk_nextedge(e2, v); + if (e2 != e && BM_ELEM_API_FLAG_TEST(e2, _FLAG_MF)) { + v = BM_edge_other_vert(e2, v); + break; + } + } while (e2 != e); + + if (e2 == e) + goto err; /* the edges do not form a closed loop */ + + e = e2; + } while (e != edges[0]); + + if (BLI_array_count(edges2) != len) { + goto err; /* we didn't use all edges in forming the boundary loop */ + } + + /* ok, edges are in correct order, now ensure they are going + * in the correct direction */ + v1found = reverse = FALSE; + for (i = 0; i < len; i++) { + if (BM_vert_in_edge(edges2[i], v1)) { + /* see if v1 and v2 are in the same edge */ + if (BM_vert_in_edge(edges2[i], v2)) { + /* if v1 is shared by the *next* edge, then the winding + * is incorrect */ + if (BM_vert_in_edge(edges2[(i + 1) % len], v1)) { + reverse = TRUE; + break; + } + } + + v1found = TRUE; + } + + if ((v1found == FALSE) && BM_vert_in_edge(edges2[i], v2)) { + reverse = TRUE; + break; + } + } + + if (reverse) { + for (i = 0; i < len / 2; i++) { + v = verts[i]; + verts[i] = verts[len - i - 1]; + verts[len - i - 1] = v; + } + } + + for (i = 0; i < len; i++) { + edges2[i] = BM_edge_exists(verts[i], verts[(i + 1) % len]); + if (!edges2[i]) { + goto err; + } + } + + f = BM_face_create(bm, verts, edges2, len, nodouble); + + /* clean up flags */ + for (i = 0; i < len; i++) { + BM_ELEM_API_FLAG_DISABLE(edges2[i], _FLAG_MF); + } + + BLI_array_free(verts); + BLI_array_free(edges2); + + return f; + +err: + for (i = 0; i < len; i++) { + BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF); + } + + BLI_array_free(verts); + BLI_array_free(edges2); + + return NULL; +} + + +/* bmesh_make_face_from_face(BMesh *bm, BMFace *source, BMFace *target) */ + + +/* + * REMOVE TAGGED XXX + * + * Called by operators to remove elements that they have marked for + * removal. + * + */ + +void BMO_remove_tagged_faces(BMesh *bm, const short oflag) +{ + BMFace *f; + BMIter iter; + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, f, oflag)) { + BM_face_kill(bm, f); + } + } +} + +void BMO_remove_tagged_edges(BMesh *bm, const short oflag) +{ + BMEdge *e; + BMIter iter; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, oflag)) { + BM_edge_kill(bm, e); + } + } +} + +void BMO_remove_tagged_verts(BMesh *bm, const short oflag) +{ + BMVert *v; + BMIter iter; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, oflag)) { + BM_vert_kill(bm, v); + } + } +} + +/*************************************************************/ +/* you need to make remove tagged verts/edges/faces + * api functions that take a filter callback..... + * and this new filter type will be for opstack flags. + * This is because the BM_remove_taggedXXX functions bypass iterator API. + * - Ops dont care about 'UI' considerations like selection state, hide state, ect. + * If you want to work on unhidden selections for instance, + * copy output from a 'select context' operator to another operator.... + */ + +static void bmo_remove_tagged_context_verts(BMesh *bm, const short oflag) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) { + if (BMO_elem_flag_test(bm, v, oflag)) { + /* Visit edge */ + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_VERT, v); e; e = BM_iter_step(&edges)) + BMO_elem_flag_enable(bm, e, oflag); + /* Visit face */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_VERT, v); f; f = BM_iter_step(&faces)) + BMO_elem_flag_enable(bm, f, oflag); + } + } + + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); + BMO_remove_tagged_verts(bm, oflag); +} + +static void bmo_remove_tagged_context_edges(BMesh *bm, const short oflag) +{ + BMEdge *e; + BMFace *f; + + BMIter edges; + BMIter faces; + + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BMO_elem_flag_test(bm, e, oflag)) { + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_EDGE, e); f; f = BM_iter_step(&faces)) { + BMO_elem_flag_enable(bm, f, oflag); + } + } + } + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); +} + +#define DEL_WIREVERT (1 << 10) + +/* warning, oflag applies to different types in some contexts, + * not just the type being removed */ +void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + switch (type) { + case DEL_VERTS: + { + bmo_remove_tagged_context_verts(bm, oflag); + + break; + } + case DEL_EDGES: + { + /* flush down to vert */ + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BMO_elem_flag_test(bm, e, oflag)) { + BMO_elem_flag_enable(bm, e->v1, oflag); + BMO_elem_flag_enable(bm, e->v2, oflag); + } + } + bmo_remove_tagged_context_edges(bm, oflag); + /* remove loose vertice */ + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) { + if (BMO_elem_flag_test(bm, v, oflag) && (!(v->e))) + BMO_elem_flag_enable(bm, v, DEL_WIREVERT); + } + BMO_remove_tagged_verts(bm, DEL_WIREVERT); + + break; + } + case DEL_EDGESFACES: + { + bmo_remove_tagged_context_edges(bm, oflag); + + break; + } + case DEL_ONLYFACES: + { + BMO_remove_tagged_faces(bm, oflag); + + break; + } + case DEL_ONLYTAGGED: + { + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); + BMO_remove_tagged_verts(bm, oflag); + + break; + } + case DEL_FACES: + { + /* go through and mark all edges and all verts of all faces for delet */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + if (BMO_elem_flag_test(bm, f, oflag)) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) + BMO_elem_flag_enable(bm, e, oflag); + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) + BMO_elem_flag_enable(bm, v, oflag); + } + } + /* now go through and mark all remaining faces all edges for keeping */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + if (!BMO_elem_flag_test(bm, f, oflag)) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) { + BMO_elem_flag_disable(bm, e, oflag); + } + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) { + BMO_elem_flag_disable(bm, v, oflag); + } + } + } + /* also mark all the vertices of remaining edges for keeping */ + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (!BMO_elem_flag_test(bm, e, oflag)) { + BMO_elem_flag_disable(bm, e->v1, oflag); + BMO_elem_flag_disable(bm, e->v2, oflag); + } + } + /* now delete marked face */ + BMO_remove_tagged_faces(bm, oflag); + /* delete marked edge */ + BMO_remove_tagged_edges(bm, oflag); + /* remove loose vertice */ + BMO_remove_tagged_verts(bm, oflag); + + break; + } + case DEL_ALL: + { + /* does this option even belong in here? */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) + BMO_elem_flag_enable(bm, f, oflag); + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) + BMO_elem_flag_enable(bm, e, oflag); + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) + BMO_elem_flag_enable(bm, v, oflag); + + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); + BMO_remove_tagged_verts(bm, oflag); + + break; + } + } +} +/*************************************************************/ + + +static void bm_vert_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMVert *source_vertex, BMVert *target_vertex) +{ + if ((source_mesh == target_mesh) && (source_vertex == target_vertex)) { + return; + } + copy_v3_v3(target_vertex->no, source_vertex->no); + CustomData_bmesh_free_block(&target_mesh->vdata, &target_vertex->head.data); + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, + source_vertex->head.data, &target_vertex->head.data); +} + +static void bm_edge_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMEdge *source_edge, BMEdge *target_edge) +{ + if ((source_mesh == target_mesh) && (source_edge == target_edge)) { + return; + } + CustomData_bmesh_free_block(&target_mesh->edata, &target_edge->head.data); + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, + source_edge->head.data, &target_edge->head.data); +} + +static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMLoop *source_loop, BMLoop *target_loop) +{ + if ((source_mesh == target_mesh) && (source_loop == target_loop)) { + return; + } + CustomData_bmesh_free_block(&target_mesh->ldata, &target_loop->head.data); + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, + source_loop->head.data, &target_loop->head.data); +} + +static void bm_face_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMFace *source_face, BMFace *target_face) +{ + if ((source_mesh == target_mesh) && (source_face == target_face)) { + return; + } + copy_v3_v3(target_face->no, source_face->no); + CustomData_bmesh_free_block(&target_mesh->pdata, &target_face->head.data); + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, + source_face->head.data, &target_face->head.data); + target_face->mat_nr = source_face->mat_nr; +} + +/* BMESH_TODO: Special handling for hide flags? */ + +void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target) +{ + const BMHeader *sheader = source; + BMHeader *theader = target; + + if (sheader->htype != theader->htype) + return; + + /* First we copy select */ + if (BM_elem_flag_test(source, BM_ELEM_SELECT)) BM_elem_select_set(target_mesh, target, TRUE); + + /* Now we copy flags */ + theader->hflag = sheader->hflag; + + /* Copy specific attributes */ + if (theader->htype == BM_VERT) + bm_vert_attrs_copy(source_mesh, target_mesh, (const BMVert *)source, (BMVert *)target); + else if (theader->htype == BM_EDGE) + bm_edge_attrs_copy(source_mesh, target_mesh, (const BMEdge *)source, (BMEdge *)target); + else if (theader->htype == BM_LOOP) + bm_loop_attrs_copy(source_mesh, target_mesh, (const BMLoop *)source, (BMLoop *)target); + else if (theader->htype == BM_FACE) + bm_face_attrs_copy(source_mesh, target_mesh, (const BMFace *)source, (BMFace *)target); +} + +BMesh *BM_mesh_copy(BMesh *bmold) +{ + BMesh *bm; + BMVert *v, *v2, **vtable = NULL; + BMEdge *e, *e2, **edges = NULL, **etable = NULL; + BLI_array_declare(edges); + BMLoop *l, /* *l2, */ **loops = NULL; + BLI_array_declare(loops); + BMFace *f, *f2, **ftable = NULL; + BMEditSelection *ese; + BMIter iter, liter; + int i, j; + + /* allocate a bmesh */ + bm = BM_mesh_create(bmold->ob, bm_mesh_allocsize_default); + + CustomData_copy(&bmold->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bmold->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bmold->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bmold->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]); + CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]); + CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]); + CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]); + + vtable = MEM_mallocN(sizeof(BMVert *) * bmold->totvert, "BM_mesh_copy vtable"); + etable = MEM_mallocN(sizeof(BMEdge *) * bmold->totedge, "BM_mesh_copy etable"); + ftable = MEM_mallocN(sizeof(BMFace *) * bmold->totface, "BM_mesh_copy ftable"); + + v = BM_iter_new(&iter, bmold, BM_VERTS_OF_MESH, NULL); + for (i = 0; v; v = BM_iter_step(&iter), i++) { + v2 = BM_vert_create(bm, v->co, NULL); /* copy between meshes so cant use 'example' argument */ + BM_elem_attrs_copy(bmold, bm, v, v2); + vtable[i] = v2; + BM_elem_index_set(v, i); /* set_inline */ + BM_elem_index_set(v2, i); /* set_inline */ + } + bmold->elem_index_dirty &= ~BM_VERT; + bm->elem_index_dirty &= ~BM_VERT; + + /* safety check */ + BLI_assert(i == bmold->totvert); + + e = BM_iter_new(&iter, bmold, BM_EDGES_OF_MESH, NULL); + for (i = 0; e; e = BM_iter_step(&iter), i++) { + e2 = BM_edge_create(bm, + vtable[BM_elem_index_get(e->v1)], + vtable[BM_elem_index_get(e->v2)], + e, FALSE); + + BM_elem_attrs_copy(bmold, bm, e, e2); + etable[i] = e2; + BM_elem_index_set(e, i); /* set_inline */ + BM_elem_index_set(e2, i); /* set_inline */ + } + bmold->elem_index_dirty &= ~BM_EDGE; + bm->elem_index_dirty &= ~BM_EDGE; + + /* safety check */ + BLI_assert(i == bmold->totedge); + + f = BM_iter_new(&iter, bmold, BM_FACES_OF_MESH, NULL); + for (i = 0; f; f = BM_iter_step(&iter), i++) { + BM_elem_index_set(f, i); /* set_inline */ + + BLI_array_empty(loops); + BLI_array_empty(edges); + BLI_array_growitems(loops, f->len); + BLI_array_growitems(edges, f->len); + + l = BM_iter_new(&liter, bmold, BM_LOOPS_OF_FACE, f); + for (j = 0; j < f->len; j++, l = BM_iter_step(&liter)) { + loops[j] = l; + edges[j] = etable[BM_elem_index_get(l->e)]; + } + + v = vtable[BM_elem_index_get(loops[0]->v)]; + v2 = vtable[BM_elem_index_get(loops[1]->v)]; + + if (!bmesh_verts_in_edge(v, v2, edges[0])) { + v = vtable[BM_elem_index_get(loops[BLI_array_count(loops) - 1]->v)]; + v2 = vtable[BM_elem_index_get(loops[0]->v)]; + } + + f2 = BM_face_create_ngon(bm, v, v2, edges, f->len, FALSE); + if (!f2) + continue; + /* use totface incase adding some faces fails */ + BM_elem_index_set(f2, (bm->totface - 1)); /* set_inline */ + + ftable[i] = f2; + + BM_elem_attrs_copy(bmold, bm, f, f2); + copy_v3_v3(f2->no, f->no); + + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f2); + for (j = 0; j < f->len; j++, l = BM_iter_step(&liter)) { + BM_elem_attrs_copy(bmold, bm, loops[j], l); + } + + if (f == bmold->act_face) bm->act_face = f2; + } + bmold->elem_index_dirty &= ~BM_FACE; + bm->elem_index_dirty &= ~BM_FACE; + + /* safety check */ + BLI_assert(i == bmold->totface); + + /* copy over edit selection history */ + for (ese = bmold->selected.first; ese; ese = ese->next) { + void *ele = NULL; + + if (ese->htype == BM_VERT) + ele = vtable[BM_elem_index_get(ese->data)]; + else if (ese->htype == BM_EDGE) + ele = etable[BM_elem_index_get(ese->data)]; + else if (ese->htype == BM_FACE) { + ele = ftable[BM_elem_index_get(ese->data)]; + } + else { + BLI_assert(0); + } + + if (ele) + BM_select_history_store(bm, ele); + } + + MEM_freeN(etable); + MEM_freeN(vtable); + MEM_freeN(ftable); + + BLI_array_free(loops); + BLI_array_free(edges); + + return bm; +} + +/* ME -> BM */ +char BM_vert_flag_from_mflag(const char meflag) +{ + return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) | + ((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0) + ); +} +char BM_edge_flag_from_mflag(const short meflag) +{ + return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) | + ((meflag & ME_SEAM) ? BM_ELEM_SEAM : 0) | + ((meflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */ + ((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0) + ); +} +char BM_face_flag_from_mflag(const char meflag) +{ + return ( ((meflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) | + ((meflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) | + ((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0) + ); +} + +/* BM -> ME */ +char BM_vert_flag_to_mflag(BMVert *eve) +{ + const char hflag = eve->head.hflag; + + return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) | + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) + ); +} +short BM_edge_flag_to_mflag(BMEdge *eed) +{ + const char hflag = eed->head.hflag; + + return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) | + ((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) | + ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) | + ((BM_edge_is_wire(NULL, eed)) ? ME_LOOSEEDGE : 0) | /* not typical */ + (ME_EDGEDRAW | ME_EDGERENDER) + ); +} +char BM_face_flag_to_mflag(BMFace *efa) +{ + const char hflag = efa->head.hflag; + + return ( ((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) | + ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) + ); +} diff --git a/source/blender/bmesh/intern/bmesh_eulers.c b/source/blender/bmesh/intern/bmesh_eulers.c new file mode 100644 index 00000000000..b1ba4ca3176 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_eulers.c @@ -0,0 +1,1215 @@ +/*some of this may come back, such as split face or split edge, if necassary for speed*/ + +#if 0 +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_eulers.c + * \ingroup bmesh + * + * BM Euler construction API. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_customdata.h" +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" + +/********************************************************* + * "Euler API" * + * * + * * + * Primitive construction operators for mesh tools. * + * * + **********************************************************/ + + +/* + The functions in this file represent the 'primitive' or 'atomic' operators that + mesh tools use to manipulate the topology of the structure.* The purpose of these + functions is to provide a trusted set of operators to manipulate the mesh topology + and which can also be combined together like building blocks to create more + sophisticated tools. It needs to be stressed that NO manipulation of an existing + mesh structure should be done outside of these functions. + + In the BM system, each euler is named by an ancronym which describes what it actually does. + Furthermore each Euler has a logical inverse. An important design criteria of all Eulers is that + through a Euler's logical inverse you can 'undo' an operation. (Special note should + be taken of bmesh_loop_reverse, which is its own inverse). + + bmesh_MF/KF: Make Face and Kill Face + bmesh_ME/KE: Make Edge and Kill Edge + bmesh_MV/KV: Make Vert and Kill Vert + bmesh_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert + bmesh_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge + bmesh_loop_reverse: Reverse a Polygon's loop cycle. (used for flip normals for one) + + Using a combination of these eleven eulers any non-manifold modelling operation can be achieved. + Each Euler operator has a detailed explanation of what is does in the comments preceding its + code. + + *The term "Euler Operator" is actually a misnomer when referring to a non-manifold + data structure. Its use is in keeping with the convention established by others. + + BMESH_TODO: + -Make seperate 'debug levels' of validation + -Add in the UnglueFaceRegionMakeVert and GlueFaceRegionKillVert eulers. + + NOTE: + -The functions in this file are notoriously difficult to debug and even understand sometimes. + better code comments would be nice.... + +*/ + + +/*MAKE Eulers*/ + +/** + * bmesh_MV + * + * MAKE VERT EULER: + * + * Makes a single loose vertex. + * + * Returns - + * A BMVert pointer. + */ + +BMVert *bmesh_mv(BMesh *bm, const float vec[3]) +{ + BMVert *v = bmesh_addvertlist(bm, NULL); + copy_v3_v3(v->co,vec); + return v; +} + +/** + * bmesh_ME + * + * MAKE EDGE EULER: + * + * Makes a single wire edge between two vertices. + * If the caller does not want there to be duplicate + * edges between the vertices, it is up to them to check + * for this condition beforehand. + * + * Returns - + * A BMEdge pointer. + */ + +BMEdge *bmesh_me(BMesh *bm, BMVert *v1, BMVert *v2) +{ + BMEdge *e=NULL; + BMNode *d1=NULL, *d2=NULL; + int valance1=0, valance2=0, edok; + + /*edge must be between two distinct vertices...*/ + if(v1 == v2) return NULL; + + #ifndef bmesh_FASTEULER + /*count valance of v1*/ + if(v1->e) { + d1 = bmesh_disk_getpointer(v1->e,v1); + if(d1) valance1 = bmesh_cycle_length(d1); + else bmesh_error(); + } + if(v2->e) { + d2 = bmesh_disk_getpointer(v2->e,v2); + if(d2) valance2 = bmesh_cycle_length(d2); + else bmesh_error(); + } + #endif + + /*go ahead and add*/ + e = bmesh_addedgelist(bm, v1, v2, NULL); + bmesh_disk_append_edge(e, e->v1); + bmesh_disk_append_edge(e, e->v2); + + #ifndef bmesh_FASTEULER + /*verify disk cycle lengths*/ + d1 = bmesh_disk_getpointer(e, e->v1); + edok = bmesh_cycle_validate(valance1+1, d1); + if(!edok) bmesh_error(); + d2 = bmesh_disk_getpointer(e, e->v2); + edok = bmesh_cycle_validate(valance2+1, d2); + if(!edok) bmesh_error(); + + /*verify that edge actually made it into the cycle*/ + edok = bmesh_disk_hasedge(v1, e); + if(!edok) bmesh_error(); + edok = bmesh_disk_hasedge(v2, e); + if(!edok) bmesh_error(); + #endif + return e; +} + + + +/** + * bmesh_MF + * + * MAKE FACE EULER: + * Takes a list of edge pointers which form a closed loop and makes a face + * from them. The first edge in elist is considered to be the start of the + * polygon, and v1 and v2 are its vertices and determine the winding of the face + * Other than the first edge, no other assumptions are made about the order of edges + * in the elist array. To verify that it is a single closed loop and derive the correct + * order a simple series of verifications is done and all elements are visited. + * + * Returns - + * A BMFace pointer + */ + +#define MF_CANDIDATE 1 +#define MF_VISITED 2 +#define MF_TAKEN 4 + +BMFace *bmesh_mf(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **elist, int len) +{ + BMFace *f = NULL; + BMEdge *curedge; + BMVert *curvert, *tv, **vlist; + int i, j, done, cont, edok; + + if(len < 2) return NULL; + + /*make sure that v1 and v2 are in elist[0]*/ + //if(bmesh_verts_in_edge(v1,v2,elist[0]) == 0) + // return NULL; + + /*clear euler flags*/ + for(i=0;i<len;i++) { + BMNode *diskbase; + BMEdge *curedge; + BMVert *v1; + int j; + + for (j=0; j<2; j++) { + int a, len=0; + + v1 = j ? elist[i]->v2 : elist[i]->v1; + diskbase = bmesh_disk_getpointer(v1->e, v1); + len = bmesh_cycle_length(diskbase); + + for(a=0,curedge=v1->e;a<len;a++,curedge = bmesh_disk_nextedge(curedge,v1)) { + curedge->head.eflag1 = curedge->head.eflag2 = 0; + } + } + } + + for(i=0;i<len;i++) { + elist[i]->head.eflag1 |= MF_CANDIDATE; + + /*if elist[i] has a loop, count its radial length*/ + if(elist[i]->loop) elist[i]->head.eflag2 = bmesh_cycle_length(&(elist[i]->l->radial)); + else elist[i]->head.eflag2 = 0; + } + + /* For each vertex in each edge, it must have exactly two MF_CANDIDATE edges attached to it + Note that this does not gauruntee that face is a single closed loop. At best it gauruntees + that elist contains a finite number of seperate closed loops. + */ +// for(i=0; i<len; i++) { +// edok = bmesh_disk_count_edgeflag(elist[i]->v1, MF_CANDIDATE, 0); +// if(edok != 2) return NULL; +// edok = bmesh_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0); +// if(edok != 2) return NULL; +// } + + /*set start edge, start vert and target vert for our loop traversal*/ + curedge = elist[0]; + tv = v1; + curvert = v2; + + if(bm->vtarlen < len) { + if (bm->vtar) MEM_freeN(bm->vtar); + bm->vtar = MEM_callocN(sizeof(BMVert *)* len, "BM Vert pointer array"); + bm->vtarlen = len; + } + /*insert tv into vlist since its the first vertex in face*/ + + i=0; + vlist=bm->vtar; + vlist[i] = tv; + + /* Basic procedure: Starting with curv we find the edge in it's disk cycle which hasn't + been visited yet. When we do, we put curv in a linked list and find the next MF_CANDIDATE + edge, loop until we find TV. We know TV is reachable because of test we did earlier. + */ + done=0; + while(!done) { + /*add curvert to vlist*/ + /*insert some error cheking here for overflows*/ + i++; + vlist[i] = curvert; + + /*mark curedge as visited*/ + curedge->head.eflag1 |= MF_VISITED; + + /*find next edge and vert*/ + curedge = bmesh_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0); + curvert = bmesh_edge_getothervert(curedge, curvert); + if(curvert == tv) { + curedge->head.eflag1 |= MF_VISITED; + done=1; + } + } + + /* Verify that all edges have been visited It's possible that we did reach tv + from sv, but that several unconnected loops were passed in via elist. + */ + cont=1; +// for(i=0; i<len; i++) { +// if((elist[i]->head.eflag1 & MF_VISITED) == 0) cont = 0; +// } + + /*if we get this far, its ok to allocate the face and add the loops*/ + if(cont) { + BMLoop *l; + BMEdge *e; + f = bmesh_addpolylist(bm, NULL); + f->len = len; + for(i=0;i<len;i++) { + curvert = vlist[i]; + l = bmesh_create_loop(bm,curvert,NULL,f,NULL); + if(!(f->loopbase)) f->lbase = l; + bmesh_cycle_append(f->lbase, l); + } + + /*take care of edge pointers and radial cycle*/ + for(i=0, l = f->loopbase; i<len; i++, l= l->next) { + e = NULL; + if(l == f->loopbase) e = elist[0]; /*first edge*/ + + else {/*search elist for others*/ + for(j=1; j<len; j++) { + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, elist[j]); + if(edok) { + e = elist[j]; + break; + } + } + } + l->e = e; /*set pointer*/ + bmesh_radial_append(e, l); /*append into radial*/ + } + + f->len = len; + + /*Validation Loop cycle*/ + edok = bmesh_cycle_validate(len, f->lbase); + if(!edok) bmesh_error(); + for(i=0, l = f->loopbase; i<len; i++, l=((l->next))) { + /*validate loop vert pointers*/ + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, l->e); + if(!edok) bmesh_error(); + /*validate the radial cycle of each edge*/ + edok = bmesh_cycle_length(&(l->radial)); + if(edok != (l->e->head.eflag2 + 1)) bmesh_error(); + } + } + + for(i=0;i<len;i++) elist[i]->head.eflag1=elist[i]->head.eflag2 = 0; + return f; +} + +/* KILL Eulers */ + +/** + * bmesh_KV + * + * KILL VERT EULER: + * + * Kills a single loose vertex. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_kv(BMesh *bm, BMVert *v) +{ + if(v->e == NULL) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) bm->totvertsel--; + + BLI_remlink(&(bm->verts), &(v->head)); + bmesh_free_vert(bm,v); + return 1; + } + return 0; +} + +/** + * bmesh_KE + * + * KILL EDGE EULER: + * + * Kills a wire edge. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_ke(BMesh *bm, BMEdge *e) +{ + int edok; + + /*Make sure that no faces!*/ + if(e->l == NULL) { + bmesh_disk_remove_edge(e, e->v1); + bmesh_disk_remove_edge(e, e->v2); + + /*verify that edge out of disk*/ + edok = bmesh_disk_hasedge(e->v1, e); + if(edok) bmesh_error(); + edok = bmesh_disk_hasedge(e->v2, e); + if(edok) bmesh_error(); + + /*remove and deallocate*/ + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel--; + BLI_remlink(&(bm->edges), &(e->head)); + bmesh_free_edge(bm, e); + return 1; + } + return 0; +} + +/** + * bmesh_KF + * + * KILL FACE EULER: + * + * The logical inverse of bmesh_MF. + * Kills a face and removes each of its loops from the radial that it belongs to. + * + * Returns - + * 1 for success, 0 for failure. +*/ + +int bmesh_kf(BMesh *bm, BMFace *bply) +{ + BMLoop *newbase,*oldbase, *curloop; + int i,len=0; + + /*add validation to make sure that radial cycle is cleaned up ok*/ + /*deal with radial cycle first*/ + len = bmesh_cycle_length(bply->lbase); + for(i=0, curloop=bply->loopbase; i < len; i++, curloop = ((curloop->next))) + bmesh_radial_remove_loop(curloop, curloop->e); + + /*now deallocate the editloops*/ + for(i=0; i < len; i++) { + newbase = ((bply->lbase->next)); + oldbase = bply->lbase; + bmesh_cycle_remove(oldbase, oldbase); + bmesh_free_loop(bm, oldbase); + bply->loopbase = newbase; + } + + if (BM_elem_flag_test(bply, BM_ELEM_SELECT)) bm->totfacesel--; + BLI_remlink(&(bm->polys), &(bply->head)); + bmesh_free_poly(bm, bply); + return 1; +} + +/*SPLIT Eulers*/ + +/** + * bmesh_SEMV + * + * SPLIT EDGE MAKE VERT: + * Takes a given edge and splits it into two, creating a new vert. + * + * + * Before: OV---------TV + * After: OV----NV---TV + * + * Returns - + * BMVert pointer. + * +*/ + +BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re) +{ + BMVert *nv, *ov; + BMNode *diskbase; + BMEdge *ne; + int i, edok, valance1=0, valance2=0; + + if(bmesh_vert_in_edge(e,tv) == 0) return NULL; + ov = bmesh_edge_getothervert(e,tv); + //v2 = tv; + + /*count valance of v1*/ + diskbase = bmesh_disk_getpointer(e, ov); + valance1 = bmesh_cycle_length(diskbase); + /*count valance of v2*/ + diskbase = bmesh_disk_getpointer(e, tv); + valance2 = bmesh_cycle_length(diskbase); + + nv = bmesh_addvertlist(bm, tv); + ne = bmesh_addedgelist(bm, nv, tv, e); + + //e->v2 = nv; + /*remove e from v2's disk cycle*/ + bmesh_disk_remove_edge(e, tv); + /*swap out tv for nv in e*/ + bmesh_edge_swapverts(e, tv, nv); + /*add e to nv's disk cycle*/ + bmesh_disk_append_edge(e, nv); + /*add ne to nv's disk cycle*/ + bmesh_disk_append_edge(ne, nv); + /*add ne to tv's disk cycle*/ + bmesh_disk_append_edge(ne, tv); + /*verify disk cycles*/ + diskbase = bmesh_disk_getpointer(ov->e,ov); + edok = bmesh_cycle_validate(valance1, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(tv->e,tv); + edok = bmesh_cycle_validate(valance2, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(nv->e,nv); + edok = bmesh_cycle_validate(2, diskbase); + if(!edok) bmesh_error(); + + /*Split the radial cycle if present*/ + if(e->l) { + BMLoop *nl,*l; + BMNode *radEBase=NULL, *radNEBase=NULL; + int radlen = bmesh_cycle_length(&(e->l->radial)); + /*Take the next loop. Remove it from radial. Split it. Append to appropriate radials.*/ + while(e->l) { + l=e->l; + l->f->len++; + bmesh_radial_remove_loop(l,e); + + nl = bmesh_create_loop(bm,NULL,NULL,l->f,l); + nl->prev = (BMHeader*)l; + nl->next = (BMHeader*)(l->next); + nl->prev->next = (BMHeader*)nl; + nl->next->prev = (BMHeader*)nl; + nl->v = nv; + + /*assign the correct edge to the correct loop*/ + if(bmesh_verts_in_edge(nl->v, ((nl->next))->v, e)) { + nl->e = e; + l->e = ne; + + /*append l into ne's rad cycle*/ + if(!radNEBase) { + radNEBase = &(l->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + + if(!radEBase) { + radEBase = &(nl->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + + bmesh_cycle_append(radEBase,&(nl->radial)); + bmesh_cycle_append(radNEBase,&(l->radial)); + + } + else if(bmesh_verts_in_edge(nl->v,((nl->next))->v,ne)) { + nl->e = ne; + l->e = e; + + if(!radNEBase) { + radNEBase = &(nl->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + if(!radEBase) { + radEBase = &(l->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + bmesh_cycle_append(radEBase,&(l->radial)); + bmesh_cycle_append(radNEBase,&(nl->radial)); + } + + } + + e->l = radEBase->data; + ne->l = radNEBase->data; + + /*verify length of radial cycle*/ + edok = bmesh_cycle_validate(radlen,&(e->l->radial)); + if(!edok) bmesh_error(); + edok = bmesh_cycle_validate(radlen,&(ne->l->radial)); + if(!edok) bmesh_error(); + + /*verify loop->v and loop->next->v pointers for e*/ + for(i=0,l=e->l; i < radlen; i++, l = l->radial_next) { + if(!(l->e == e)) bmesh_error(); + if(!(l->radial.data == l)) bmesh_error(); + if( ((l->prev))->e != ne && ((l->next))->e != ne) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, e); + if(!edok) bmesh_error(); + if(l->v == ((l->next))->v) bmesh_error(); + if(l->e == ((l->next))->e) bmesh_error(); + /*verify loop cycle for kloop->f*/ + edok = bmesh_cycle_validate(l->f->len, l->f->lbase); + if(!edok) bmesh_error(); + } + /*verify loop->v and loop->next->v pointers for ne*/ + for(i=0,l=ne->l; i < radlen; i++, l = l->radial_next) { + if(!(l->e == ne)) bmesh_error(); + if(!(l->radial.data == l)) bmesh_error(); + if( ((l->prev))->e != e && ((l->next))->e != e) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, ne); + if(!edok) bmesh_error(); + if(l->v == ((l->next))->v) bmesh_error(); + if(l->e == ((l->next))->e) bmesh_error(); + /*verify loop cycle for kloop->f. Redundant*/ + edok = bmesh_cycle_validate(l->f->len, l->f->lbase); + if(!edok) bmesh_error(); + } + } + + if(re) *re = ne; + return nv; +} + +/** + * bmesh_SFME + * + * SPLIT FACE MAKE EDGE: + * + * Takes as input two vertices in a single face. An edge is created which divides the original face + * into two distinct regions. One of the regions is assigned to the original face and it is closed off. + * The second region has a new face assigned to it. + * + * Examples: + * + * Before: After: + * ---------- ---------- + * | | | | + * | | | f1 | + * v1 f1 v2 v1======v2 + * | | | f2 | + * | | | | + * ---------- ---------- + * + * Note that the input vertices can be part of the same edge. This will result in a two edged face. + * This is desirable for advanced construction tools and particularly essential for edge bevel. Because + * of this it is up to the caller to decide what to do with the extra edge. + * + * Note that the tesselator abuses eflag2 while using this euler! (don't ever ever do this....) + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **rl) +{ + + BMFace *f2; + BMLoop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL; + BMEdge *e; + int i, len, f1len, f2len; + + + /*verify that v1 and v2 are in face.*/ + len = bmesh_cycle_length(f->lbase); + for(i = 0, curloop = f->loopbase; i < len; i++, curloop = ((curloop->next)) ) { + if(curloop->v == v1) v1loop = curloop; + else if(curloop->v == v2) v2loop = curloop; + } + + if(!v1loop || !v2loop) return NULL; + + /*allocate new edge between v1 and v2*/ + e = bmesh_addedgelist(bm, v1, v2,NULL); + bmesh_disk_append_edge(e, v1); + bmesh_disk_append_edge(e, v2); + + f2 = bmesh_addpolylist(bm,f); + f1loop = bmesh_create_loop(bm,v2,e,f,v2loop); + f2loop = bmesh_create_loop(bm,v1,e,f2,v1loop); + + f1loop->prev = v2loop->prev; + f2loop->prev = v1loop->prev; + v2loop->prev->next = (BMHeader*)f1loop; + v1loop->prev->next = (BMHeader*)f2loop; + + f1loop->next = (BMHeader*)v1loop; + f2loop->next = (BMHeader*)v2loop; + v1loop->prev = (BMHeader*)f1loop; + v2loop->prev = (BMHeader*)f2loop; + + f2->loopbase = f2loop; + f->loopbase = f1loop; + + /*validate both loops*/ + /*I dont know how many loops are supposed to be in each face at this point! FIXME!*/ + + /*go through all of f2's loops and make sure they point to it properly.*/ + f2len = bmesh_cycle_length(f2->lbase); + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->f = f2; + + /*link up the new loops into the new edges radial*/ + bmesh_radial_append(e, f1loop); + bmesh_radial_append(e, f2loop); + + + f2->len = f2len; + + f1len = bmesh_cycle_length(f->lbase); + f->len = f1len; + + if(rl) *rl = f2loop; + return f2; +} + + +/** + * bmesh_JEKV + * + * JOIN EDGE KILL VERT: + * Takes a an edge and pointer to one of its vertices and collapses + * the edge on that vertex. + * + * Before: OE KE + * ------- ------- + * | || | + * OV KV TV + * + * + * After: OE + * --------------- + * | | + * OV TV + * + * + * Restrictions: + * KV is a vertex that must have a valance of exactly two. Furthermore + * both edges in KV's disk cycle (OE and KE) must be unique (no double + * edges). + * + * It should also be noted that this euler has the possibility of creating + * faces with just 2 edges. It is up to the caller to decide what to do with + * these faces. + * + * Returns - + * 1 for success, 0 for failure. + */ +int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + BMEdge *oe; + BMVert *ov, *tv; + BMNode *diskbase; + BMLoop *killoop,*nextl; + int len,radlen=0, halt = 0, i, valance1, valance2,edok; + + if(bmesh_vert_in_edge(ke,kv) == 0) return 0; + diskbase = bmesh_disk_getpointer(kv->e, kv); + len = bmesh_cycle_length(diskbase); + + if(len == 2) { + oe = bmesh_disk_nextedge(ke, kv); + tv = bmesh_edge_getothervert(ke, kv); + ov = bmesh_edge_getothervert(oe, kv); + halt = bmesh_verts_in_edge(kv, tv, oe); //check for double edges + + if(halt) return 0; + else { + + /*For verification later, count valance of ov and tv*/ + diskbase = bmesh_disk_getpointer(ov->e, ov); + valance1 = bmesh_cycle_length(diskbase); + diskbase = bmesh_disk_getpointer(tv->e, tv); + valance2 = bmesh_cycle_length(diskbase); + + /*remove oe from kv's disk cycle*/ + bmesh_disk_remove_edge(oe,kv); + /*relink oe->kv to be oe->tv*/ + bmesh_edge_swapverts(oe, kv, tv); + /*append oe to tv's disk cycle*/ + bmesh_disk_append_edge(oe, tv); + /*remove ke from tv's disk cycle*/ + bmesh_disk_remove_edge(ke, tv); + + + + /*deal with radial cycle of ke*/ + if(ke->l) { + /*first step, fix the neighboring loops of all loops in ke's radial cycle*/ + radlen = bmesh_cycle_length(&(ke->l->radial)); + for(i=0,killoop = ke->l; i<radlen; i++, killoop = bmesh_radial_nextloop(killoop)) { + /*relink loops and fix vertex pointer*/ + killoop->next->prev = killoop->prev; + killoop->prev->next = killoop->next; + if( ((killoop->next))->v == kv) ((killoop->next))->v = tv; + + /*fix len attribute of face*/ + killoop->f->len--; + if(killoop->f->loopbase == killoop) killoop->f->lbase = ((killoop->next)); + } + /*second step, remove all the hanging loops attached to ke*/ + killoop = ke->l; + radlen = bmesh_cycle_length(&(ke->l->radial)); + /*make sure we have enough room in bm->lpar*/ + if(bm->lparlen < radlen) { + MEM_freeN(bm->lpar); + bm->lpar = MEM_callocN(sizeof(BMLoop *)* radlen, "BM Loop pointer array"); + bm->lparlen = bm->lparlen * radlen; + } + /*this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well...*/ + i=0; + while(i<radlen) { + bm->lpar[i] = killoop; + killoop = killoop->radial_next; + i++; + } + i=0; + while(i<radlen) { + bmesh_free_loop(bm,bm->lpar[i]); + i++; + } + /*Validate radial cycle of oe*/ + edok = bmesh_cycle_validate(radlen,&(oe->l->radial)); + + } + + + /*Validate disk cycles*/ + diskbase = bmesh_disk_getpointer(ov->e,ov); + edok = bmesh_cycle_validate(valance1, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(tv->e,tv); + edok = bmesh_cycle_validate(valance2, diskbase); + if(!edok) bmesh_error(); + + /*Validate loop cycle of all faces attached to oe*/ + for(i=0,nextl = oe->l; i<radlen; i++, nextl = bmesh_radial_nextloop(nextl)) { + edok = bmesh_cycle_validate(nextl->f->len,nextl->f->lbase); + if(!edok) bmesh_error(); + } + /*deallocate edge*/ + BLI_remlink(&(bm->edges), &(ke->head)); + bmesh_free_edge(bm, ke); + /*deallocate vertex*/ + BLI_remlink(&(bm->verts), &(kv->head)); + bmesh_free_vert(bm, kv); + return 1; + } + } + return 0; +} + + +/** + * bmesh_loop_reverse + * + * FLIP FACE EULER + * + * Changes the winding order of a face from CW to CCW or vice versa. + * This euler is a bit peculiar in compairson to others as it is its + * own inverse. + * + * BMESH_TODO: reinsert validation code. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_loop_reverse(BMesh *bm, BMFace *f) +{ + BMLoop *l = f->loopbase, *curloop, *oldprev, *oldnext; + int i, j, edok, len = 0; + + len = bmesh_cycle_length(l); + if(bm->edarlen < len) { + MEM_freeN(bm->edar); + bm->edar = MEM_callocN(sizeof(BMEdge *)* len, "BM Edge pointer array"); + bm->edarlen = len; + } + + for(i=0, curloop = l; i< len; i++, curloop= ((curloop->next)) ) { + curloop->e->head.eflag1 = 0; + curloop->e->head.eflag2 = bmesh_cycle_length(&curloop->radial); + bmesh_radial_remove_loop(curloop, curloop->e); + /*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/ + curloop->radial.next = curloop->radial.prev = NULL; + bm->edar[i] = curloop->e; + } + + /*actually reverse the loop. This belongs in bmesh_cycle_reverse!*/ + for(i=0, curloop = l; i < len; i++) { + oldnext = ((curloop->next)); + oldprev = ((curloop->prev)); + curloop->next = (BMHeader*)oldprev; + curloop->prev = (BMHeader*)oldnext; + curloop = oldnext; + } + + if(len == 2) { //two edged face + //do some verification here! + l->e = bm->edar[1]; + ((l->next))->e = bm->edar[0]; + } + else { + for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) { + edok = 0; + for(j=0; j < len; j++) { + edok = bmesh_verts_in_edge(curloop->v, ((curloop->next))->v, bm->edar[j]); + if(edok) { + curloop->e = bm->edar[j]; + break; + } + } + } + } + /*rebuild radial*/ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next ) bmesh_radial_append(curloop->e, curloop); + + /*validate radial*/ + for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) { + edok = bmesh_cycle_validate(curloop->e->head.eflag2, &(curloop->radial)); + if(!edok) { + bmesh_error(); + } + } + return 1; +} + +/** + * bmesh_JFKE + * + * JOIN FACE KILL EDGE: + * + * Takes two faces joined by a single 2-manifold edge and fuses them togather. + * The edge shared by the faces must not be connected to any other edges which have + * Both faces in its radial cycle + * + * Examples: + * + * A B + * ---------- ---------- + * | | | | + * | f1 | | f1 | + * v1========v2 = Ok! v1==V2==v3 == Wrong! + * | f2 | | f2 | + * | | | | + * ---------- ---------- + * + * In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used. + * In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller + * in this case should call bmesh_JEKV on the extra edges before attempting to fuse f1 and f2. + * + * Also note that the order of arguments decides whether or not certain per-face attributes are present + * in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited + * from f1, not f2. + * + * Returns - + * A BMFace pointer +*/ + +//disregarding f1loop and f2loop, if a vertex appears in a joined face more than once, we cancel + +BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + + BMLoop *curloop, *f1loop=NULL, *f2loop=NULL; + int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, edok, shared; + + if(f1 == f2) return NULL; //can't join a face to itself + /*verify that e is in both f1 and f2*/ + f1len = bmesh_cycle_length(f1->lbase); + f2len = bmesh_cycle_length(f2->lbase); + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) { + if(curloop->e == e) { + f1loop = curloop; + break; + } + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) { + if(curloop->e==e) { + f2loop = curloop; + break; + } + } + if(!(f1loop && f2loop)) return NULL; + + /*validate that edge is 2-manifold edge*/ + radlen = bmesh_cycle_length(&(f1loop->radial)); + if(radlen != 2) return NULL; + + /*validate direction of f2's loop cycle is compatible.*/ + if(f1loop->v == f2loop->v) return NULL; + + /* + validate that for each face, each vertex has another edge in its disk cycle that is + not e, and not shared. + */ + if(bmesh_radial_find_face( ((f1loop->next))->e,f2)) return NULL; + if(bmesh_radial_find_face( ((f1loop->prev))->e,f2)) return NULL; + if(bmesh_radial_find_face( ((f2loop->next))->e,f1)) return NULL; + if(bmesh_radial_find_face( ((f2loop->prev))->e,f1)) return NULL; + + /*validate only one shared edge*/ + shared = BM_face_share_edges(f1,f2); + if(shared > 1) return NULL; + + /*validate no internal joins*/ + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0; + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0; + + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) { + if(curloop != f1loop) + curloop->v->head.eflag1++; + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) { + if(curloop != f2loop) + curloop->v->head.eflag1++; + } + + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) { + if(curloop->v->head.eflag1 > 1) + return NULL; + } + + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) { + if(curloop->v->head.eflag1 > 1) + return NULL; + } + + /*join the two loops*/ + f1loop->prev->next = f2loop->next; + f2loop->next->prev = f1loop->prev; + + f1loop->next->prev = f2loop->prev; + f2loop->prev->next = f1loop->next; + + /*if f1loop was baseloop, give f1loop->next the base.*/ + if(f1->loopbase == f1loop) f1->lbase = ((f1loop->next)); + + /*validate the new loop*/ + loopok = bmesh_cycle_validate((f1len+f2len)-2, f1->lbase); + if(!loopok) bmesh_error(); + + /*make sure each loop points to the proper face*/ + newlen = bmesh_cycle_length(f1->lbase); + for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = ((curloop->next)) ) curloop->f = f1; + + f1->len = newlen; + + edok = bmesh_cycle_validate(f1->len, f1->lbase); + if(!edok) bmesh_error(); + + /*remove edge from the disk cycle of its two vertices.*/ + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1); + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /*deallocate edge and its two loops as well as f2*/ + BLI_remlink(&(bm->edges), &(f1loop->e->head)); + BLI_remlink(&(bm->polys), &(f2->head)); + bmesh_free_edge(bm, f1loop->e); + bmesh_free_loop(bm, f1loop); + bmesh_free_loop(bm, f2loop); + bmesh_free_poly(bm, f2); + return f1; +} + +/** +* bmesh_URMV +* +* UNGLUE REGION MAKE VERT: +* +* Takes a locally manifold disk of face corners and 'unglues' it +* creating a new vertex +* +**/ + +#define URMV_VISIT 1 +#define URMV_VISIT2 2 + +BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv) +{ + BMVert *nv = NULL; + BMLoop *l = NULL, *sl = NULL; + BMEdge *curedge = NULL; + int numloops = 0, numedges = 0, i, maxedges, maxloops; + + + /*BMESH_TODO: Validation*/ + /*validate radial cycle of all collected loops*/ + /*validate the disk cycle of sv, and nv*/ + /*validate the face length of all faces? overkill?*/ + /*validate the l->e pointers of all affected faces, ie: l->v and l->next->v should be equivalent to l->e*/ + + /*verify that sv has edges*/ + if(sv->e == NULL) + return NULL; + + /*first verify no wire edges on sv*/ + curedge = sv->e; + do { + if(curedge->l == NULL) + return NULL; + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*next verify that sv is in sf*/ + l = sf->loopbase; + do { + if(l->v == sv) { + sl = l; + break; + } + l = (l->next); + } while(l != sf->lbase); + + if(sl == NULL) + return NULL; + + /*clear euler flags*/ + sv->head.eflag1 = 0; + + curedge = sv->e; + do { + curedge->head.eflag1 = 0; + l = curedge->l; + do { + l->head.eflag1 = 0; + l->f->head.eflag1 = 0; + l = bmesh_radial_nextloop(l); + } while(l != curedge->l); + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*search through face disk and flag elements as we go.*/ + /*Note, test this to make sure that it works correct on + non-manifold faces! + */ + l = sl; + l->e->head.eflag1 |= URMV_VISIT; + l->f->head.eflag1 |= URMV_VISIT; + do { + if(l->v == sv) + l = bmesh_radial_nextloop((l->prev)); + else + l = bmesh_radial_nextloop((l->next)); + l->e->head.eflag1 |= URMV_VISIT; + l->f->head.eflag1 |= URMV_VISIT; + } while(l != sl && (bmesh_cycle_length(&(l->radial)) > 1) ); + + /*Verify that all visited edges are at least 1 or 2 manifold*/ + curedge = sv->e; + do { + if(curedge->head.eflag1 && (bmesh_cycle_length(&(curedge->l->radial)) > 2) ) + return NULL; + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*allocate temp storage - we overallocate here instead of trying to be clever*/ + maxedges = 0; + maxloops = 0; + curedge = sv->e; + do { + if(curedge->l) { + l = curedge->l; + do { + maxloops += l->f->len; + l = bmesh_radial_nextloop(l); + } while(l != curedge->l); + } + maxedges+= 1; + curedge = bmesh_disk_nextedge(curedge,sv); + } while(curedge != sv->e); + + if(bm->edarlen < maxedges) { + MEM_freeN(bm->edar); + bm->edar = MEM_callocN(sizeof(BMEdge *) * maxedges, "BM Edge pointer array"); + bm->edarlen = maxedges; + } + if(bm->lparlen < maxloops) { + MEM_freeN(bm->lpar); + bm->lpar = MEM_callocN(sizeof(BMLoop *) * maxloops, "BM Loop pointer array"); + bm->lparlen = maxloops; + } + + /*first get loops by looping around edges and loops around that edges faces*/ + curedge = sv->e; + do { + if(curedge->l) { + l = curedge->l; + do { + if( (l->head.eflag1 & URMV_VISIT) && (!(l->head.eflag1 & URMV_VISIT2)) ) { + bm->lpar[numloops] = l; + l->head.eflag1 |= URMV_VISIT2; + numloops++; + } + l = bmesh_radial_nextloop(l); + } while(l != curedge->l); + } + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*now collect edges by looping around edges and looking at visited flags*/ + curedge = sv->e; + do { + if(curedge->head.eflag1 & URMV_VISIT) { + bm->edar[numedges] = curedge; + numedges++; + } + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*make new vertex*/ + nv = bmesh_addvertlist(bm, sv); + + /*go through and relink edges*/ + for(i = 0; i < numedges; i++) { + curedge = bm->edar[i]; + /*remove curedge from sv*/ + bmesh_disk_remove_edge(curedge, sv); + /*swap out sv for nv in curedge*/ + bmesh_edge_swapverts(curedge, sv, nv); + /*add curedge to nv's disk cycle*/ + bmesh_disk_append_edge(curedge, nv); + } + + /*go through and relink loops*/ + for(i = 0; i < numloops; i ++) { + l = bm->lpar[i]; + if(l->v == sv) + l->v = nv; + } + return nv; +} +#endif diff --git a/source/blender/bmesh/intern/bmesh_inline.c b/source/blender/bmesh/intern/bmesh_inline.c new file mode 100644 index 00000000000..4433aaa0fc6 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_inline.c @@ -0,0 +1,71 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_inline.c + * \ingroup bmesh + * + * BM Inline functions. + */ + +#ifndef __BMESH_INLINE_C__ +#define __BMESH_INLINE_C__ + +#include "bmesh.h" + +BM_INLINE char BM_elem_flag_test(const void *element, const char hflag) +{ + return ((const BMHeader *)element)->hflag & hflag; +} + +BM_INLINE void BM_elem_flag_enable(void *element, const char hflag) +{ + ((BMHeader *)element)->hflag |= hflag; +} + +BM_INLINE void BM_elem_flag_disable(void *element, const char hflag) +{ + ((BMHeader *)element)->hflag &= ~hflag; +} + +BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag) +{ + ((BMHeader *)element)->hflag ^= hflag; +} + +BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b) +{ + ((BMHeader *)element_a)->hflag = + ((BMHeader *)element_b)->hflag = (((BMHeader *)element_a)->hflag | + ((BMHeader *)element_b)->hflag); +} + +BM_INLINE void BM_elem_index_set(void *element, const int index) +{ + ((BMHeader *)element)->index = index; +} + +BM_INLINE int BM_elem_index_get(const void *element) +{ + return ((BMHeader *)element)->index; +} + +#endif /* __BMESH_INLINE_C__ */ diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c new file mode 100644 index 00000000000..d5f22690d63 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -0,0 +1,984 @@ +/* + * ***** 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) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_interp.c + * \ingroup bmesh + * + * Functions for interpolating data across the surface of a mesh. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_multires.h" + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/** + * bmesh_data_interp_from_verts + * + * Interpolates per-vertex data from two sources to a target. + */ +void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac) +{ + if (v1->head.data && v2->head.data) { + /* first see if we can avoid interpolation */ + if (fac <= 0.0f) { + if (v1 == v) { + /* do nothing */ + } + else { + CustomData_bmesh_free_block(&bm->vdata, &v->head.data); + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v1->head.data, &v->head.data); + } + } + else if (fac >= 1.0f) { + if (v2 == v) { + /* do nothing */ + } + else { + CustomData_bmesh_free_block(&bm->vdata, &v->head.data); + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v2->head.data, &v->head.data); + } + } + else { + void *src[2]; + float w[2]; + + src[0] = v1->head.data; + src[1] = v2->head.data; + w[0] = 1.0f-fac; + w[1] = fac; + CustomData_bmesh_interp(&bm->vdata, src, w, NULL, 2, v->head.data); + } + } +} + +/* + * BM Data Vert Average + * + * Sets all the customdata (e.g. vert, loop) associated with a vert + * to the average of the face regions surrounding it. + */ + + +static void UNUSED_FUNCTION(BM_Data_Vert_Average)(BMesh *UNUSED(bm), BMFace *UNUSED(f)) +{ + // BMIter iter; +} + +/** + * bmesh_data_facevert_edgeinterp + * + * Walks around the faces of an edge and interpolates the per-face-edge + * data between two sources to a target. + * + * Returns - + * Nothing + */ + +void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BMVert *v, BMEdge *e1, const float fac) +{ + void *src[2]; + float w[2]; + BMLoop *v1loop = NULL, *vloop = NULL, *v2loop = NULL; + BMLoop *l_iter = NULL; + + if (!e1->l) { + return; + } + + w[1] = 1.0f - fac; + w[0] = fac; + + l_iter = e1->l; + do { + if (l_iter->v == v1) { + v1loop = l_iter; + vloop = v1loop->next; + v2loop = vloop->next; + } + else if (l_iter->v == v) { + v1loop = l_iter->next; + vloop = l_iter; + v2loop = l_iter->prev; + } + + if (!v1loop || !v2loop) + return; + + src[0] = v1loop->head.data; + src[1] = v2loop->head.data; + + CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, vloop->head.data); + } while ((l_iter = l_iter->radial_next) != e1->l); +} + +void BM_loops_to_corners(BMesh *bm, Mesh *me, int findex, + BMFace *f, int numTex, int numCol) +{ + BMLoop *l; + BMIter iter; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + int i, j; + + for (i = 0; i < numTex; i++) { + texface = CustomData_get_n(&me->fdata, CD_MTFACE, findex, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->head.data, CD_MTEXPOLY, i); + + ME_MTEXFACE_CPY(texface, texpoly); + + j = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i); + copy_v2_v2(texface->uv[j], mloopuv->uv); + + j++; + } + + } + + for (i = 0; i < numCol; i++) { + mcol = CustomData_get_n(&me->fdata, CD_MCOL, findex, i); + + j = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPCOL, i); + mcol[j].r = mloopcol->r; + mcol[j].g = mloopcol->g; + mcol[j].b = mloopcol->b; + mcol[j].a = mloopcol->a; + + j++; + } + } +} + +/** + * BM_data_interp_from_face + * + * projects target onto source, and pulls interpolated customdata from + * source. + * + * Returns - + * Nothing + */ +void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source) +{ + BMLoop *l_iter; + BMLoop *l_first; + + void **blocks = NULL; + float (*cos)[3] = NULL, *w = NULL; + BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__); + int i; + + BM_elem_attrs_copy(bm, bm, source, target); + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + copy_v3_v3(cos[i], l_iter->v->co); + blocks[i] = l_iter->head.data; + i++; + } while ((l_iter = l_iter->next) != l_first); + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(target); + do { + interp_weights_poly_v3(w, cos, source->len, l_iter->v->co); + CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, l_iter->head.data); + i++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_array_fixedstack_free(cos); + BLI_array_fixedstack_free(w); + BLI_array_fixedstack_free(blocks); +} + +/* some math stuff for dealing with doubles, put here to + * avoid merge errors - joeedh */ + +#define VECMUL(a, b) (((a)[0] = (a)[0] * (b)), ((a)[1] = (a)[1] * (b)), ((a)[2] = (a)[2] * (b))) +#define VECADD2(a, b) (((a)[0] = (a)[0] + (b)[0]), ((a)[1] = (a)[1] + (b)[1]), ((a)[2] = (a)[2] + (b)[2])) +#define VECSUB2(a, b) (((a)[0] = (a)[0] - (b)[0]), ((a)[1] = (a)[1] - (b)[1]), ((a)[2] = (a)[2] - (b)[2])) + +/* find closest point to p on line through l1, l2 and return lambda, + * where (0 <= lambda <= 1) when cp is in the line segement l1, l2 + */ +static double closest_to_line_v3_d(double cp[3], const double p[3], const double l1[3], const double l2[3]) +{ + double h[3], u[3], lambda; + VECSUB(u, l2, l1); + VECSUB(h, p, l1); + lambda = INPR(u, h) / INPR(u, u); + cp[0] = l1[0] + u[0] * lambda; + cp[1] = l1[1] + u[1] * lambda; + cp[2] = l1[2] + u[2] * lambda; + return lambda; +} + +/* point closest to v1 on line v2-v3 in 3D */ +static void UNUSED_FUNCTION(closest_to_line_segment_v3_d)(double *closest, double v1[3], double v2[3], double v3[3]) +{ + double lambda, cp[3]; + + lambda = closest_to_line_v3_d(cp, v1, v2, v3); + + if (lambda <= 0.0) { + VECCOPY(closest, v2); + } + else if (lambda >= 1.0) { + VECCOPY(closest, v3); + } + else { + VECCOPY(closest, cp); + } +} + +static double UNUSED_FUNCTION(len_v3_d)(const double a[3]) +{ + return sqrt(INPR(a, a)); +} + +static double UNUSED_FUNCTION(len_v3v3_d)(const double a[3], const double b[3]) +{ + double d[3]; + + VECSUB(d, b, a); + return sqrt(INPR(d, d)); +} + +static void cent_quad_v3_d(double *cent, double *v1, double *v2, double *v3, double *v4) +{ + cent[0] = 0.25 * (v1[0] + v2[0] + v3[0] + v4[0]); + cent[1] = 0.25 * (v1[1] + v2[1] + v3[1] + v4[1]); + cent[2] = 0.25 * (v1[2] + v2[2] + v3[2] + v4[2]); +} + +static void UNUSED_FUNCTION(cent_tri_v3_d)(double *cent, double *v1, double *v2, double *v3) +{ + cent[0] = (1.0 / 3.0) * (v1[0] + v2[0] + v3[0]); + cent[1] = (1.0 / 3.0) * (v1[1] + v2[1] + v3[1]); + cent[2] = (1.0 / 3.0) * (v1[2] + v2[2] + v3[2]); +} + +static void UNUSED_FUNCTION(cross_v3_v3v3_d)(double r[3], const double a[3], const double b[3]) +{ + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; +} + +/* distance v1 to line-piece v2-v3 */ +static double UNUSED_FUNCTION(dist_to_line_segment_v2_d)(double v1[3], double v2[3], double v3[3]) +{ + double labda, rc[2], pt[2], len; + + rc[0] = v3[0] - v2[0]; + rc[1] = v3[1] - v2[1]; + len = rc[0] * rc[0] + rc[1] * rc[1]; + if (len == 0.0) { + rc[0] = v1[0] - v2[0]; + rc[1] = v1[1] - v2[1]; + return sqrt(rc[0] * rc[0] + rc[1] * rc[1]); + } + + labda = (rc[0] * (v1[0] - v2[0]) + rc[1] * (v1[1] - v2[1])) / len; + if (labda <= 0.0) { + pt[0] = v2[0]; + pt[1] = v2[1]; + } + else if (labda >= 1.0) { + pt[0] = v3[0]; + pt[1] = v3[1]; + } + else { + pt[0] = labda * rc[0] + v2[0]; + pt[1] = labda * rc[1] + v2[1]; + } + + rc[0] = pt[0] - v1[0]; + rc[1] = pt[1] - v1[1]; + return sqrt(rc[0] * rc[0] + rc[1] * rc[1]); +} + + +MINLINE double line_point_side_v2_d(const double *l1, const double *l2, const double *pt) +{ + return ((l1[0] - pt[0]) * (l2[1] - pt[1])) - + ((l2[0] - pt[0]) * (l1[1] - pt[1])); +} + +/* point in quad - only convex quads */ +static int isect_point_quad_v2_d(double pt[2], double v1[2], double v2[2], double v3[2], double v4[2]) +{ + if (line_point_side_v2_d(v1, v2, pt) >= 0.0) { + if (line_point_side_v2_d(v2, v3, pt) >= 0.0) { + if (line_point_side_v2_d(v3, v4, pt) >= 0.0) { + if (line_point_side_v2_d(v4, v1, pt) >= 0.0) { + return 1; + } + } + } + } + else { + if (! (line_point_side_v2_d(v2, v3, pt) >= 0.0)) { + if (! (line_point_side_v2_d(v3, v4, pt) >= 0.0)) { + if (! (line_point_side_v2_d(v4, v1, pt) >= 0.0)) { + return -1; + } + } + } + } + + return 0; +} + +/***** multires interpolation***** + * + * mdisps is a grid of displacements, ordered thus: + * + * v1/center----v4/next -> x + * | | + * | | + * v2/prev------v3/cur + * | + * V + * y + */ + +static int compute_mdisp_quad(BMLoop *l, double v1[3], double v2[3], double v3[3], double v4[3], + double e1[3], double e2[3]) +{ + double cent[3] = {0.0, 0.0, 0.0}, n[3], p[3]; + BMLoop *l_first; + BMLoop *l_iter; + + /* computer center */ + l_iter = l_first = BM_FACE_FIRST_LOOP(l->f); + do { + cent[0] += (double)l_iter->v->co[0]; + cent[1] += (double)l_iter->v->co[1]; + cent[2] += (double)l_iter->v->co[2]; + } while ((l_iter = l_iter->next) != l_first); + + VECMUL(cent, (1.0 / (double)l->f->len)); + + VECADD(p, l->prev->v->co, l->v->co); + VECMUL(p, 0.5); + VECADD(n, l->next->v->co, l->v->co); + VECMUL(n, 0.5); + + VECCOPY(v1, cent); + VECCOPY(v2, p); + VECCOPY(v3, l->v->co); + VECCOPY(v4, n); + + VECSUB(e1, v2, v1); + VECSUB(e2, v3, v4); + + return 1; +} + +/* funnily enough, I think this is identical to face_to_crn_interp, heh */ +static double quad_coord(double aa[3], double bb[3], double cc[3], double dd[3], int a1, int a2) +{ + double x, y, z, f1; + + x = aa[a1] * cc[a2] - cc[a1] * aa[a2]; + y = aa[a1] * dd[a2] + bb[a1] * cc[a2] - cc[a1] * bb[a2] - dd[a1] * aa[a2]; + z = bb[a1] * dd[a2] - dd[a1] * bb[a2]; + + if (fabs(2 * (x - y + z)) > DBL_EPSILON * 10.0) { + double f2; + + f1 = (sqrt(y * y - 4.0 * x * z) - y + 2.0 * z) / (2.0 * (x - y + z)); + f2 = (-sqrt(y * y - 4.0 * x * z) - y + 2.0 * z) / (2.0 * (x - y + z)); + + f1 = fabs(f1); + f2 = fabs(f2); + f1 = MIN2(f1, f2); + CLAMP(f1, 0.0, 1.0 + DBL_EPSILON); + } + else { + f1 = -z / (y - 2 * z); + CLAMP(f1, 0.0, 1.0 + DBL_EPSILON); + + if (isnan(f1) || f1 > 1.0 || f1 < 0.0) { + int i; + + for (i = 0; i < 2; i++) { + if (fabsf(aa[i]) < FLT_EPSILON * 100) + return aa[(i + 1) % 2] / fabs(bb[(i + 1) % 2] - aa[(i + 1) % 2]); + if (fabsf(cc[i]) < FLT_EPSILON * 100) + return cc[(i + 1) % 2] / fabs(dd[(i + 1) % 2] - cc[(i + 1) % 2]); + } + } + } + + return f1; +} + +static int quad_co(double *x, double *y, double v1[3], double v2[3], double v3[3], double v4[3], + double p[3], float n[3]) +{ + float projverts[5][3], n2[3]; + double dprojverts[4][3], origin[3] = {0.0f, 0.0f, 0.0f}; + int i; + + /* project points into 2d along normal */ + VECCOPY(projverts[0], v1); + VECCOPY(projverts[1], v2); + VECCOPY(projverts[2], v3); + VECCOPY(projverts[3], v4); + VECCOPY(projverts[4], p); + + normal_quad_v3(n2, projverts[0], projverts[1], projverts[2], projverts[3]); + + if (INPR(n, n2) < -FLT_EPSILON) { + return 0; + } + + /* rotate */ + poly_rotate_plane(n, projverts, 5); + + /* flatten */ + for (i = 0; i < 5; i++) { + projverts[i][2] = 0.0f; + } + + /* subtract origin */ + for (i = 0; i < 4; i++) { + VECSUB2(projverts[i], projverts[4]); + } + + VECCOPY(dprojverts[0], projverts[0]); + VECCOPY(dprojverts[1], projverts[1]); + VECCOPY(dprojverts[2], projverts[2]); + VECCOPY(dprojverts[3], projverts[3]); + + if (!isect_point_quad_v2_d(origin, dprojverts[0], dprojverts[1], dprojverts[2], dprojverts[3])) { + return 0; + } + + *y = quad_coord(dprojverts[1], dprojverts[0], dprojverts[2], dprojverts[3], 0, 1); + *x = quad_coord(dprojverts[2], dprojverts[1], dprojverts[3], dprojverts[0], 0, 1); + + return 1; +} + + +/* tl is loop to project onto, l is loop whose internal displacement, co, is being + * projected. x and y are location in loop's mdisps grid of point co. */ +static int mdisp_in_mdispquad(BMesh *bm, BMLoop *l, BMLoop *tl, double p[3], double *x, double *y, int res) +{ + double v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3]; + double eps = FLT_EPSILON * 4000; + + if (len_v3(l->v->no) == 0.0f) + BM_vert_normal_update_all(bm, l->v); + if (len_v3(tl->v->no) == 0.0f) + BM_vert_normal_update_all(bm, tl->v); + + compute_mdisp_quad(tl, v1, v2, v3, v4, e1, e2); + + /* expand quad a bit */ + cent_quad_v3_d(c, v1, v2, v3, v4); + + VECSUB2(v1, c); VECSUB2(v2, c); + VECSUB2(v3, c); VECSUB2(v4, c); + VECMUL(v1, 1.0 + eps); VECMUL(v2, 1.0 + eps); + VECMUL(v3, 1.0 + eps); VECMUL(v4, 1.0 + eps); + VECADD2(v1, c); VECADD2(v2, c); + VECADD2(v3, c); VECADD2(v4, c); + + if (!quad_co(x, y, v1, v2, v3, v4, p, l->v->no)) + return 0; + + *x *= res - 1; + *y *= res - 1; + + return 1; +} + +static void bmesh_loop_interp_mdisps(BMesh *bm, BMLoop *target, BMFace *source) +{ + MDisps *mdisps; + BMLoop *l_iter; + BMLoop *l_first; + double x, y, d, v1[3], v2[3], v3[3], v4[3] = {0.0f, 0.0f, 0.0f}, e1[3], e2[3]; + int ix, iy, res; + + /* ignore 2-edged faces */ + if (target->f->len < 3) + return; + + if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) + return; + + mdisps = CustomData_bmesh_get(&bm->ldata, target->head.data, CD_MDISPS); + compute_mdisp_quad(target, v1, v2, v3, v4, e1, e2); + + /* if no disps data allocate a new grid, the size of the first grid in source. */ + if (!mdisps->totdisp) { + MDisps *md2 = CustomData_bmesh_get(&bm->ldata, BM_FACE_FIRST_LOOP(source)->head.data, CD_MDISPS); + + mdisps->totdisp = md2->totdisp; + if (mdisps->totdisp) { + mdisps->disps = MEM_callocN(sizeof(float) * 3 * mdisps->totdisp, + "mdisp->disps in bmesh_loop_intern_mdisps"); + } + else { + return; + } + } + + res = (int)sqrt(mdisps->totdisp); + d = 1.0 / (double)(res - 1); + for (x = 0.0f, ix = 0; ix < res; x += d, ix++) { + for (y = 0.0f, iy = 0; iy < res; y += d, iy++) { + double co1[3], co2[3], co[3]; + /* double xx, yy; */ /* UNUSED */ + + VECCOPY(co1, e1); + + /* if (!iy) yy = y + (double)FLT_EPSILON * 20; */ + /* else yy = y - (double)FLT_EPSILON * 20; */ + + VECMUL(co1, y); + VECADD2(co1, v1); + + VECCOPY(co2, e2); + VECMUL(co2, y); + VECADD2(co2, v4); + + /* if (!ix) xx = x + (double)FLT_EPSILON * 20; */ /* UNUSED */ + /* else xx = x - (double)FLT_EPSILON * 20; */ /* UNUSED */ + + VECSUB(co, co2, co1); + VECMUL(co, x); + VECADD2(co, co1); + + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + double x2, y2; + MDisps *md1, *md2; + + md1 = CustomData_bmesh_get(&bm->ldata, target->head.data, CD_MDISPS); + md2 = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS); + + if (mdisp_in_mdispquad(bm, target, l_iter, co, &x2, &y2, res)) { + /* int ix2 = (int)x2; */ /* UNUSED */ + /* int iy2 = (int)y2; */ /* UNUSED */ + + old_mdisps_bilinear(md1->disps[iy * res + ix], md2->disps, res, (float)x2, (float)y2); + } + } while ((l_iter = l_iter->next) != l_first); + } + } +} + +void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) +{ + BMLoop *l; + BMIter liter; + + if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) + return; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + MDisps *mdp = CustomData_bmesh_get(&bm->ldata, l->prev->head.data, CD_MDISPS); + MDisps *mdl = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS); + MDisps *mdn = CustomData_bmesh_get(&bm->ldata, l->next->head.data, CD_MDISPS); + float co1[3]; + int sides; + int y; + + /* + * mdisps is a grid of displacements, ordered thus: + * + * v4/next + * | + * | v1/cent-----mid2 ---> x + * | | | + * | | | + * v2/prev---mid1-----v3/cur + * | + * V + * y + */ + + sides = (int)sqrt(mdp->totdisp); + for (y = 0; y < sides; y++) { + add_v3_v3v3(co1, mdn->disps[y * sides], mdl->disps[y]); + mul_v3_fl(co1, 0.5); + + copy_v3_v3(mdn->disps[y * sides], co1); + copy_v3_v3(mdl->disps[y], co1); + } + } + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + MDisps *mdl1 = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS); + MDisps *mdl2; + float co1[3], co2[3], co[3]; + int sides; + int y; + + /* + * mdisps is a grid of displacements, ordered thus: + * + * v4/next + * | + * | v1/cent-----mid2 ---> x + * | | | + * | | | + * v2/prev---mid1-----v3/cur + * | + * V + * y + */ + + if (l->radial_next == l) + continue; + + if (l->radial_next->v == l->v) + mdl2 = CustomData_bmesh_get(&bm->ldata, l->radial_next->head.data, CD_MDISPS); + else + mdl2 = CustomData_bmesh_get(&bm->ldata, l->radial_next->next->head.data, CD_MDISPS); + + sides = (int)sqrt(mdl1->totdisp); + for (y = 0; y < sides; y++) { + int a1, a2, o1, o2; + + if (l->v != l->radial_next->v) { + a1 = sides * y + sides - 2; + a2 = (sides - 2) * sides + y; + + o1 = sides * y + sides - 1; + o2 = (sides - 1) * sides + y; + } + else { + a1 = sides * y + sides - 2; + a2 = sides * y + sides - 2; + o1 = sides * y + sides - 1; + o2 = sides * y + sides - 1; + } + + /* magic blending numbers, hardcoded! */ + add_v3_v3v3(co1, mdl1->disps[a1], mdl2->disps[a2]); + mul_v3_fl(co1, 0.18); + + add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]); + mul_v3_fl(co2, 0.32); + + add_v3_v3v3(co, co1, co2); + + copy_v3_v3(mdl1->disps[o1], co); + copy_v3_v3(mdl2->disps[o2], co); + } + } +} + +void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source) +{ + bmesh_loop_interp_mdisps(bm, target, source); +} + +void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source, + int do_vertex, int do_multires) +{ + BMLoop *l_iter; + BMLoop *l_first; + void **blocks = NULL; + void **vblocks = NULL; + float (*cos)[3] = NULL, co[3], *w = NULL; + float cent[3] = {0.0f, 0.0f, 0.0f}; + BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(vblocks, BM_NGON_STACK_SIZE, do_vertex ? source->len : 0, __func__); + int i, ax, ay; + + BM_elem_attrs_copy(bm, bm, source, target->f); + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + copy_v3_v3(cos[i], l_iter->v->co); + add_v3_v3(cent, cos[i]); + + w[i] = 0.0f; + blocks[i] = l_iter->head.data; + + if (do_vertex) { + vblocks[i] = l_iter->v->head.data; + } + i++; + + } while ((l_iter = l_iter->next) != l_first); + + /* find best projection of face XY, XZ or YZ: barycentric weights of + * the 2d projected coords are the same and faster to compute */ + + axis_dominant_v3(&ax, &ay, source->no); + + /* scale source face coordinates a bit, so points sitting directonly on an + * edge will work. */ + mul_v3_fl(cent, 1.0f / (float)source->len); + for (i = 0; i < source->len; i++) { + float vec[3], tmp[3]; + sub_v3_v3v3(vec, cent, cos[i]); + mul_v3_fl(vec, 0.001f); + add_v3_v3(cos[i], vec); + + copy_v3_v3(tmp, cos[i]); + cos[i][0] = tmp[ax]; + cos[i][1] = tmp[ay]; + cos[i][2] = 0.0; + } + + + /* interpolate */ + co[0] = target->v->co[ax]; + co[1] = target->v->co[ay]; + co[2] = 0.0f; + + interp_weights_poly_v3(w, cos, source->len, co); + CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, target->head.data); + if (do_vertex) { + CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, source->len, target->v->head.data); + BLI_array_fixedstack_free(vblocks); + } + + BLI_array_fixedstack_free(cos); + BLI_array_fixedstack_free(w); + BLI_array_fixedstack_free(blocks); + + if (do_multires) { + if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + bmesh_loop_interp_mdisps(bm, target, source); + } + } +} + + +void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source) +{ + BMLoop *l_iter; + BMLoop *l_first; + void **blocks = NULL; + float (*cos)[3] = NULL, *w = NULL; + float cent[3] = {0.0f, 0.0f, 0.0f}; + BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__); + int i; + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + copy_v3_v3(cos[i], l_iter->v->co); + add_v3_v3(cent, cos[i]); + + w[i] = 0.0f; + blocks[i] = l_iter->v->head.data; + i++; + } while ((l_iter = l_iter->next) != l_first); + + /* scale source face coordinates a bit, so points sitting directonly on an + * edge will work. */ + mul_v3_fl(cent, 1.0f / (float)source->len); + for (i = 0; i < source->len; i++) { + float vec[3]; + sub_v3_v3v3(vec, cent, cos[i]); + mul_v3_fl(vec, 0.01); + add_v3_v3(cos[i], vec); + } + + /* interpolate */ + interp_weights_poly_v3(w, cos, source->len, v->co); + CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, source->len, v->head.data); + + BLI_array_fixedstack_free(cos); + BLI_array_fixedstack_free(w); + BLI_array_fixedstack_free(blocks); +} + +static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) +{ + BMIter iter; + BLI_mempool *oldpool = olddata->pool; + void *block; + + CustomData_bmesh_init_pool(data, data == &bm->ldata ? 2048 : 512); + + if (data == &bm->vdata) { + BMVert *eve; + + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, eve->head.data, &block); + CustomData_bmesh_free_block(olddata, &eve->head.data); + eve->head.data = block; + } + } + else if (data == &bm->edata) { + BMEdge *eed; + + BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, eed->head.data, &block); + CustomData_bmesh_free_block(olddata, &eed->head.data); + eed->head.data = block; + } + } + else if (data == &bm->pdata || data == &bm->ldata) { + BMIter liter; + BMFace *efa; + BMLoop *l; + + BM_ITER(efa, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (data == &bm->pdata) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, efa->head.data, &block); + CustomData_bmesh_free_block(olddata, &efa->head.data); + efa->head.data = block; + } + + if (data == &bm->ldata) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, efa) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, l->head.data, &block); + CustomData_bmesh_free_block(olddata, &l->head.data); + l->head.data = block; + } + } + } + } + + if (oldpool) { + /* this should never happen but can when dissolve fails - [#28960] */ + BLI_assert(data->pool != oldpool); + + BLI_mempool_destroy(oldpool); + } +} + +void BM_data_layer_add(BMesh *bm, CustomData *data, int type) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_add_layer(data, type, CD_DEFAULT, NULL, 0); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_add_layer_named(data, type, CD_DEFAULT, NULL, 0, name); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void BM_data_layer_free(BMesh *bm, CustomData *data, int type) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_free_layer_active(data, type, 0); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_free_layer(data, type, 0, CustomData_get_layer_index_n(data, type, n)); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +float BM_elem_float_data_get(CustomData *cd, void *element, int type) +{ + float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type); + return f ? *f : 0.0f; +} + +void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val) +{ + float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type); + if (f) *f = val; +} diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c new file mode 100644 index 00000000000..0c6ac24f456 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -0,0 +1,417 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_iterators.c + * \ingroup bmesh + * + * Functions to abstract looping over bmesh data structures. + * + * See: bmesh_iterators_inlin.c too, some functions are here for speed reasons. + */ + + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * note, we have BM_vert_at_index/BM_edge_at_index/BM_face_at_index for arrays + */ +void *BM_iter_at_index(struct BMesh *bm, const char itype, void *data, int index) +{ + BMIter iter; + void *val; + int i; + + /* sanity check */ + if (index < 0) { + return NULL; + } + + val = BM_iter_new(&iter, bm, itype, data); + + i = 0; + while (i < index) { + val = BM_iter_step(&iter); + i++; + } + + return val; +} + + +/* + * ITERATOR AS ARRAY + * + * Sometimes its convenient to get the iterator as an array + * to avoid multiple calls to BM_iter_at_index. + */ + +int BM_iter_as_array(struct BMesh *bm, const char type, void *data, void **array, const int len) +{ + int i = 0; + + /* sanity check */ + if (len > 0) { + + BMIter iter; + void *val; + + BM_ITER(val, &iter, bm, type, data) { + array[i] = val; + i++; + if (i == len) { + return len; + } + } + } + + return i; +} + + +/* + * INIT ITERATOR + * + * Clears the internal state of an iterator + * For begin() callbacks. + * + */ + +static void init_iterator(BMIter *iter) +{ + iter->firstvert = iter->nextvert = NULL; + iter->firstedge = iter->nextedge = NULL; + iter->firstloop = iter->nextloop = NULL; + iter->firstpoly = iter->nextpoly = NULL; + iter->ldata = NULL; +} + +/* + * Notes on iterator implementation: + * + * Iterators keep track of the next element + * in a sequence. When a step() callback is + * invoked the current value of 'next' is stored + * to be returned later and the next variable is + * incremented. + * + * When the end of a sequence is + * reached, next should always equal NULL + * + * The 'bmiter__' prefix is used because these are used in + * bmesh_iterators_inine.c but should otherwise be seen as + * private. + */ + +/* + * VERT OF MESH CALLBACKS + * + */ + +void bmiter__vert_of_mesh_begin(BMIter *iter) +{ + BLI_mempool_iternew(iter->bm->vpool, &iter->pooliter); +} + +void *bmiter__vert_of_mesh_step(BMIter *iter) +{ + return BLI_mempool_iterstep(&iter->pooliter); + +} + +void bmiter__edge_of_mesh_begin(BMIter *iter) +{ + BLI_mempool_iternew(iter->bm->epool, &iter->pooliter); +} + +void *bmiter__edge_of_mesh_step(BMIter *iter) +{ + return BLI_mempool_iterstep(&iter->pooliter); + +} + +void bmiter__face_of_mesh_begin(BMIter *iter) +{ + BLI_mempool_iternew(iter->bm->fpool, &iter->pooliter); +} + +void *bmiter__face_of_mesh_step(BMIter *iter) +{ + return BLI_mempool_iterstep(&iter->pooliter); + +} + +/* + * EDGE OF VERT CALLBACKS + * + */ + +void bmiter__edge_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + if (iter->vdata->e) { + iter->firstedge = iter->vdata->e; + iter->nextedge = iter->vdata->e; + } +} + +void *bmiter__edge_of_vert_step(BMIter *iter) +{ + BMEdge *current = iter->nextedge; + + if (iter->nextedge) + iter->nextedge = bmesh_disk_nextedge(iter->nextedge, iter->vdata); + + if (iter->nextedge == iter->firstedge) iter->nextedge = NULL; + + return current; +} + +/* + * FACE OF VERT CALLBACKS + * + */ + +void bmiter__face_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + iter->count = 0; + if (iter->vdata->e) + iter->count = bmesh_disk_count_facevert(iter->vdata); + if (iter->count) { + iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->e, iter->vdata); + iter->nextedge = iter->firstedge; + iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } +} +void *bmiter__face_of_vert_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->count && iter->nextloop) { + iter->count--; + iter->nextloop = bmesh_radial_find_next_facevert(iter->nextloop, iter->vdata); + if (iter->nextloop == iter->firstloop) { + iter->nextedge = bmesh_disk_find_next_faceedge(iter->nextedge, iter->vdata); + iter->firstloop = bmesh_radial_find_first_facevert(iter->nextedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } + } + + if (!iter->count) iter->nextloop = NULL; + + return current ? current->f : NULL; +} + + +/* + * LOOP OF VERT CALLBACKS + * + */ + +void bmiter__loop_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + iter->count = 0; + if (iter->vdata->e) + iter->count = bmesh_disk_count_facevert(iter->vdata); + if (iter->count) { + iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->e, iter->vdata); + iter->nextedge = iter->firstedge; + iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } +} +void *bmiter__loop_of_vert_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->count) { + iter->count--; + iter->nextloop = bmesh_radial_find_next_facevert(iter->nextloop, iter->vdata); + if (iter->nextloop == iter->firstloop) { + iter->nextedge = bmesh_disk_find_next_faceedge(iter->nextedge, iter->vdata); + iter->firstloop = bmesh_radial_find_first_facevert(iter->nextedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } + } + + if (!iter->count) iter->nextloop = NULL; + + + if (current) { + return current; + } + + return NULL; +} + + +void bmiter__loops_of_edge_begin(BMIter *iter) +{ + BMLoop *l; + + l = iter->edata->l; + + /* note sure why this sets ldata ... */ + init_iterator(iter); + + iter->firstloop = iter->nextloop = l; +} + +void *bmiter__loops_of_edge_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) + iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if (iter->nextloop == iter->firstloop) + iter->nextloop = NULL; + + if (current) { + return current; + } + + return NULL; +} + +void bmiter__loops_of_loop_begin(BMIter *iter) +{ + BMLoop *l; + + l = iter->ldata; + + /* note sure why this sets ldata ... */ + init_iterator(iter); + + iter->firstloop = l; + iter->nextloop = bmesh_radial_nextloop(iter->firstloop); + + if (iter->nextloop == iter->firstloop) + iter->nextloop = NULL; +} + +void *bmiter__loops_of_loop_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + if (current) { + return current; + } + + return NULL; +} + +/* + * FACE OF EDGE CALLBACKS + * + */ + +void bmiter__face_of_edge_begin(BMIter *iter) +{ + init_iterator(iter); + + if (iter->edata->l) { + iter->firstloop = iter->edata->l; + iter->nextloop = iter->edata->l; + } +} + +void *bmiter__face_of_edge_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current ? current->f : NULL; +} + +/* + * VERT OF FACE CALLBACKS + * + */ + +void bmiter__vert_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); +} + +void *bmiter__vert_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = iter->nextloop->next; + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current ? current->v : NULL; +} + +/* + * EDGE OF FACE CALLBACKS + * + */ + +void bmiter__edge_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); +} + +void *bmiter__edge_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = iter->nextloop->next; + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current ? current->e : NULL; +} + +/* + * LOOP OF FACE CALLBACKS + * + */ + +void bmiter__loop_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); +} + +void *bmiter__loop_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = iter->nextloop->next; + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current; +} diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.c b/source/blender/bmesh/intern/bmesh_iterators_inline.c new file mode 100644 index 00000000000..ef644f96ce0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.c @@ -0,0 +1,160 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_iterators_inline.c + * \ingroup bmesh + * + * BMesh inline iterator functions. + */ + +#ifndef __BMESH_ITERATORS_INLINE_C__ +#define __BMESH_ITERATORS_INLINE_C__ + +#include "bmesh.h" + +/* inline here optimizes out the switch statement when called with + * constant values (which is very common), nicer for loop-in-loop situations */ + +/* + * BMESH ITERATOR STEP + * + * Calls an iterators step fucntion to return + * the next element. + */ + +BM_INLINE void *BM_iter_step(BMIter *iter) +{ + return iter->step(iter); +} + + +/* + * BMESH ITERATOR INIT + * + * Takes a bmesh iterator structure and fills + * it with the appropriate function pointers based + * upon its type and then calls BMeshIter_step() + * to return the first element of the iterator. + * + */ +BM_INLINE void *BM_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data) +{ + /* int argtype; */ + iter->itype = itype; + iter->bm = bm; + + /* inlining optimizes out this switch when called with the defined type */ + switch (itype) { + case BM_VERTS_OF_MESH: + iter->begin = bmiter__vert_of_mesh_begin; + iter->step = bmiter__vert_of_mesh_step; + break; + case BM_EDGES_OF_MESH: + iter->begin = bmiter__edge_of_mesh_begin; + iter->step = bmiter__edge_of_mesh_step; + break; + case BM_FACES_OF_MESH: + iter->begin = bmiter__face_of_mesh_begin; + iter->step = bmiter__face_of_mesh_step; + break; + case BM_EDGES_OF_VERT: + if (!data) + return NULL; + + iter->begin = bmiter__edge_of_vert_begin; + iter->step = bmiter__edge_of_vert_step; + iter->vdata = data; + break; + case BM_FACES_OF_VERT: + if (!data) + return NULL; + + iter->begin = bmiter__face_of_vert_begin; + iter->step = bmiter__face_of_vert_step; + iter->vdata = data; + break; + case BM_LOOPS_OF_VERT: + if (!data) + return NULL; + + iter->begin = bmiter__loop_of_vert_begin; + iter->step = bmiter__loop_of_vert_step; + iter->vdata = data; + break; + case BM_FACES_OF_EDGE: + if (!data) + return NULL; + + iter->begin = bmiter__face_of_edge_begin; + iter->step = bmiter__face_of_edge_step; + iter->edata = data; + break; + case BM_VERTS_OF_FACE: + if (!data) + return NULL; + + iter->begin = bmiter__vert_of_face_begin; + iter->step = bmiter__vert_of_face_step; + iter->pdata = data; + break; + case BM_EDGES_OF_FACE: + if (!data) + return NULL; + + iter->begin = bmiter__edge_of_face_begin; + iter->step = bmiter__edge_of_face_step; + iter->pdata = data; + break; + case BM_LOOPS_OF_FACE: + if (!data) + return NULL; + + iter->begin = bmiter__loop_of_face_begin; + iter->step = bmiter__loop_of_face_step; + iter->pdata = data; + break; + case BM_LOOPS_OF_LOOP: + if (!data) + return NULL; + + iter->begin = bmiter__loops_of_loop_begin; + iter->step = bmiter__loops_of_loop_step; + iter->ldata = data; + break; + case BM_LOOPS_OF_EDGE: + if (!data) + return NULL; + + iter->begin = bmiter__loops_of_edge_begin; + iter->step = bmiter__loops_of_edge_step; + iter->edata = data; + break; + default: + break; + } + + iter->begin(iter); + return BM_iter_step(iter); +} + + +#endif /* __BMESH_ITERATORS_INLINE_C__ */ diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c new file mode 100644 index 00000000000..8a8ccb5efb1 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -0,0 +1,910 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_marking.c + * \ingroup bmesh + * + * Selection routines for bmesh structures. + * This is actually all old code ripped from + * editmesh_lib.c and slightly modified to work + * for bmesh's. This also means that it has some + * of the same problems.... something that + * that should be addressed eventually. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" + +#include "bmesh.h" + + +/* + * BMESH SELECTMODE FLUSH + * + * Makes sure to flush selections + * 'upwards' (ie: all verts of an edge + * selects the edge and so on). This + * should only be called by system and not + * tool authors. + * + */ + +static void recount_totsels(BMesh *bm) +{ + BMIter iter; + BMHeader *ele; + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + int *tots[3]; + int i; + + /* recount (tot * sel) variables */ + bm->totvertsel = bm->totedgesel = bm->totfacesel = 0; + tots[0] = &bm->totvertsel; + tots[1] = &bm->totedgesel; + tots[2] = &bm->totfacesel; + + for (i = 0; i < 3; i++) { + ele = BM_iter_new(&iter, bm, iter_types[i], NULL); + for ( ; ele; ele = BM_iter_step(&iter)) { + if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) *tots[i] += 1; + } + } +} + +void BM_mesh_select_mode_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l_iter; + BMLoop *l_first; + BMFace *f; + + BMIter edges; + BMIter faces; + + int ok; + + if (bm->selectmode & SCE_SELECT_VERTEX) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) + { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + } + else { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + } + } + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + } + else { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + } + } + } + else if (bm->selectmode & SCE_SELECT_EDGE) { + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(&(l_iter->e->head), BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + } + else { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + } + } + } + + /* Remove any deselected elements from the BMEditSelection */ + BM_select_history_validate(bm); + + recount_totsels(bm); +} + +/* BMESH NOTE: matches EM_deselect_flush() behavior from trunk */ +void BM_mesh_deselect_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l_iter; + BMLoop *l_first; + BMFace *f; + + BMIter edges; + BMIter faces; + + int ok; + + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (!(BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN))) + { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + } + } + + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok == FALSE) { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + } + } + + /* Remove any deselected elements from the BMEditSelection */ + BM_select_history_validate(bm); + + recount_totsels(bm); +} + + +/* BMESH NOTE: matches EM_select_flush() behavior from trunk */ +void BM_mesh_select_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l_iter; + BMLoop *l_first; + BMFace *f; + + BMIter edges; + BMIter faces; + + int ok; + + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) + { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + } + } + + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + } + } + + recount_totsels(bm); +} + +/* + * BMESH SELECT VERT + * + * Changes selection state of a single vertex + * in a mesh + * + */ + +void BM_vert_select_set(BMesh *bm, BMVert *v, int select) +{ + /* BMIter iter; */ + /* BMEdge *e; */ + + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) { + bm->totvertsel += 1; + BM_elem_flag_enable(v, BM_ELEM_SELECT); + } + } + else { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + bm->totvertsel -= 1; + BM_elem_flag_disable(v, BM_ELEM_SELECT); + } + } +} + +/* + * BMESH SELECT EDGE + * + * Changes selection state of a single edge + * in a mesh. + * + */ + +void BM_edge_select_set(BMesh *bm, BMEdge *e, int select) +{ + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel += 1; + + BM_elem_flag_enable(&(e->head), BM_ELEM_SELECT); + BM_elem_select_set(bm, e->v1, TRUE); + BM_elem_select_set(bm, e->v2, TRUE); + } + else { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel -= 1; + BM_elem_flag_disable(&(e->head), BM_ELEM_SELECT); + + if ( bm->selectmode == SCE_SELECT_EDGE || + bm->selectmode == SCE_SELECT_FACE || + bm->selectmode == (SCE_SELECT_EDGE | SCE_SELECT_FACE)) + { + + BMIter iter; + BMVert *verts[2] = {e->v1, e->v2}; + BMEdge *e2; + int i; + + for (i = 0; i < 2; i++) { + int deselect = 1; + + for (e2 = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, verts[i]); e2; e2 = BM_iter_step(&iter)) { + if (e2 == e) { + continue; + } + + if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) { + deselect = 0; + break; + } + } + + if (deselect) BM_vert_select_set(bm, verts[i], FALSE); + } + } + else { + BM_elem_select_set(bm, e->v1, FALSE); + BM_elem_select_set(bm, e->v2, FALSE); + } + + } +} + +/* + * + * BMESH SELECT FACE + * + * Changes selection state of a single + * face in a mesh. + * + */ + +void BM_face_select_set(BMesh *bm, BMFace *f, int select) +{ + BMLoop *l_iter; + BMLoop *l_first; + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + bm->totfacesel++; + } + + BM_elem_flag_enable(&(f->head), BM_ELEM_SELECT); + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_vert_select_set(bm, l_iter->v, TRUE); + BM_edge_select_set(bm, l_iter->e, TRUE); + } while ((l_iter = l_iter->next) != l_first); + } + else { + BMIter liter; + BMLoop *l; + + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) bm->totfacesel -= 1; + BM_elem_flag_disable(&(f->head), BM_ELEM_SELECT); + + /* flush down to edges */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BMIter fiter; + BMFace *f2; + BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) { + if (BM_elem_flag_test(f2, BM_ELEM_SELECT)) + break; + } + + if (!f2) + { + BM_elem_select_set(bm, l->e, FALSE); + } + } + + /* flush down to verts */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BMIter eiter; + BMEdge *e; + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, l->v) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) + break; + } + + if (!e) { + BM_elem_select_set(bm, l->v, FALSE); + } + } + } +} + +/* + * BMESH SELECTMODE SET + * + * Sets the selection mode for the bmesh + * + */ + +void BM_select_mode_set(BMesh *bm, int selectmode) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + bm->selectmode = selectmode; + + if (bm->selectmode & SCE_SELECT_VERTEX) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) + BM_elem_flag_disable(e, 0); + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) + BM_elem_flag_disable(f, 0); + BM_mesh_select_mode_flush(bm); + } + else if (bm->selectmode & SCE_SELECT_EDGE) { + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) + BM_elem_flag_disable(v, 0); + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BM_elem_flag_test(&(e->head), BM_ELEM_SELECT)) { + BM_edge_select_set(bm, e, TRUE); + } + } + BM_mesh_select_mode_flush(bm); + } + else if (bm->selectmode & SCE_SELECT_FACE) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) + BM_elem_flag_disable(e, 0); + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + if (BM_elem_flag_test(&(f->head), BM_ELEM_SELECT)) { + BM_face_select_set(bm, f, TRUE); + } + } + BM_mesh_select_mode_flush(bm); + } +} + + +int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide) +{ + BMHeader *head; + BMIter iter; + int tot = 0; + + if (htype & BM_VERT) { + for (head = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); head; head = BM_iter_step(&iter)) { + if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue; + if (BM_elem_flag_test(head, hflag)) tot++; + } + } + if (htype & BM_EDGE) { + for (head = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) { + if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue; + if (BM_elem_flag_test(head, hflag)) tot++; + } + } + if (htype & BM_FACE) { + for (head = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) { + if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue; + if (BM_elem_flag_test(head, hflag)) tot++; + } + } + + return tot; +} + +/* note: by design, this will not touch the editselection history stuff */ +void BM_elem_select_set(struct BMesh *bm, void *element, int select) +{ + BMHeader *head = element; + + if (head->htype == BM_VERT) BM_vert_select_set(bm, (BMVert *)element, select); + else if (head->htype == BM_EDGE) BM_edge_select_set(bm, (BMEdge *)element, select); + else if (head->htype == BM_FACE) BM_face_select_set(bm, (BMFace *)element, select); +} + +/* this replaces the active flag used in uv/face mode */ +void BM_active_face_set(BMesh *bm, BMFace *efa) +{ + bm->act_face = efa; +} + +BMFace *BM_active_face_get(BMesh *bm, int sloppy) +{ + if (bm->act_face) { + return bm->act_face; + } + else if (sloppy) { + BMIter iter; + BMFace *f = NULL; + BMEditSelection *ese; + + /* Find the latest non-hidden face from the BMEditSelection */ + ese = bm->selected.last; + for ( ; ese; ese = ese->prev) { + if (ese->htype == BM_FACE) { + f = (BMFace *)ese->data; + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + f = NULL; + } + else { + break; + } + } + } + /* Last attempt: try to find any selected face */ + if (f == NULL) { + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + break; + } + } + } + return f; /* can still be null */ + } + return NULL; +} + +/* 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 BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese) +{ + if (ese->htype == BM_VERT) { + BMVert *eve = ese->data; + copy_v3_v3(r_center, eve->co); + } + else if (ese->htype == BM_EDGE) { + BMEdge *eed = ese->data; + add_v3_v3v3(r_center, eed->v1->co, eed->v2->co); + mul_v3_fl(r_center, 0.5); + } + else if (ese->htype == BM_FACE) { + BMFace *efa = ese->data; + BM_face_center_bounds_calc(bm, efa, r_center); + } +} + +void BM_editselection_normal(float r_normal[3], BMEditSelection *ese) +{ + if (ese->htype == BM_VERT) { + BMVert *eve = ese->data; + copy_v3_v3(r_normal, eve->no); + } + else if (ese->htype == BM_EDGE) { + BMEdge *eed = ese->data; + float plane[3]; /* need a plane to correct the normal */ + float vec[3]; /* temp vec storage */ + + add_v3_v3v3(r_normal, eed->v1->no, eed->v2->no); + sub_v3_v3v3(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 */ + cross_v3_v3v3(vec, r_normal, plane); + cross_v3_v3v3(r_normal, plane, vec); + normalize_v3(r_normal); + + } + else if (ese->htype == BM_FACE) { + BMFace *efa = ese->data; + copy_v3_v3(r_normal, efa->no); + } +} + +/* ref - editmesh_lib.cL:EM_editselection_plane() */ + +/* Calculate a plane that is rightangles to the edge/vert/faces normal + * also make the plane run along an axis that is related to the geometry, + * because this is used for the manipulators Y axis. */ +void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese) +{ + if (ese->htype == BM_VERT) { + BMVert *eve = ese->data; + float vec[3] = {0.0f, 0.0f, 0.0f}; + + if (ese->prev) { /* use previously selected data to make a useful vertex plane */ + BM_editselection_center(bm, vec, ese->prev); + sub_v3_v3v3(r_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.5f) vec[0] = 1.0f; + else if (eve->no[1] < 0.5f) vec[1] = 1.0f; + else vec[2] = 1.0f; + cross_v3_v3v3(r_plane, eve->no, vec); + } + } + else if (ese->htype == BM_EDGE) { + BMEdge *eed = ese->data; + + /* the plane is simple, it runs along 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 */ + sub_v3_v3v3(r_plane, eed->v2->co, eed->v1->co); + } + else { + sub_v3_v3v3(r_plane, eed->v1->co, eed->v2->co); + } + + } + else if (ese->htype == BM_FACE) { + BMFace *efa = ese->data; + float vec[3] = {0.0f, 0.0f, 0.0f}; + + /* for now, use face normal */ + + /* 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 (efa->len < 3) { + /* crappy fallback method */ + if (efa->no[0] < 0.5f) vec[0] = 1.0f; + else if (efa->no[1] < 0.5f) vec[1] = 1.0f; + else vec[2] = 1.0f; + cross_v3_v3v3(r_plane, efa->no, vec); + } + else { + BMVert *verts[4] = {NULL}; + + BM_iter_as_array(bm, BM_VERTS_OF_FACE, efa, (void **)verts, 4); + + if (efa->len == 4) { + float vecA[3], vecB[3]; + sub_v3_v3v3(vecA, verts[3]->co, verts[2]->co); + sub_v3_v3v3(vecB, verts[0]->co, verts[1]->co); + add_v3_v3v3(r_plane, vecA, vecB); + + sub_v3_v3v3(vecA, verts[0]->co, verts[3]->co); + sub_v3_v3v3(vecB, verts[1]->co, verts[2]->co); + add_v3_v3v3(vec, vecA, vecB); + /* use the biggest edge length */ + if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) { + copy_v3_v3(r_plane, vec); + } + } + else { + /* BMESH_TODO (not urgent, use longest ngon edge for alignment) */ + + /* start with v1-2 */ + sub_v3_v3v3(r_plane, verts[0]->co, verts[1]->co); + + /* test the edge between v2-3, use if longer */ + sub_v3_v3v3(vec, verts[1]->co, verts[2]->co); + if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) + copy_v3_v3(r_plane, vec); + + /* test the edge between v1-3, use if longer */ + sub_v3_v3v3(vec, verts[2]->co, verts[0]->co); + if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) { + copy_v3_v3(r_plane, vec); + } + } + + } + } + normalize_v3(r_plane); +} + +int BM_select_history_check(BMesh *bm, void *data) +{ + BMEditSelection *ese; + + for (ese = bm->selected.first; ese; ese = ese->next) { + if (ese->data == data) { + return TRUE; + } + } + + return FALSE; +} + +void BM_select_history_remove(BMesh *bm, void *data) +{ + BMEditSelection *ese; + for (ese = bm->selected.first; ese; ese = ese->next) { + if (ese->data == data) { + BLI_freelinkN(&(bm->selected), ese); + break; + } + } +} + +void BM_select_history_clear(BMesh *bm) +{ + BLI_freelistN(&bm->selected); + bm->selected.first = bm->selected.last = NULL; +} + +void BM_select_history_store(BMesh *bm, void *data) +{ + BMEditSelection *ese; + if (!BM_select_history_check(bm, data)) { + ese = (BMEditSelection *) MEM_callocN(sizeof(BMEditSelection), "BMEdit Selection"); + ese->htype = ((BMHeader *)data)->htype; + ese->data = data; + BLI_addtail(&(bm->selected), ese); + } +} + +void BM_select_history_validate(BMesh *bm) +{ + BMEditSelection *ese, *nextese; + + ese = bm->selected.first; + + while (ese) { + nextese = ese->next; + if (!BM_elem_flag_test(ese->data, BM_ELEM_SELECT)) { + BLI_freelinkN(&(bm->selected), ese); + } + ese = nextese; + } +} + +void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + BMIter iter; + BMHeader *ele; + int i; + + if (hflag & BM_ELEM_SELECT) { + BM_select_history_clear(bm); + } + + for (i = 0; i < 3; i++) { + if (htype & iter_types[i]) { + ele = BM_iter_new(&iter, bm, iter_types[i], NULL); + for ( ; ele; ele = BM_iter_step(&iter)) { + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, ele, FALSE); + } + BM_elem_flag_disable(ele, hflag); + } + } + } +} + +void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + BMIter iter; + BMHeader *ele; + int i; + + if (hflag & BM_ELEM_SELECT) { + BM_select_history_clear(bm); + } + + for (i = 0; i < 3; i++) { + if (htype & iter_types[i]) { + ele = BM_iter_new(&iter, bm, iter_types[i], NULL); + for ( ; ele; ele = BM_iter_step(&iter)) { + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, ele, TRUE); + } + BM_elem_flag_enable(ele, hflag); + } + } + } +} + +/***************** Mesh Hiding stuff *********** */ + +#define BM_ELEM_HIDE_SET(ele, hide) \ + (hide) ? BM_elem_flag_enable(ele, BM_ELEM_HIDDEN) : BM_elem_flag_disable(ele, BM_ELEM_HIDDEN); + +static void vert_flush_hide_set(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMEdge *e; + int hide = TRUE; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + hide = hide && BM_elem_flag_test(e, BM_ELEM_HIDDEN); + } + + BM_ELEM_HIDE_SET(v, hide); +} + +static void edge_flush_hide(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMFace *f; + int hide = TRUE; + + BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) { + hide = hide && BM_elem_flag_test(f, BM_ELEM_HIDDEN); + } + + BM_ELEM_HIDE_SET(e, hide); +} + +void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide) +{ + /* vert hiding: vert + surrounding edges and faces */ + BMIter iter, fiter; + BMEdge *e; + BMFace *f; + + BM_ELEM_HIDE_SET(v, hide); + + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + BM_ELEM_HIDE_SET(e, hide); + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + BM_ELEM_HIDE_SET(f, hide); + } + } +} + +void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide) +{ + BMIter iter; + BMFace *f; + /* BMVert *v; */ + + /* edge hiding: faces around the edge */ + BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) { + BM_ELEM_HIDE_SET(f, hide); + } + + BM_ELEM_HIDE_SET(e, hide); + + /* hide vertices if necassary */ + vert_flush_hide_set(bm, e->v1); + vert_flush_hide_set(bm, e->v2); +} + +void BM_face_hide_set(BMesh *bm, BMFace *f, int hide) +{ + BMIter iter; + BMLoop *l; + + BM_ELEM_HIDE_SET(f, hide); + + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + edge_flush_hide(bm, l->e); + } + + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + vert_flush_hide_set(bm, l->v); + } +} + +#undef BM_ELEM_HIDE_SET + + +void BM_elem_hide_set(BMesh *bm, void *element, int hide) +{ + BMHeader *h = element; + + /* Follow convention of always deselecting before + * hiding an element */ + if (hide) { + BM_elem_select_set(bm, element, FALSE); + } + + switch (h->htype) { + case BM_VERT: + BM_vert_hide_set(bm, element, hide); + break; + case BM_EDGE: + BM_edge_hide_set(bm, element, hide); + break; + case BM_FACE: + BM_face_hide_set(bm, element, hide); + break; + } +} diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c new file mode 100644 index 00000000000..a432049e238 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -0,0 +1,625 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_mesh.c + * \ingroup bmesh + * + * BM mesh level functions. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_utildefines.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_tessmesh.h" +#include "BKE_customdata.h" +#include "BKE_multires.h" + +#include "ED_mesh.h" + +#include "bmesh_private.h" + +/* used as an extern, defined in bmesh.h */ +int bm_mesh_allocsize_default[4] = {512, 512, 2048, 512}; + +/* bmesh_error stub */ +void bmesh_error(void) +{ + printf("BM modelling error!\n"); + + /* This placeholder assert makes modelling errors easier to catch + * in the debugger, until bmesh_error is replaced with something + * better. */ + BLI_assert(0); +} + +static void bmesh_mempool_init(BMesh *bm, const int allocsize[4]) +{ + bm->vpool = BLI_mempool_create(sizeof(BMVert), allocsize[0], allocsize[0], FALSE, TRUE); + bm->epool = BLI_mempool_create(sizeof(BMEdge), allocsize[1], allocsize[1], FALSE, TRUE); + bm->lpool = BLI_mempool_create(sizeof(BMLoop), allocsize[2], allocsize[2], FALSE, FALSE); + bm->fpool = BLI_mempool_create(sizeof(BMFace), allocsize[3], allocsize[3], FALSE, TRUE); + +#ifdef USE_BMESH_HOLES + bm->looplistpool = BLI_mempool_create(sizeof(BMLoopList), allocsize[3], allocsize[3], FALSE, FALSE); +#endif + + /* allocate one flag pool that we dont get rid of. */ + bm->toolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), 512, 512, FALSE, FALSE); +} + +/* + * BMESH MAKE MESH + * + * Allocates a new BMesh structure. + * Returns - + * Pointer to a BM + * + */ + +BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4]) +{ + /* allocate the structure */ + BMesh *bm = MEM_callocN(sizeof(BMesh), __func__); + + bm->ob = ob; + + /* allocate the memory pools for the mesh elements */ + bmesh_mempool_init(bm, allocsize); + + /* allocate one flag pool that we dont get rid of. */ + bm->stackdepth = 1; + bm->totflags = 1; + + return bm; +} + +/* + * BMESH FREE MESH + * + * Frees a BMesh structure. + */ + +void BM_mesh_data_free(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMLoop *l; + BMFace *f; + + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) { + CustomData_bmesh_free_block(&(bm->vdata), &(v->head.data)); + } + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + CustomData_bmesh_free_block(&(bm->edata), &(e->head.data)); + } + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + CustomData_bmesh_free_block(&(bm->pdata), &(f->head.data)); + for (l = BM_iter_new(&loops, bm, BM_LOOPS_OF_FACE, f); l; l = BM_iter_step(&loops)) { + CustomData_bmesh_free_block(&(bm->ldata), &(l->head.data)); + } + } + + /* Free custom data pools, This should probably go in CustomData_free? */ + if (bm->vdata.totlayer) BLI_mempool_destroy(bm->vdata.pool); + if (bm->edata.totlayer) BLI_mempool_destroy(bm->edata.pool); + if (bm->ldata.totlayer) BLI_mempool_destroy(bm->ldata.pool); + if (bm->pdata.totlayer) BLI_mempool_destroy(bm->pdata.pool); + + /* free custom data */ + CustomData_free(&bm->vdata, 0); + CustomData_free(&bm->edata, 0); + CustomData_free(&bm->ldata, 0); + CustomData_free(&bm->pdata, 0); + + /* destroy element pools */ + BLI_mempool_destroy(bm->vpool); + BLI_mempool_destroy(bm->epool); + BLI_mempool_destroy(bm->lpool); + BLI_mempool_destroy(bm->fpool); + + /* destroy flag pool */ + BLI_mempool_destroy(bm->toolflagpool); + +#ifdef USE_BMESH_HOLES + BLI_mempool_destroy(bm->looplistpool); +#endif + + /* These tables aren't used yet, so it's not stricly necessary + * to 'end' them (with 'e' param) but if someone tries to start + * using them, having these in place will save a lot of pain */ + mesh_octree_table(NULL, NULL, NULL, 'e'); + mesh_mirrtopo_table(NULL, 'e'); + + BLI_freelistN(&bm->selected); + + BMO_error_clear(bm); +} + +void BM_mesh_clear(BMesh *bm) +{ + Object *ob = bm->ob; + + /* free old mesh */ + BM_mesh_data_free(bm); + memset(bm, 0, sizeof(BMesh)); + + /* re-initialize mesh */ + bm->ob = ob; + + /* allocate the memory pools for the mesh elements */ + bmesh_mempool_init(bm, bm_mesh_allocsize_default); + + bm->stackdepth = 1; + bm->totflags = 1; +} + +/* + * BMESH FREE MESH + * + * Frees a BMesh structure. + */ + +void BM_mesh_free(BMesh *bm) +{ + BM_mesh_data_free(bm); + MEM_freeN(bm); +} + +/* + * BMESH COMPUTE NORMALS + * + * Updates the normals of a mesh. + * Note that this can only be called + * + */ + +void BM_mesh_normals_update(BMesh *bm) +{ + BMVert *v; + BMFace *f; + BMLoop *l; + BMEdge *e; + BMIter verts; + BMIter faces; + BMIter loops; + BMIter edges; + unsigned int maxlength = 0; + int index; + float (*projectverts)[3]; + float (*edgevec)[3]; + + /* first, find out the largest face in mesh */ + BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) + continue; + + if (f->len > maxlength) maxlength = f->len; + } + + /* make sure we actually have something to do */ + if (maxlength < 3) return; + + /* allocate projectverts array */ + projectverts = MEM_callocN(sizeof(float) * maxlength * 3, "BM normal computation array"); + + /* calculate all face normals */ + BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) + continue; +#if 0 /* UNUSED */ + if (f->head.flag & BM_NONORMCALC) + continue; +#endif + + bmesh_update_face_normal(bm, f, f->no, projectverts); + } + + /* Zero out vertex normals */ + BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + continue; + + zero_v3(v->no); + } + + /* compute normalized direction vectors for each edge. directions will be + * used below for calculating the weights of the face normals on the vertex + * normals */ + index = 0; + edgevec = MEM_callocN(sizeof(float) * 3 * bm->totedge, "BM normal computation array"); + BM_ITER(e, &edges, bm, BM_EDGES_OF_MESH, NULL) { + BM_elem_index_set(e, index); /* set_inline */ + + if (e->l) { + sub_v3_v3v3(edgevec[index], e->v2->co, e->v1->co); + normalize_v3(edgevec[index]); + } + else { + /* the edge vector will not be needed when the edge has no radial */ + } + + index++; + } + bm->elem_index_dirty &= ~BM_EDGE; + + /* add weighted face normals to vertices */ + BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) { + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) + continue; + + BM_ITER(l, &loops, bm, BM_LOOPS_OF_FACE, f) { + float *e1diff, *e2diff; + float dotprod; + float fac; + + /* calculate the dot product of the two edges that + * meet at the loop's vertex */ + e1diff = edgevec[BM_elem_index_get(l->prev->e)]; + e2diff = edgevec[BM_elem_index_get(l->e)]; + dotprod = dot_v3v3(e1diff, e2diff); + + /* edge vectors are calculated from e->v1 to e->v2, so + * adjust the dot product if one but not both loops + * actually runs from from e->v2 to e->v1 */ + if ((l->prev->e->v1 == l->prev->v) ^ (l->e->v1 == l->v)) { + dotprod = -dotprod; + } + + fac = saacos(-dotprod); + + /* accumulate weighted face normal into the vertex's normal */ + madd_v3_v3fl(l->v->no, f->no, fac); + } + } + + /* normalize the accumulated vertex normals */ + BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + continue; + + if (normalize_v3(v->no) == 0.0f) { + normalize_v3_v3(v->no, v->co); + } + } + + MEM_freeN(edgevec); + MEM_freeN(projectverts); +} + +/* + This function ensures correct normals for the mesh, but + sets the flag BM_ELEM_TAG in flipped faces, to allow restoration + of original normals. + + if undo is 0: calculate right normals + if undo is 1: restore original normals + */ +//keep in sycn with utils.c! +#define FACE_FLIP 8 +static void bmesh_rationalize_normals(BMesh *bm, int undo) +{ + BMOperator bmop; + BMFace *f; + BMIter iter; + + if (undo) { + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_TAG)) { + BM_face_normal_flip(bm, f); + } + BM_elem_flag_disable(f, BM_ELEM_TAG); + } + + return; + } + + BMO_op_initf(bm, &bmop, "righthandfaces faces=%af doflip=%d", FALSE); + + BMO_push(bm, &bmop); + bmesh_righthandfaces_exec(bm, &bmop); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, f, FACE_FLIP)) + BM_elem_flag_enable(f, BM_ELEM_TAG); + else BM_elem_flag_disable(f, BM_ELEM_TAG); + } + + BMO_pop(bm); + BMO_op_finish(bm, &bmop); +} + +static void bmesh_set_mdisps_space(BMesh *bm, int from, int to) +{ + /* switch multires data out of tangent space */ + if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + Object *ob = bm->ob; + BMEditMesh *em = BMEdit_Create(bm, FALSE); + DerivedMesh *dm = CDDM_from_BMEditMesh(em, NULL, TRUE, FALSE); + MDisps *mdisps; + BMFace *f; + BMIter iter; + // int i = 0; // UNUSED + + multires_set_space(dm, ob, from, to); + + mdisps = CustomData_get_layer(&dm->loopData, CD_MDISPS); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BMLoop *l; + BMIter liter; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + MDisps *lmd = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS); + + if (!lmd->disps) { + printf("%s: warning - 'lmd->disps' == NULL\n", __func__); + } + + if (lmd->disps && lmd->totdisp == mdisps->totdisp) { + memcpy(lmd->disps, mdisps->disps, sizeof(float) * 3 * lmd->totdisp); + } + else if (mdisps->disps) { + if (lmd->disps) + MEM_freeN(lmd->disps); + + lmd->disps = MEM_dupallocN(mdisps->disps); + lmd->totdisp = mdisps->totdisp; + } + + mdisps++; + // i += 1; + } + } + + dm->needsFree = 1; + dm->release(dm); + + /* setting this to NULL prevents BMEdit_Free from freeing it */ + em->bm = NULL; + BMEdit_Free(em); + MEM_freeN(em); + } +} + +/* + * BMESH BEGIN/END EDIT + * + * Functions for setting up a mesh for editing and cleaning up after + * the editing operations are done. These are called by the tools/operator + * API for each time a tool is executed. + */ +void bmesh_begin_edit(BMesh *bm, int flag) +{ + bm->opflag = flag; + + /* Most operators seem to be using BMO_OP_FLAG_UNTAN_MULTIRES to change the MDisps to + * absolute space during mesh edits. With this enabled, changes to the topology + * (loop cuts, edge subdivides, etc) are not reflected in the higher levels of + * the mesh at all, which doesn't seem right. Turning off completely for now, + * until this is shown to be better for certain types of mesh edits. */ +#if BMOP_UNTAN_MULTIRES_ENABLED + /* switch multires data out of tangent space */ + if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + bmesh_set_mdisps_space(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE); + + /* ensure correct normals, if possible */ + bmesh_rationalize_normals(bm, 0); + BM_mesh_normals_update(bm); + } + else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 0); + } +#else + if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 0); + } +#endif +} + +void bmesh_end_edit(BMesh *bm, int flag) +{ + /* BMO_OP_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_begin_edit. */ +#if BMOP_UNTAN_MULTIRES_ENABLED + /* switch multires data into tangent space */ + if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* set normals to their previous winding */ + bmesh_rationalize_normals(bm, 1); + bmesh_set_mdisps_space(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT); + } + else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 1); + } +#else + if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 1); + } +#endif + + bm->opflag = 0; + + /* compute normals, clear temp flags and flush selections */ + BM_mesh_normals_update(bm); + BM_mesh_select_mode_flush(bm); +} + +void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag) +{ + BMIter iter; + BMHeader *ele; + +#ifdef DEBUG + BM_ELEM_INDEX_VALIDATE(bm, "Should Never Fail!", __func__); +#endif + + if (hflag & BM_VERT) { + if (bm->elem_index_dirty & BM_VERT) { + int index = 0; + BM_ITER(ele, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + bm->elem_index_dirty &= ~BM_VERT; + BLI_assert(index == bm->totvert); + } + else { + // printf("%s: skipping vert index calc!\n", __func__); + } + } + + if (hflag & BM_EDGE) { + if (bm->elem_index_dirty & BM_EDGE) { + int index = 0; + BM_ITER(ele, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + bm->elem_index_dirty &= ~BM_EDGE; + BLI_assert(index == bm->totedge); + } + else { + // printf("%s: skipping edge index calc!\n", __func__); + } + } + + if (hflag & BM_FACE) { + if (bm->elem_index_dirty & BM_FACE) { + int index = 0; + BM_ITER(ele, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + bm->elem_index_dirty &= ~BM_FACE; + BLI_assert(index == bm->totface); + } + else { + // printf("%s: skipping face index calc!\n", __func__); + } + } +} + + +/* array checking/setting macros */ +/* currently vert/edge/loop/face index data is being abused, but we should + * eventually be able to rely on it being valid. To this end, there are macros + * that validate them (so blender doesnt crash), but also print errors so we can + * fix the offending parts of the code, this way after some months we can + * confine this code for debug mode. + * + * + */ + +void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func, + const char *msg_a, const char *msg_b) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + + const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE}; + const char *type_names[3] = {"vert", "edge", "face"}; + + BMIter iter; + BMHeader *ele; + int i; + int is_any_error = 0; + + for (i = 0; i < 3; i++) { + const int is_dirty = (flag_types[i] & bm->elem_index_dirty); + int index = 0; + int is_error = FALSE; + int err_val = 0; + int err_idx = 0; + + BM_ITER(ele, &iter, bm, iter_types[i], NULL) { + if (!is_dirty) { + if (BM_elem_index_get(ele) != index) { + err_val = BM_elem_index_get(ele); + err_idx = index; + is_error = TRUE; + } + } + + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + + if ((is_error == TRUE) && (is_dirty == FALSE)) { + is_any_error = TRUE; + fprintf(stderr, + "Invalid Index: at %s, %s, %s[%d] invalid index %d, '%s', '%s'\n", + location, func, type_names[i], err_idx, err_val, msg_a, msg_b); + } + else if ((is_error == FALSE) && (is_dirty == TRUE)) { + +#if 0 /* mostly annoying */ + + /* dirty may have been incorrectly set */ + fprintf(stderr, + "Invalid Dirty: at %s, %s (%s), dirty flag was set but all index values are correct, '%s', '%s'\n", + location, func, type_names[i], msg_a, msg_b); +#endif + } + } + +#if 0 /* mostly annoying, even in debug mode */ +#ifdef DEBUG + if (is_any_error == 0) { + fprintf(stderr, + "Valid Index Success: at %s, %s, '%s', '%s'\n", + location, func, msg_a, msg_b); + } +#endif +#endif + (void) is_any_error; /* shut up the compiler */ + +} + +BMVert *BM_vert_at_index(BMesh *bm, const int index) +{ + return BLI_mempool_findelem(bm->vpool, index); +} + +BMEdge *BM_edge_at_index(BMesh *bm, const int index) +{ + return BLI_mempool_findelem(bm->epool, index); +} + +BMFace *BM_face_at_index(BMesh *bm, const int index) +{ + return BLI_mempool_findelem(bm->fpool, index); +} diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c new file mode 100644 index 00000000000..246c8a4655b --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -0,0 +1,769 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_mods.c + * \ingroup bmesh + * + * This file contains functions for locally modifying + * the topology of existing mesh data. (split, join, flip etc). + */ + +#include "MEM_guardedalloc.h" + + +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_smallhash.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/** + * bmesh_dissolve_disk + * + * Turns the face region surrounding a manifold vertex into + * A single polygon. + * + * + * Example: + * + * |=========| |=========| + * | \ / | | | + * Before: | V | After: | | + * | / \ | | | + * |=========| |=========| + * + * + */ +#if 1 +int BM_vert_dissolve(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMEdge *e; + int len = 0; + + if (!v) { + return FALSE; + } + + e = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, v); + for ( ; e; e = BM_iter_step(&iter)) { + len++; + } + + if (len == 1) { + if (v->e) + BM_edge_kill(bm, v->e); + BM_vert_kill(bm, v); + return TRUE; + } + + if (!BM_vert_is_manifold(bm, v)) { + if (!v->e) BM_vert_kill(bm, v); + else if (!v->e->l) { + BM_edge_kill(bm, v->e); + BM_vert_kill(bm, v); + } + else { + return FALSE; + } + + return TRUE; + } + + return BM_disk_dissolve(bm, v); +} + +int BM_disk_dissolve(BMesh *bm, BMVert *v) +{ + BMFace *f, *f2; + BMEdge *e, *keepedge = NULL, *baseedge = NULL; + int len = 0; + + if (!BM_vert_is_manifold(bm, v)) { + return FALSE; + } + + if (v->e) { + /* v->e we keep, what else */ + e = v->e; + do { + e = bmesh_disk_nextedge(e, v); + if (!(BM_edge_share_faces(e, v->e))) { + keepedge = e; + baseedge = v->e; + break; + } + len++; + } while (e != v->e); + } + + /* this code for handling 2 and 3-valence verts + * may be totally bad */ + if (keepedge == NULL && len == 3) { + /* handle specific case for three-valence. solve it by + * increasing valence to four. this may be hackish. . */ + BMLoop *loop = e->l; + if (loop->v == v) loop = loop->next; + if (!BM_face_split(bm, loop->f, v, loop->v, NULL, NULL)) + return FALSE; + + if (!BM_disk_dissolve(bm, v)) { + return FALSE; + } + return TRUE; + } + else if (keepedge == NULL && len == 2) { + /* collapse the verte */ + e = BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE); + + if (!e) { + return FALSE; + } + + /* handle two-valenc */ + f = e->l->f; + f2 = e->l->radial_next->f; + + if (f != f2 && !BM_faces_join_pair(bm, f, f2, e)) { + return FALSE; + } + + return TRUE; + } + + if (keepedge) { + int done = 0; + + while (!done) { + done = 1; + e = v->e; + do { + f = NULL; + len = bmesh_radial_length(e->l); + if (len == 2 && (e != baseedge) && (e != keepedge)) { + f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e); + /* return if couldn't join faces in manifold + * conditions */ + //!disabled for testing why bad things happen + if (!f) { + return FALSE; + } + } + + if (f) { + done = 0; + break; + } + e = bmesh_disk_nextedge(e, v); + } while (e != v->e); + } + + /* collapse the verte */ + e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, TRUE); + + if (!e) { + return FALSE; + } + + /* get remaining two face */ + f = e->l->f; + f2 = e->l->radial_next->f; + + if (f != f2) { + /* join two remaining face */ + if (!BM_faces_join_pair(bm, f, f2, e)) { + return FALSE; + } + } + } + + return TRUE; +} +#else +void BM_disk_dissolve(BMesh *bm, BMVert *v) +{ + BMFace *f; + BMEdge *e; + BMIter iter; + int done, len; + + if (v->e) { + done = 0; + while (!done) { + done = 1; + + /* loop the edges looking for an edge to dissolv */ + for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, v); e; + e = BM_iter_step(&iter)) { + f = NULL; + len = bmesh_cycle_length(&(e->l->radial)); + if (len == 2) { + f = BM_faces_join_pair(bm, e->l->f, ((BMLoop *)(e->l->radial_next))->f, e); + } + if (f) { + done = 0; + break; + } + }; + } + BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE); + } +} +#endif + +/** + * BM_faces_join_pair + * + * Joins two adjacenct faces togather. + * + * Because this method calls to BM_faces_join to do its work, ff a pair + * of faces share multiple edges, the pair of faces will be joined at + * every edge (not just edge e). This part of the functionality might need + * to be reconsidered. + * + * If the windings do not match the winding of the new face will follow + * f1's winding (i.e. f2 will be reversed before the join). + * + * Returns: + * pointer to the combined face + */ + +BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + BMLoop *l1, *l2; + BMEdge *jed = NULL; + BMFace *faces[2] = {f1, f2}; + + jed = e; + if (!jed) { + BMLoop *l_first; + /* search for an edge that has both these faces in its radial cycl */ + l1 = l_first = BM_FACE_FIRST_LOOP(f1); + do { + if (l1->radial_next->f == f2) { + jed = l1->e; + break; + } + } while ((l1 = l1->next) != l_first); + } + + if (!jed) { + bmesh_error(); + return NULL; + } + + l1 = jed->l; + + if (!l1) { + bmesh_error(); + return NULL; + } + + l2 = l1->radial_next; + if (l1->v == l2->v) { + bmesh_loop_reverse(bm, f2); + } + + f1 = BM_faces_join(bm, faces, 2); + + return f1; +} + +/* connects two verts together, automatically (if very naively) finding the + * face they both share (if there is one) and splittling it. use this at your + * own risk, as it doesn't handle the many complex cases it should (like zero-area faces, + * multiple faces, etc). + * + * this is really only meant for cases where you don't know before hand the face + * the two verts belong to for splitting (e.g. the subdivision operator). + */ + +BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf) +{ + BMIter iter, iter2; + BMVert *v; + BMLoop *nl; + BMFace *face; + + /* be warned: this can do weird things in some ngon situation, see BM_LegalSplit */ + for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) { + for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) { + if (v == v2) { + face = BM_face_split(bm, face, v1, v2, &nl, NULL); + + if (nf) *nf = face; + return nl->e; + } + } + } + + return NULL; +} + +/** + * BM_face_split + * + * Splits a single face into two. + * + * f - the original face + * v1 & v2 - vertices which define the split edge, must be different + * nl - pointer which will receive the BMLoop for the split edge in the new face + * + * Notes: the + + * Returns - + * Pointer to the newly created face representing one side of the split + * if the split is successful (and the original original face will be the + * other side). NULL if the split fails. + * + */ + +BMFace *BM_face_split(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **nl, BMEdge *UNUSED(example)) +{ + const int has_mdisp = CustomData_has_layer(&bm->ldata, CD_MDISPS); + BMFace *nf, *of; + + /* do we have a multires layer */ + if (has_mdisp) { + of = BM_face_copy(bm, f, 0, 0); + } + +#ifdef USE_BMESH_HOLES + nf = bmesh_sfme(bm, f, v1, v2, nl, NULL); +#else + nf = bmesh_sfme(bm, f, v1, v2, nl); +#endif + + if (nf) { + BM_elem_attrs_copy(bm, bm, f, nf); + copy_v3_v3(nf->no, f->no); + + /* handle multires update */ + if (has_mdisp && (nf != f)) { + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE); + } while ((l_iter = l_iter->next) != l_first); + + l_iter = l_first = BM_FACE_FIRST_LOOP(nf); + do { + BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE); + } while ((l_iter = l_iter->next) != l_first); + + BM_face_kill(bm, of); + + BM_face_multires_bounds_smooth(bm, f); + BM_face_multires_bounds_smooth(bm, nf); + } + } + + return nf; +} + +/** + * BM_vert_collapse_faces + * + * Collapses a vertex that has only two manifold edges + * onto a vertex it shares an edge with. Fac defines + * the amount of interpolation for Custom Data. + * + * Note that this is not a general edge collapse function. + * + * Note this function is very close to 'BM_vert_collapse_edges', both collapse + * a vertex and return a new edge. Except this takes a factor and merges + * custom data. + * + * BMESH_TODO: + * Insert error checking for KV valance. + * + * @param fac The factor along the edge + * @param join_faces When true the faces around the vertex will be joined + * otherwise collapse the vertex by merging the 2 edges this vert touches into one. + * @returns The New Edge + */ + +BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces) +{ + BMEdge *ne = NULL; + BMVert *tv = bmesh_edge_getothervert(ke, kv); + + BMEdge *e2; + BMVert *tv2; + + BMIter iter; + BMLoop *l_iter = NULL, *kvloop = NULL, *tvloop = NULL; + + void *src[2]; + float w[2]; + + /* Only intended to be called for 2-valence vertices */ + BLI_assert(bmesh_disk_count(kv) <= 2); + + + /* first modify the face loop data */ + w[0] = 1.0f - fac; + w[1] = fac; + + if (ke->l) { + l_iter = ke->l; + do { + if (l_iter->v == tv && l_iter->next->v == kv) { + tvloop = l_iter; + kvloop = l_iter->next; + + src[0] = kvloop->head.data; + src[1] = tvloop->head.data; + CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, kvloop->head.data); + } + } while ((l_iter = l_iter->radial_next) != ke->l); + } + + /* now interpolate the vertex data */ + BM_data_interp_from_verts(bm, kv, tv, kv, fac); + + e2 = bmesh_disk_nextedge(ke, kv); + tv2 = BM_edge_other_vert(e2, kv); + + if (join_faces) { + BMFace **faces = NULL, *f; + BLI_array_staticdeclare(faces, 8); + + BM_ITER(f, &iter, bm, BM_FACES_OF_VERT, kv) { + BLI_array_append(faces, f); + } + + if (BLI_array_count(faces) >= 2) { + BMFace *f2 = BM_faces_join(bm, faces, BLI_array_count(faces)); + if (f2) { + BMLoop *nl = NULL; + if (BM_face_split(bm, f2, tv, tv2, &nl, NULL)) { + ne = nl->e; + } + } + } + + BLI_array_free(faces); + + return ne; + } + + /* single face or no faces */ + /* same as BM_vert_collapse_edges() however we already + * have vars to perform this operation so dont call. */ + bmesh_jekv(bm, ke, kv); + ne = BM_edge_exists(tv, tv2); + + return ne; +} + + +/** + * BM_vert_collapse_edges + * + * Collapses a vertex onto another vertex it shares an edge with. + * + * Returns - + * The New Edge + */ + +BMEdge *BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + /* nice example implimentation but we want loops to have their customdata + * accounted for */ +#if 0 + BMEdge *ne = NULL; + + /* Collapse between 2 edges */ + + /* in this case we want to keep all faces and not join them, + * rather just get rid of the veretex - see bug [#28645] */ + BMVert *tv = bmesh_edge_getothervert(ke, kv); + if (tv) { + BMEdge *e2 = bmesh_disk_nextedge(ke, kv); + if (e2) { + BMVert *tv2 = BM_edge_other_vert(e2, kv); + if (tv2) { + /* only action, other calls here only get the edge to return */ + bmesh_jekv(bm, ke, kv); + + ne = BM_edge_exists(tv, tv2); + } + } + } + + return ne; +#else + /* with these args faces are never joined, same as above + * but account for loop customdata */ + return BM_vert_collapse_faces(bm, ke, kv, 1.0f, FALSE); +#endif +} + +#undef DO_V_INTERP + +/** + * BM_split_edge + * + * Splits an edge. v should be one of the vertices in e and + * defines the direction of the splitting operation for interpolation + * purposes. + * + * Returns - + * the new vert + */ + +BMVert *BM_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent) +{ + BMVert *nv, *v2; + BMFace **oldfaces = NULL; + BMEdge *dummy; + BLI_array_staticdeclare(oldfaces, 32); + SmallHash hash; + + /* we need this for handling multire */ + if (!ne) + ne = &dummy; + + /* do we have a multires layer */ + if (CustomData_has_layer(&bm->ldata, CD_MDISPS) && e->l) { + BMLoop *l; + int i; + + l = e->l; + do { + BLI_array_append(oldfaces, l->f); + l = l->radial_next; + } while (l != e->l); + + /* create a hash so we can differentiate oldfaces from new face */ + BLI_smallhash_init(&hash); + + for (i = 0; i < BLI_array_count(oldfaces); i++) { + oldfaces[i] = BM_face_copy(bm, oldfaces[i], 1, 1); + BLI_smallhash_insert(&hash, (intptr_t)oldfaces[i], NULL); + } + } + + v2 = bmesh_edge_getothervert(e, v); + nv = bmesh_semv(bm, v, e, ne); + if (nv == NULL) { + return NULL; + } + + sub_v3_v3v3(nv->co, v2->co, v->co); + madd_v3_v3v3fl(nv->co, v->co, nv->co, percent); + + if (ne) { + (*ne)->head.hflag = e->head.hflag; + BM_elem_attrs_copy(bm, bm, e, *ne); + } + + /* v->nv->v2 */ + BM_data_interp_face_vert_edge(bm, v2, v, nv, e, percent); + BM_data_interp_from_verts(bm, v, v2, nv, percent); + + if (CustomData_has_layer(&bm->ldata, CD_MDISPS) && e->l && nv) { + int i, j; + + /* interpolate new/changed loop data from copied old face */ + for (j = 0; j < 2; j++) { + for (i = 0; i < BLI_array_count(oldfaces); i++) { + BMEdge *e1 = j ? *ne : e; + BMLoop *l, *l2; + + l = e1->l; + if (!l) { + bmesh_error(); + break; + } + + do { + if (!BLI_smallhash_haskey(&hash, (intptr_t)l->f)) { + BMLoop *l2_first; + + l2 = l2_first = BM_FACE_FIRST_LOOP(l->f); + do { + BM_loop_interp_multires(bm, l2, oldfaces[i]); + } while ((l2 = l2->next) != l2_first); + } + l = l->radial_next; + } while (l != e1->l); + } + } + + /* destroy the old face */ + for (i = 0; i < BLI_array_count(oldfaces); i++) { + BM_face_verts_kill(bm, oldfaces[i]); + } + + /* fix boundaries a bit, doesn't work too well quite ye */ +#if 0 + for (j = 0; j < 2; j++) { + BMEdge *e1 = j ? *ne : e; + BMLoop *l, *l2; + + l = e1->l; + if (!l) { + bmesh_error(); + break; + } + + do { + BM_face_multires_bounds_smooth(bm, l->f); + l = l->radial_next; + } while (l != e1->l); + } +#endif + + BLI_array_free(oldfaces); + BLI_smallhash_release(&hash); + } + + return nv; +} + +BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts) +{ + int i; + float percent; + BMVert *nv = NULL; + + for (i = 0; i < numcuts; i++) { + percent = 1.0f / (float)(numcuts + 1 - i); + nv = BM_edge_split(bm, e->v2, e, NULL, percent); + } + return nv; +} + +int BM_face_validate(BMesh *bm, BMFace *face, FILE *err) +{ + BMIter iter; + BLI_array_declare(verts); + BMVert **verts = NULL; + BMLoop *l; + int ret = 1, i, j; + + if (face->len == 2) { + fprintf(err, "warning: found two-edged face. face ptr: %p\n", face); + fflush(err); + } + + for (l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, face); l; l = BM_iter_step(&iter)) { + BLI_array_growone(verts); + verts[BLI_array_count(verts) - 1] = l->v; + + if (l->e->v1 == l->e->v2) { + fprintf(err, "Found bmesh edge with identical verts!\n"); + fprintf(err, " edge ptr: %p, vert: %p\n", l->e, l->e->v1); + fflush(err); + ret = 0; + } + } + + for (i = 0; i < BLI_array_count(verts); i++) { + for (j = 0; j < BLI_array_count(verts); j++) { + if (j == i) { + continue; + } + + if (verts[i] == verts[j]) { + fprintf(err, "Found duplicate verts in bmesh face!\n"); + fprintf(err, " face ptr: %p, vert: %p\n", face, verts[i]); + fflush(err); + ret = 0; + } + } + } + + BLI_array_free(verts); + return ret; +} + +/* + * BM Rotate Edge + * + * Spins an edge topologically, either counter-clockwise or clockwise. + * If ccw is true, the edge is spun counter-clockwise, otherwise it is + * spun clockwise. + * + * Returns the spun edge. Note that this works by dissolving the edge + * then re-creating it, so the returned edge won't have the same pointer + * address as the original one. + * + * Returns NULL on error (e.g., if the edge isn't surrounded by exactly + * two faces). + */ +BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw) +{ + BMVert *v1, *v2; + BMLoop *l, *l1, *l2, *nl; + BMFace *f; + BMIter liter; + + v1 = e->v1; + v2 = e->v2; + + if (BM_edge_face_count(e) != 2) + return NULL; + + /* If either of e's vertices has valence 2, then + * dissolving the edge would leave a spur, so not allowed */ + if (BM_vert_edge_count(e->v1) == 2 || BM_vert_edge_count(e->v2) == 2) + return NULL; + + f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e); + + if (f == NULL) + return NULL; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (l->v == v1) + l1 = l; + else if (l->v == v2) + l2 = l; + } + + if (ccw) { + l1 = l1->prev; + l2 = l2->prev; + } + else { + l1 = l1->next; + l2 = l2->next; + } + + if (!BM_face_split(bm, f, l1->v, l2->v, &nl, NULL)) + return NULL; + + return nl->e; +} + +BMVert *BM_vert_rip ( BMesh *bm, BMFace *sf, BMVert *sv) +{ + return bmesh_urmv(bm, sf, sv); +} diff --git a/source/blender/bmesh/intern/bmesh_newcore.c b/source/blender/bmesh/intern/bmesh_newcore.c new file mode 100644 index 00000000000..47b8536b3df --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_newcore.c @@ -0,0 +1,2024 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_newcore.c + * \ingroup bmesh + * + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math_vector.h" + +#include "BKE_DerivedMesh.h" + +#include "BLI_listbase.h" +#include "BLI_array.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* use so valgrinds memcheck alerts us when undefined index is used. + * TESTING ONLY! */ +// #define USE_DEBUG_INDEX_MEMCHECK + +#ifdef USE_DEBUG_INDEX_MEMCHECK +#define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \ + { \ + int undef_idx; \ + BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \ + } \ + +#endif + +BMVert *BM_vert_create(BMesh *bm, const float co[3], const struct BMVert *example) +{ + BMVert *v = BLI_mempool_calloc(bm->vpool); + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(v) +#else + BM_elem_index_set(v, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_VERT; /* may add to middle of the pool */ + + bm->totvert++; + + v->head.htype = BM_VERT; + + /* 'v->no' is handled by BM_elem_attrs_copy */ + if (co) copy_v3_v3(v->co, co); + + /* allocate flag */ + v->oflags = BLI_mempool_calloc(bm->toolflagpool); + + CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + + if (example) { + BM_elem_attrs_copy(bm, bm, (BMVert *)example, (BMVert *)v); + } + + BM_CHECK_ELEMENT(bm, v); + + return (BMVert *) v; +} + +/** + * BMESH EDGE EXIST + * + * Finds out if two vertices already have an edge + * connecting them. Note that multiple edges may + * exist between any two vertices, and therefore + * This function only returns the first one found. + * + * Returns - + * BMEdge pointer + */ +BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2) +{ + BMIter iter; + BMEdge *e; + + BM_ITER(e, &iter, NULL, BM_EDGES_OF_VERT, v1) { + if (e->v1 == v2 || e->v2 == v2) + return e; + } + + return NULL; +} + +BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble) +{ + BMEdge *e; + + if (nodouble && (e = BM_edge_exists(v1, v2))) + return (BMEdge *)e; + + e = BLI_mempool_calloc(bm->epool); + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(e) +#else + BM_elem_index_set(e, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_EDGE; /* may add to middle of the pool */ + + bm->totedge++; + + e->head.htype = BM_EDGE; + + /* allocate flag */ + e->oflags = BLI_mempool_calloc(bm->toolflagpool); + + e->v1 = (BMVert *) v1; + e->v2 = (BMVert *) v2; + + + CustomData_bmesh_set_default(&bm->edata, &e->head.data); + + bmesh_disk_append_edge(e, e->v1); + bmesh_disk_append_edge(e, e->v2); + + if (example) + BM_elem_attrs_copy(bm, bm, (BMEdge *)example, (BMEdge *)e); + + BM_CHECK_ELEMENT(bm, e); + + return (BMEdge *) e; +} + +static BMLoop *bmesh_create_loop(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, const BMLoop *example) +{ + BMLoop *l = NULL; + + l = BLI_mempool_calloc(bm->lpool); + l->next = l->prev = NULL; + l->v = v; + l->e = e; + l->f = f; + l->radial_next = l->radial_prev = NULL; + l->head.data = NULL; + l->head.htype = BM_LOOP; + + bm->totloop++; + + if (example) + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, example->head.data, &l->head.data); + else + CustomData_bmesh_set_default(&bm->ldata, &l->head.data); + + return l; +} + +static BMLoop *bm_face_boundry_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool); +#endif + BMLoop *l = bmesh_create_loop(bm, startv, starte, f, NULL); + + bmesh_radial_append(starte, l); + +#ifdef USE_BMESH_HOLES + lst->first = lst->last = l; + BLI_addtail(&f->loops, lst); +#else + f->l_first = l; +#endif + + l->f = f; + + return l; +} + +BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts) +{ + BMEdge **edges = NULL; + BMVert **verts = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + BMLoop *l_iter; + BMLoop *l_first; + BMLoop *l2; + BMFace *f2; + int i; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (copyverts) { + BMVert *v = BM_vert_create(bm, l_iter->v->co, l_iter->v); + BLI_array_append(verts, v); + } + else { + BLI_array_append(verts, l_iter->v); + } + } while ((l_iter = l_iter->next) != l_first); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + i = 0; + do { + if (copyedges) { + BMEdge *e; + BMVert *v1, *v2; + + if (l_iter->e->v1 == verts[i]) { + v1 = verts[i]; + v2 = verts[(i + 1) % f->len]; + } + else { + v2 = verts[i]; + v1 = verts[(i + 1) % f->len]; + } + + e = BM_edge_create(bm, v1, v2, l_iter->e, FALSE); + BLI_array_append(edges, e); + } + else { + BLI_array_append(edges, l_iter->e); + } + + i++; + } while ((l_iter = l_iter->next) != l_first); + + f2 = BM_face_create(bm, verts, edges, f->len, FALSE); + + BM_elem_attrs_copy(bm, bm, f, f2); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + l2 = BM_FACE_FIRST_LOOP(f2); + do { + BM_elem_attrs_copy(bm, bm, l_iter, l2); + l2 = l2->next; + } while ((l_iter = l_iter->next) != l_first); + + return f2; +} + +BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble) +{ + BMFace *f = NULL; + BMLoop *l, *startl, *lastl; + int i, overlap; + + if (len == 0) { + /* just return NULL for no */ + return NULL; + } + + if (nodouble) { + /* Check if face already exists */ + overlap = BM_face_exists(bm, verts, len, &f); + if (overlap) { + return f; + } + else { + BLI_assert(f == NULL); + } + } + + f = BLI_mempool_calloc(bm->fpool); + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(f) +#else + BM_elem_index_set(f, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */ + + bm->totface++; + + f->head.htype = BM_FACE; + + startl = lastl = bm_face_boundry_add(bm, (BMFace *)f, verts[0], edges[0]); + + startl->v = (BMVert *)verts[0]; + startl->e = (BMEdge *)edges[0]; + for (i = 1; i < len; i++) { + l = bmesh_create_loop(bm, verts[i], edges[i], (BMFace *)f, edges[i]->l); + + l->f = (BMFace *) f; + bmesh_radial_append(edges[i], l); + + l->prev = lastl; + lastl->next = l; + lastl = l; + } + + /* allocate flag */ + f->oflags = BLI_mempool_calloc(bm->toolflagpool); + + CustomData_bmesh_set_default(&bm->pdata, &f->head.data); + + startl->prev = lastl; + lastl->next = startl; + + f->len = len; + +#ifdef USE_BMESH_HOLES + f->totbounds = 0; +#endif + + BM_CHECK_ELEMENT(bm, f); + + return (BMFace *) f; +} + +int bmesh_check_element(BMesh *UNUSED(bm), void *element, const char htype) +{ + BMHeader *head = element; + int err = 0; + + if (!element) + return 1; + + if (head->htype != htype) + return 2; + + switch (htype) { + case BM_VERT: { + BMVert *v = element; + if (v->e && v->e->head.htype != BM_EDGE) { + err |= 4; + } + break; + } + case BM_EDGE: { + BMEdge *e = element; + if (e->l && e->l->head.htype != BM_LOOP) + err |= 8; + if (e->l && e->l->f->head.htype != BM_FACE) + err |= 16; + if (e->v1_disk_link.prev == NULL || + e->v2_disk_link.prev == NULL || + e->v1_disk_link.next == NULL || + e->v2_disk_link.next == NULL) + { + err |= 32; + } + if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL)) + err |= 64; + if (e->l && e->l->f->len <= 0) + err |= 128; + break; + } + case BM_LOOP: { + BMLoop *l = element, *l2; + int i; + + if (l->f->head.htype != BM_FACE) + err |= 256; + if (l->e->head.htype != BM_EDGE) + err |= 512; + if (l->v->head.htype != BM_VERT) + err |= 1024; + if (!BM_vert_in_edge(l->e, l->v)) { + fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__); + err |= 2048; + } + + if (l->radial_next == NULL || l->radial_prev == NULL) + err |= (1 << 12); + if (l->f->len <= 0) + err |= (1 << 13); + + /* validate boundary loop--invalid for hole loops, of course, + * but we won't be allowing those for a while ye */ + l2 = l; + i = 0; + do { + if (i >= BM_NGON_MAX) { + break; + } + + i++; + } while ((l2 = l2->next) != l); + + if (i != l->f->len || l2 != l) + err |= (1 << 14); + + if (!bmesh_radial_validate(bmesh_radial_length(l), l)) + err |= (1 << 15); + + break; + } + case BM_FACE: { + BMFace *f = element; + BMLoop *l_iter; + BMLoop *l_first; + int len = 0; + +#ifdef USE_BMESH_HOLES + if (!f->loops.first) +#else + if (!f->l_first) +#endif + { + err |= (1 << 16); + } + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (l_iter->f != f) { + fprintf(stderr, "%s: loop inside one face points to another! (bmesh internal error)\n", __func__); + err |= (1 << 17); + } + + if (!l_iter->e) + err |= (1 << 18); + if (!l_iter->v) + err |= (1 << 19); + if (!BM_vert_in_edge(l_iter->e, l_iter->v) || !BM_vert_in_edge(l_iter->e, l_iter->next->v)) { + err |= (1 << 20); + } + + if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter)) + err |= (1 << 21); + + if (!bmesh_disk_count(l_iter->v) || !bmesh_disk_count(l_iter->next->v)) + err |= (1 << 22); + + len++; + } while ((l_iter = l_iter->next) != l_first); + + if (len != f->len) + err |= (1 << 23); + } + } + + if (err) { + bmesh_error(); + } + + return err; +} + +/* low level function, only free's, + * does not change adjust surrounding geometry */ +static void bmesh_kill_only_vert(BMesh *bm, BMVert *v) +{ + bm->totvert--; + bm->elem_index_dirty |= BM_VERT; + + BM_select_history_remove(bm, v); + if (v->head.data) + CustomData_bmesh_free_block(&bm->vdata, &v->head.data); + + BLI_mempool_free(bm->toolflagpool, v->oflags); + BLI_mempool_free(bm->vpool, v); +} + +static void bmesh_kill_only_edge(BMesh *bm, BMEdge *e) +{ + bm->totedge--; + bm->elem_index_dirty |= BM_EDGE; + + BM_select_history_remove(bm, e); + + if (e->head.data) + CustomData_bmesh_free_block(&bm->edata, &e->head.data); + + BLI_mempool_free(bm->toolflagpool, e->oflags); + BLI_mempool_free(bm->epool, e); +} + +static void bmesh_kill_only_face(BMesh *bm, BMFace *f) +{ + if (bm->act_face == f) + bm->act_face = NULL; + + bm->totface--; + bm->elem_index_dirty |= BM_FACE; + + BM_select_history_remove(bm, f); + + if (f->head.data) + CustomData_bmesh_free_block(&bm->pdata, &f->head.data); + + BLI_mempool_free(bm->toolflagpool, f->oflags); + BLI_mempool_free(bm->fpool, f); +} + +static void bmesh_kill_only_loop(BMesh *bm, BMLoop *l) +{ + bm->totloop--; + if (l->head.data) + CustomData_bmesh_free_block(&bm->ldata, &l->head.data); + + BLI_mempool_free(bm->lpool, l); +} + +void BM_face_edges_kill(BMesh *bm, BMFace *f) +{ + BMEdge **edges = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BMLoop *l_iter; + BMLoop *l_first; + int i; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BLI_array_append(edges, l_iter->e); + } while ((l_iter = l_iter->next) != l_first); + + for (i = 0; i < BLI_array_count(edges); i++) { + BM_edge_kill(bm, edges[i]); + } + + BLI_array_free(edges); +} + +void BM_face_verts_kill(BMesh *bm, BMFace *f) +{ + BMVert **verts = NULL; + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + BMLoop *l_iter; + BMLoop *l_first; + int i; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BLI_array_append(verts, l_iter->v); + } while ((l_iter = l_iter->next) != l_first); + + for (i = 0; i < BLI_array_count(verts); i++) { + BM_vert_kill(bm, verts[i]); + } + + BLI_array_free(verts); +} + +void BM_face_kill(BMesh *bm, BMFace *f) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *ls, *ls_next; +#endif + + BM_CHECK_ELEMENT(bm, f); + +#ifdef USE_BMESH_HOLES + for (ls = f->loops.first; ls; ls = ls_next) +#else + if (f->l_first) +#endif + { + BMLoop *l_iter, *l_next, *l_first; + +#ifdef USE_BMESH_HOLES + ls_next = ls->next; + l_iter = l_first = ls->first; +#else + l_iter = l_first = f->l_first; +#endif + + do { + l_next = l_iter->next; + + bmesh_radial_remove_loop(l_iter, l_iter->e); + bmesh_kill_only_loop(bm, l_iter); + + } while ((l_iter = l_next) != l_first); + +#ifdef USE_BMESH_HOLES + BLI_mempool_free(bm->looplistpool, ls); +#endif + } + + bmesh_kill_only_face(bm, f); +} + +void BM_edge_kill(BMesh *bm, BMEdge *e) +{ + + bmesh_disk_remove_edge(e, e->v1); + bmesh_disk_remove_edge(e, e->v2); + + if (e->l) { + BMLoop *l = e->l, *lnext, *startl = e->l; + + do { + lnext = l->radial_next; + if (lnext->f == l->f) { + BM_face_kill(bm, l->f); + break; + } + + BM_face_kill(bm, l->f); + + if (l == lnext) + break; + l = lnext; + } while (l != startl); + } + + bmesh_kill_only_edge(bm, e); +} + +void BM_vert_kill(BMesh *bm, BMVert *v) +{ + if (v->e) { + BMEdge *e, *nexte; + + e = v->e; + while (v->e) { + nexte = bmesh_disk_nextedge(e, v); + BM_edge_kill(bm, e); + e = nexte; + } + } + + bmesh_kill_only_vert(bm, v); +} + +/********** private disk and radial cycle functions ********** */ + +/** + * bmesh_loop_reverse + * + * FLIP FACE EULER + * + * Changes the winding order of a face from CW to CCW or vice versa. + * This euler is a bit peculiar in compairson to others as it is its + * own inverse. + * + * BMESH_TODO: reinsert validation code. + * + * Returns - + * 1 for success, 0 for failure. + */ + +static int bmesh_loop_length(BMLoop *l) +{ + BMLoop *l_first = l; + int i = 0; + + do { + i++; + } while ((l = l->next) != l_first); + + return i; +} + +static int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f +#ifdef USE_BMESH_HOLES + , BMLoopList *lst +#endif + ) +{ + +#ifdef USE_BMESH_HOLES + BMLoop *l_first = lst->first; +#else + BMLoop *l_first = f->l_first; +#endif + + BMLoop *l_iter, *oldprev, *oldnext; + BMEdge **edar = NULL; + MDisps *md; + BLI_array_staticdeclare(edar, BM_NGON_STACK_SIZE); + int i, j, edok, len = 0, do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS); + + len = bmesh_loop_length(l_first); + + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { + BMEdge *curedge = l_iter->e; + bmesh_radial_remove_loop(l_iter, curedge); + BLI_array_append(edar, curedge); + } + + /* actually reverse the loop */ + for (i = 0, l_iter = l_first; i < len; i++) { + oldnext = l_iter->next; + oldprev = l_iter->prev; + l_iter->next = oldprev; + l_iter->prev = oldnext; + l_iter = oldnext; + + if (do_disps) { + float (*co)[3]; + int x, y, sides; + + md = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS); + if (!md->totdisp || !md->disps) + continue; + + sides = (int)sqrt(md->totdisp); + co = md->disps; + + for (x = 0; x < sides; x++) { + for (y = 0; y < x; y++) { + swap_v3_v3(co[y * sides + x], co[sides * x + y]); + } + } + } + } + + if (len == 2) { /* two edged face */ + /* do some verification here! */ + l_first->e = edar[1]; + l_first->next->e = edar[0]; + } + else { + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { + edok = 0; + for (j = 0; j < len; j++) { + edok = bmesh_verts_in_edge(l_iter->v, l_iter->next->v, edar[j]); + if (edok) { + l_iter->e = edar[j]; + break; + } + } + } + } + /* rebuild radia */ + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) + bmesh_radial_append(l_iter->e, l_iter); + + /* validate radia */ + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { + BM_CHECK_ELEMENT(bm, l_iter); + BM_CHECK_ELEMENT(bm, l_iter->e); + BM_CHECK_ELEMENT(bm, l_iter->v); + BM_CHECK_ELEMENT(bm, l_iter->f); + } + + BLI_array_free(edar); + + BM_CHECK_ELEMENT(bm, f); + + return 1; +} + +int bmesh_loop_reverse(BMesh *bm, BMFace *f) +{ +#ifdef USE_BMESH_HOLES + return bmesh_loop_reverse_loop(bm, f, f->loops.first); +#else + return bmesh_loop_reverse_loop(bm, f); +#endif +} + +static void bmesh_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag) +{ + BMHeader **eles = veles; + int i; + + for (i = 0; i < tot; i++) { + BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], flag); + } +} + +static void bmesh_clear_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag) +{ + BMHeader **eles = veles; + int i; + + for (i = 0; i < tot; i++) { + BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], flag); + } +} + +#define FACE_MARK (1 << 10) + +static int count_flagged_radial(BMesh *bm, BMLoop *l, int flag) +{ + BMLoop *l2 = l; + int i = 0, c = 0; + + do { + if (!l2) { + bmesh_error(); + goto error; + } + + i += BM_ELEM_API_FLAG_TEST(l2->f, flag) ? 1 : 0; + l2 = bmesh_radial_nextloop(l2); + if (c >= BM_LOOP_RADIAL_MAX) { + bmesh_error(); + goto error; + } + c++; + } while (l2 != l); + + return i; + +error: + BMO_error_raise(bm, bm->currentop, BMERR_MESH_ERROR, NULL); + return 0; +} + +static int UNUSED_FUNCTION(count_flagged_disk)(BMVert *v, int flag) +{ + BMEdge *e = v->e; + int i = 0; + + if (!e) + return 0; + + do { + i += BM_ELEM_API_FLAG_TEST(e, flag) ? 1 : 0; + e = bmesh_disk_nextedge(e, v); + } while (e != v->e); + + return i; +} + +static int disk_is_flagged(BMVert *v, int flag) +{ + BMEdge *e = v->e; + + if (!e) + return FALSE; + + do { + BMLoop *l = e->l; + + if (!l) { + return FALSE; + } + + if (bmesh_radial_length(l) == 1) + return FALSE; + + do { + if (!BM_ELEM_API_FLAG_TEST(l->f, flag)) + return FALSE; + + l = l->radial_next; + } while (l != e->l); + + e = bmesh_disk_nextedge(e, v); + } while (e != v->e); + + return TRUE; +} + +/* Midlevel Topology Manipulation Functions */ + +/* + * BM_faces_join + * + * Joins a collected group of faces into one. Only restriction on + * the input data is that the faces must be connected to each other. + * + * If a pair of faces share multiple edges, the pair of + * faces will be joined at every edge. + * + * Returns a pointer to the combined face. + */ +BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface) +{ + BMFace *f, *newf; +#ifdef USE_BMESH_HOLES + BMLoopList *lst; + ListBase holes = {NULL, NULL}; +#endif + BMLoop *l_iter; + BMLoop *l_first; + BMEdge **edges = NULL; + BMEdge **deledges = NULL; + BMVert **delverts = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BLI_array_staticdeclare(deledges, BM_NGON_STACK_SIZE); + BLI_array_staticdeclare(delverts, BM_NGON_STACK_SIZE); + BMVert *v1 = NULL, *v2 = NULL; + const char *err = NULL; + int i, tote = 0; + + if (!totface) { + bmesh_error(); + return NULL; + } + + if (totface == 1) + return faces[0]; + + bmesh_systag_elements(bm, faces, totface, _FLAG_JF); + + for (i = 0; i < totface; i++) { + f = faces[i]; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + int rlen = count_flagged_radial(bm, l_iter, _FLAG_JF); + + if (rlen > 2) { + err = "Input faces do not form a contiguous manifold region"; + goto error; + } + else if (rlen == 1) { + BLI_array_append(edges, l_iter->e); + + if (!v1) { + v1 = l_iter->v; + v2 = BM_edge_other_vert(l_iter->e, l_iter->v); + } + tote++; + } + else if (rlen == 2) { + int d1, d2; + + d1 = disk_is_flagged(l_iter->e->v1, _FLAG_JF); + d2 = disk_is_flagged(l_iter->e->v2, _FLAG_JF); + + if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) { + /* don't remove an edge it makes up the side of another face + * else this will remove the face as well - campbell */ + if (BM_edge_face_count(l_iter->e) <= 2) { + BLI_array_append(deledges, l_iter->e); + BM_ELEM_API_FLAG_ENABLE(l_iter->e, _FLAG_JF); + } + } + else { + if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) { + BLI_array_append(delverts, l_iter->e->v1); + BM_ELEM_API_FLAG_ENABLE(l_iter->e->v1, _FLAG_JF); + } + + if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) { + BLI_array_append(delverts, l_iter->e->v2); + BM_ELEM_API_FLAG_ENABLE(l_iter->e->v2, _FLAG_JF); + } + } + } + } while ((l_iter = l_iter->next) != l_first); + +#ifdef USE_BMESH_HOLES + for (lst = f->loops.first; lst; lst = lst->next) { + if (lst == f->loops.first) continue; + + BLI_remlink(&f->loops, lst); + BLI_addtail(&holes, lst); + } +#endif + + } + + /* create region fac */ + newf = BM_face_create_ngon(bm, v1, v2, edges, tote, FALSE); + if (!newf || BMO_error_occurred(bm)) { + if (!BMO_error_occurred(bm)) + err = "Invalid boundary region to join faces"; + goto error; + } + + /* copy over loop data */ + l_iter = l_first = BM_FACE_FIRST_LOOP(newf); + do { + BMLoop *l2 = l_iter->radial_next; + + do { + if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF)) + break; + l2 = l2->radial_next; + } while (l2 != l_iter); + + if (l2 != l_iter) { + /* I think this is correct */ + if (l2->v != l_iter->v) { + l2 = l2->next; + } + + BM_elem_attrs_copy(bm, bm, l2, l_iter); + } + } while ((l_iter = l_iter->next) != l_first); + + BM_elem_attrs_copy(bm, bm, faces[0], newf); + +#ifdef USE_BMESH_HOLES + /* add hole */ + BLI_movelisttolist(&newf->loops, &holes); +#endif + + /* update loop face pointer */ +#ifdef USE_BMESH_HOLES + for (lst = newf->loops.first; lst; lst = lst->next) +#endif + { +#ifdef USE_BMESH_HOLES + l_iter = l_first = lst->first; +#else + l_iter = l_first = BM_FACE_FIRST_LOOP(newf); +#endif + do { + l_iter->f = newf; + } while ((l_iter = l_iter->next) != l_first); + } + + bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF); + BM_ELEM_API_FLAG_DISABLE(newf, _FLAG_JF); + + /* handle multires data */ + if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(newf); + do { + for (i = 0; i < totface; i++) { + BM_loop_interp_multires(bm, l_iter, faces[i]); + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* delete old geometr */ + for (i = 0; i < BLI_array_count(deledges); i++) { + BM_edge_kill(bm, deledges[i]); + } + + for (i = 0; i < BLI_array_count(delverts); i++) { + BM_vert_kill(bm, delverts[i]); + } + + BLI_array_free(edges); + BLI_array_free(deledges); + BLI_array_free(delverts); + + BM_CHECK_ELEMENT(bm, newf); + return newf; +error: + bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF); + BLI_array_free(edges); + BLI_array_free(deledges); + BLI_array_free(delverts); + + if (err) { + BMO_error_raise(bm, bm->currentop, BMERR_DISSOLVEFACES_FAILED, err); + } + return NULL; +} + +static BMFace *bmesh_addpolylist(BMesh *bm, BMFace *UNUSED(example)) +{ + BMFace *f; +#ifdef USE_BMESH_HOLES + BMLoopList *lst; +#endif + + f = BLI_mempool_calloc(bm->fpool); +#ifdef USE_BMESH_HOLES + lst = BLI_mempool_calloc(bm->looplistpool); +#endif + + f->head.htype = BM_FACE; +#ifdef USE_BMESH_HOLES + BLI_addtail(&f->loops, lst); +#endif + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(f) +#else + BM_elem_index_set(f, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */ + + bm->totface++; + + /* allocate flag */ + f->oflags = BLI_mempool_calloc(bm->toolflagpool); + + CustomData_bmesh_set_default(&bm->pdata, &f->head.data); + + f->len = 0; + +#ifdef USE_BMESH_HOLES + f->totbounds = 1; +#endif + + return (BMFace *) f; +} + +/** + * bmesh_SFME + * + * SPLIT FACE MAKE EDGE: + * + * Takes as input two vertices in a single face. An edge is created which divides the original face + * into two distinct regions. One of the regions is assigned to the original face and it is closed off. + * The second region has a new face assigned to it. + * + * Examples: + * + * Before: After: + * ---------- ---------- + * | | | | + * | | | f1 | + * v1 f1 v2 v1======v2 + * | | | f2 | + * | | | | + * ---------- ---------- + * + * Note that the input vertices can be part of the same edge. This will + * result in a two edged face. This is desirable for advanced construction + * tools and particularly essential for edge bevel. Because of this it is + * up to the caller to decide what to do with the extra edge. + * + * If holes is NULL, then both faces will lose + * all holes from the original face. Also, you cannot split between + * a hole vert and a boundary vert; that case is handled by higher- + * level wrapping functions (when holes are fully implemented, anyway). + * + * Note that holes represents which holes goes to the new face, and of + * course this requires removing them from the exitsing face first, since + * you cannot have linked list links inside multiple lists. + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, + BMLoop **rl +#ifdef USE_BMESH_HOLES + , ListBase *holes +#endif + ) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *lst, *lst2; +#endif + + BMFace *f2; + BMLoop *l_iter, *l_first; + BMLoop *v1loop = NULL, *v2loop = NULL, *f1loop = NULL, *f2loop = NULL; + BMEdge *e; + int i, len, f1len, f2len; + + /* verify that v1 and v2 are in face */ + len = f->len; + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < len; i++, l_iter = l_iter->next) { + if (l_iter->v == v1) v1loop = l_iter; + else if (l_iter->v == v2) v2loop = l_iter; + } + + if (!v1loop || !v2loop) { + return NULL; + } + + /* allocate new edge between v1 and v2 */ + e = BM_edge_create(bm, v1, v2, NULL, FALSE); + + f2 = bmesh_addpolylist(bm, f); + f1loop = bmesh_create_loop(bm, v2, e, f, v2loop); + f2loop = bmesh_create_loop(bm, v1, e, f2, v1loop); + + f1loop->prev = v2loop->prev; + f2loop->prev = v1loop->prev; + v2loop->prev->next = f1loop; + v1loop->prev->next = f2loop; + + f1loop->next = v1loop; + f2loop->next = v2loop; + v1loop->prev = f1loop; + v2loop->prev = f2loop; + +#ifdef USE_BMESH_HOLES + lst = f->loops.first; + lst2 = f2->loops.first; + + lst2->first = lst2->last = f2loop; + lst->first = lst->last = f1loop; +#else + f2->l_first = f2loop; + f->l_first = f1loop; +#endif + + /* validate both loop */ + /* I dont know how many loops are supposed to be in each face at this point! FIXME */ + + /* go through all of f2's loops and make sure they point to it properly */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f2); + f2len = 0; + do { + l_iter->f = f2; + f2len++; + } while ((l_iter = l_iter->next) != l_first); + + /* link up the new loops into the new edges radia */ + bmesh_radial_append(e, f1loop); + bmesh_radial_append(e, f2loop); + + f2->len = f2len; + + f1len = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + f1len++; + } while ((l_iter = l_iter->next) != l_first); + + f->len = f1len; + + if (rl) *rl = f2loop; + +#ifdef USE_BMESH_HOLES + if (holes) { + BLI_movelisttolist(&f2->loops, holes); + } + else { + /* this code is not significant until holes actually work */ + //printf("warning: call to split face euler without holes argument; holes will be tossed.\n"); + for (lst = f->loops.last; lst != f->loops.first; lst = lst2) { + lst2 = lst->prev; + BLI_mempool_free(bm->looplistpool, lst); + } + } +#endif + + BM_CHECK_ELEMENT(bm, e); + BM_CHECK_ELEMENT(bm, f); + BM_CHECK_ELEMENT(bm, f2); + + return f2; +} + +/** + * bmesh_SEMV + * + * SPLIT EDGE MAKE VERT: + * Takes a given edge and splits it into two, creating a new vert. + * + * + * Before: OV---------TV + * After: OV----NV---TV + * + * Returns - + * BMVert pointer. + * + */ + +BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re) +{ + BMLoop *nextl; + BMEdge *ne; + BMVert *nv, *ov; + int i, edok, valence1 = 0, valence2 = 0; + + if (bmesh_vert_in_edge(e, tv) == 0) { + return NULL; + } + ov = bmesh_edge_getothervert(e, tv); + + /* count valence of v1 */ + valence1 = bmesh_disk_count(ov); + + /* count valence of v2 */ + valence2 = bmesh_disk_count(tv); + + nv = BM_vert_create(bm, tv->co, tv); + ne = BM_edge_create(bm, nv, tv, e, FALSE); + + bmesh_disk_remove_edge(ne, tv); + bmesh_disk_remove_edge(ne, nv); + + /* remove e from v2's disk cycle */ + bmesh_disk_remove_edge(e, tv); + + /* swap out tv for nv in e */ + bmesh_edge_swapverts(e, tv, nv); + + /* add e to nv's disk cycl */ + bmesh_disk_append_edge(e, nv); + + /* add ne to nv's disk cycl */ + bmesh_disk_append_edge(ne, nv); + + /* add ne to tv's disk cycl */ + bmesh_disk_append_edge(ne, tv); + + /* verify disk cycle */ + edok = bmesh_disk_validate(valence1, ov->e, ov); + if (!edok) bmesh_error(); + edok = bmesh_disk_validate(valence2, tv->e, tv); + if (!edok) bmesh_error(); + edok = bmesh_disk_validate(2, nv->e, nv); + if (!edok) bmesh_error(); + + /* Split the radial cycle if presen */ + nextl = e->l; + e->l = NULL; + if (nextl) { + BMLoop *nl, *l; + int radlen = bmesh_radial_length(nextl); + int first1 = 0, first2 = 0; + + /* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */ + while (nextl) { + l = nextl; + l->f->len++; + nextl = nextl != nextl->radial_next ? nextl->radial_next : NULL; + bmesh_radial_remove_loop(l, NULL); + + nl = bmesh_create_loop(bm, NULL, NULL, l->f, l); + nl->prev = l; + nl->next = (l->next); + nl->prev->next = nl; + nl->next->prev = nl; + nl->v = nv; + + /* assign the correct edge to the correct loo */ + if (bmesh_verts_in_edge(nl->v, nl->next->v, e)) { + nl->e = e; + l->e = ne; + + /* append l into ne's rad cycl */ + if (!first1) { + first1 = 1; + l->radial_next = l->radial_prev = NULL; + } + + if (!first2) { + first2 = 1; + l->radial_next = l->radial_prev = NULL; + } + + bmesh_radial_append(nl->e, nl); + bmesh_radial_append(l->e, l); + } + else if (bmesh_verts_in_edge(nl->v, nl->next->v, ne)) { + nl->e = ne; + l->e = e; + + /* append l into ne's rad cycl */ + if (!first1) { + first1 = 1; + l->radial_next = l->radial_prev = NULL; + } + + if (!first2) { + first2 = 1; + l->radial_next = l->radial_prev = NULL; + } + + bmesh_radial_append(nl->e, nl); + bmesh_radial_append(l->e, l); + } + + } + + /* verify length of radial cycl */ + edok = bmesh_radial_validate(radlen, e->l); + if (!edok) bmesh_error(); + edok = bmesh_radial_validate(radlen, ne->l); + if (!edok) bmesh_error(); + + /* verify loop->v and loop->next->v pointers for */ + for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) { + if (!(l->e == e)) bmesh_error(); + //if (!(l->radial_next == l)) bmesh_error(); + if (l->prev->e != ne && l->next->e != ne) { + bmesh_error(); + } + edok = bmesh_verts_in_edge(l->v, l->next->v, e); + if (!edok) bmesh_error(); + if (l->v == l->next->v) bmesh_error(); + if (l->e == l->next->e) bmesh_error(); + + /* verify loop cycle for kloop-> */ + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->v); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->f); + } + /* verify loop->v and loop->next->v pointers for n */ + for (i = 0, l = ne->l; i < radlen; i++, l = l->radial_next) { + if (!(l->e == ne)) bmesh_error(); + //if (!(l->radial_next == l)) bmesh_error(); + if (l->prev->e != e && l->next->e != e) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, l->next->v, ne); + if (!edok) bmesh_error(); + if (l->v == l->next->v) bmesh_error(); + if (l->e == l->next->e) bmesh_error(); + + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->v); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->f); + } + } + + BM_CHECK_ELEMENT(bm, ne); + BM_CHECK_ELEMENT(bm, nv); + BM_CHECK_ELEMENT(bm, ov); + BM_CHECK_ELEMENT(bm, e); + BM_CHECK_ELEMENT(bm, tv); + + if (re) *re = ne; + return nv; +} + +/** + * bmesh_JEKV + * + * JOIN EDGE KILL VERT: + * Takes a an edge and pointer to one of its vertices and collapses + * the edge on that vertex. + * + * Before: OE KE + * ------- ------- + * | || | + * OV KV TV + * + * + * After: OE + * --------------- + * | | + * OV TV + * + * + * Restrictions: + * KV is a vertex that must have a valance of exactly two. Furthermore + * both edges in KV's disk cycle (OE and KE) must be unique (no double + * edges). + * + * It should also be noted that this euler has the possibility of creating + * faces with just 2 edges. It is up to the caller to decide what to do with + * these faces. + * + * Returns - + * 1 for success, 0 for failure. + */ +int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + BMEdge *oe; + BMVert *ov, *tv; + BMLoop *killoop, *l; + int len, radlen = 0, halt = 0, i, valence1, valence2, edok; + + if (bmesh_vert_in_edge(ke, kv) == 0) { + return FALSE; + } + + len = bmesh_disk_count(kv); + + if (len == 2) { + oe = bmesh_disk_nextedge(ke, kv); + tv = bmesh_edge_getothervert(ke, kv); + ov = bmesh_edge_getothervert(oe, kv); + halt = bmesh_verts_in_edge(kv, tv, oe); /* check for double edge */ + + if (halt) { + return FALSE; + } + else { + /* For verification later, count valence of ov and t */ + valence1 = bmesh_disk_count(ov); + valence2 = bmesh_disk_count(tv); + + /* remove oe from kv's disk cycl */ + bmesh_disk_remove_edge(oe, kv); + /* relink oe->kv to be oe->t */ + bmesh_edge_swapverts(oe, kv, tv); + /* append oe to tv's disk cycl */ + bmesh_disk_append_edge(oe, tv); + /* remove ke from tv's disk cycl */ + bmesh_disk_remove_edge(ke, tv); + + /* deal with radial cycle of k */ + radlen = bmesh_radial_length(ke->l); + if (ke->l) { + /* first step, fix the neighboring loops of all loops in ke's radial cycl */ + for (i = 0, killoop = ke->l; i < radlen; i++, killoop = bmesh_radial_nextloop(killoop)) { + /* relink loops and fix vertex pointer */ + if (killoop->next->v == kv) { + killoop->next->v = tv; + } + + killoop->next->prev = killoop->prev; + killoop->prev->next = killoop->next; + if (BM_FACE_FIRST_LOOP(killoop->f) == killoop) { + BM_FACE_FIRST_LOOP(killoop->f) = killoop->next; + } + killoop->next = NULL; + killoop->prev = NULL; + + /* fix len attribute of fac */ + killoop->f->len--; + } + /* second step, remove all the hanging loops attached to k */ + radlen = bmesh_radial_length(ke->l); + + if (LIKELY(radlen)) { + BMLoop **loops = NULL; + BLI_array_fixedstack_declare(loops, BM_NGON_STACK_SIZE, radlen, __func__); + + killoop = ke->l; + + /* this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well.. */ + for (i = 0; i < radlen; i++) { + loops[i] = killoop; + killoop = bmesh_radial_nextloop(killoop); + } + for (i = 0; i < radlen; i++) { + bm->totloop--; + BLI_mempool_free(bm->lpool, loops[i]); + } + BLI_array_fixedstack_free(loops); + } + + /* Validate radial cycle of o */ + edok = bmesh_radial_validate(radlen, oe->l); + if (!edok) { + bmesh_error(); + } + } + + /* deallocate edg */ + bmesh_kill_only_edge(bm, ke); + + /* deallocate verte */ + bmesh_kill_only_vert(bm, kv); + + /* Validate disk cycle lengths of ov, tv are unchange */ + edok = bmesh_disk_validate(valence1, ov->e, ov); + if (!edok) bmesh_error(); + edok = bmesh_disk_validate(valence2, tv->e, tv); + if (!edok) bmesh_error(); + + /* Validate loop cycle of all faces attached to o */ + for (i = 0, l = oe->l; i < radlen; i++, l = bmesh_radial_nextloop(l)) { + if (l->e != oe) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, l->next->v, oe); + if (!edok) bmesh_error(); + edok = bmesh_loop_validate(l->f); + if (!edok) bmesh_error(); + + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->v); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->f); + } + + BM_CHECK_ELEMENT(bm, ov); + BM_CHECK_ELEMENT(bm, tv); + BM_CHECK_ELEMENT(bm, oe); + + return TRUE; + } + } + return FALSE; +} + +/** + * bmesh_JFKE + * + * JOIN FACE KILL EDGE: + * + * Takes two faces joined by a single 2-manifold edge and fuses them togather. + * The edge shared by the faces must not be connected to any other edges which have + * Both faces in its radial cycle + * + * Examples: + * + * A B + * ---------- ---------- + * | | | | + * | f1 | | f1 | + * v1========v2 = Ok! v1==V2==v3 == Wrong! + * | f2 | | f2 | + * | | | | + * ---------- ---------- + * + * In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used. + * In example B however, f1 and f2 are joined by multiple edges and will produce an error. The caller + * in this case should call bmesh_JEKV on the extra edges before attempting to fuse f1 and f2. + * + * Also note that the order of arguments decides whether or not certain per-face attributes are present + * in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited + * from f1, not f2. + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + BMLoop *l_iter, *f1loop = NULL, *f2loop = NULL; + int newlen = 0, i, f1len = 0, f2len = 0, radlen = 0, edok, shared; + BMIter iter; + + /* can't join a face to itsel */ + if (f1 == f2) { + return NULL; + } + + /* verify that e is in both f1 and f2 */ + f1len = f1->len; + f2len = f2->len; + BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f1) { + if (l_iter->e == e) { + f1loop = l_iter; + break; + } + } + BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f2) { + if (l_iter->e == e) { + f2loop = l_iter; + break; + } + } + if (!(f1loop && f2loop)) { + return NULL; + } + + /* validate that edge is 2-manifold edg */ + radlen = bmesh_radial_length(f1loop); + if (radlen != 2) { + return NULL; + } + + /* validate direction of f2's loop cycle is compatible */ + if (f1loop->v == f2loop->v) { + return NULL; + } + + /* validate that for each face, each vertex has another edge in its disk cycle that is + * not e, and not shared. */ + if ( bmesh_radial_find_face(f1loop->next->e, f2) || + bmesh_radial_find_face(f1loop->prev->e, f2) || + bmesh_radial_find_face(f2loop->next->e, f1) || + bmesh_radial_find_face(f2loop->prev->e, f1) ) + { + return NULL; + } + + /* validate only one shared edg */ + shared = BM_face_share_edges(f1, f2); + if (shared > 1) { + return NULL; + } + + /* validate no internal join */ + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } + + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) { + if (l_iter != f1loop) { + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + } + } + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) { + if (l_iter != f2loop) { + /* as soon as a duplicate is found, bail out */ + if (BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) { + return NULL; + } + } + } + + /* join the two loop */ + f1loop->prev->next = f2loop->next; + f2loop->next->prev = f1loop->prev; + + f1loop->next->prev = f2loop->prev; + f2loop->prev->next = f1loop->next; + + /* if f1loop was baseloop, make f1loop->next the base. */ + if (BM_FACE_FIRST_LOOP(f1) == f1loop) + BM_FACE_FIRST_LOOP(f1) = f1loop->next; + + /* increase length of f1 */ + f1->len += (f2->len - 2); + + /* make sure each loop points to the proper fac */ + newlen = f1->len; + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next) + l_iter->f = f1; + + /* remove edge from the disk cycle of its two vertices */ + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1); + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /* deallocate edge and its two loops as well as f2 */ + BLI_mempool_free(bm->toolflagpool, f1loop->e->oflags); + BLI_mempool_free(bm->epool, f1loop->e); + bm->totedge--; + BLI_mempool_free(bm->lpool, f1loop); + bm->totloop--; + BLI_mempool_free(bm->lpool, f2loop); + bm->totloop--; + BLI_mempool_free(bm->toolflagpool, f2->oflags); + BLI_mempool_free(bm->fpool, f2); + bm->totface--; + /* account for both above */ + bm->elem_index_dirty |= BM_EDGE | BM_FACE; + + BM_CHECK_ELEMENT(bm, f1); + + /* validate the new loop cycle */ + edok = bmesh_loop_validate(f1); + if (!edok) bmesh_error(); + + return f1; +} + +/* + * BMESH SPLICE VERT + * + * merges two verts into one (v into vtarget). + */ +static int bmesh_splicevert(BMesh *bm, BMVert *v, BMVert *vtarget) +{ + BMEdge *e; + BMLoop *l; + BMIter liter; + + /* verts already spliced */ + if (v == vtarget) { + return FALSE; + } + + /* retarget all the loops of v to vtarget */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + l->v = vtarget; + } + + /* move all the edges from v's disk to vtarget's disk */ + e = v->e; + while (e != NULL) { + bmesh_disk_remove_edge(e, v); + bmesh_edge_swapverts(e, v, vtarget); + bmesh_disk_append_edge(e, vtarget); + e = v->e; + } + + BM_CHECK_ELEMENT(bm, v); + BM_CHECK_ELEMENT(bm, vtarget); + + /* v is unused now, and can be killed */ + BM_vert_kill(bm, v); + + return TRUE; +} + +/* BMESH CUT VERT + * + * cut all disjoint fans that meet at a vertex, making a unique + * vertex for each region. returns an array of all resulting + * vertices. + */ +static int bmesh_cutvert(BMesh *bm, BMVert *v, BMVert ***vout, int *len) +{ + BMEdge **stack = NULL; + BLI_array_declare(stack); + BMVert **verts = NULL; + GHash *visithash; + BMIter eiter, liter; + BMLoop *l; + BMEdge *e; + int i, maxindex; + BMLoop *nl; + + visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh_cutvert visithash"); + + maxindex = 0; + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (BLI_ghash_haskey(visithash, e)) { + continue; + } + + /* Prime the stack with this unvisited edge */ + BLI_array_append(stack, e); + + /* Considering only edges and faces incident on vertex v, walk + * the edges & faces and assign an index to each connected set */ + while ((e = BLI_array_pop(stack))) { + BLI_ghash_insert(visithash, e, SET_INT_IN_POINTER(maxindex)); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + nl = (l->v == v) ? l->prev : l->next; + if (!BLI_ghash_haskey(visithash, nl->e)) { + BLI_array_append(stack, nl->e); + } + } + } + + maxindex++; + } + + /* Make enough verts to split v for each group */ + verts = MEM_callocN(sizeof(BMVert *) * maxindex, "bmesh_cutvert"); + verts[0] = v; + for (i = 1; i < maxindex; i++) { + verts[i] = BM_vert_create(bm, v->co, v); + } + + /* Replace v with the new verts in each group */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e)); + if (i == 0) { + continue; + } + + /* Loops here should alway refer to an edge that has v as an + * endpoint. For each appearance of this vert in a face, there + * will actually be two iterations: one for the loop heading + * towards vertex v, and another for the loop heading out from + * vertex v. Only need to swap the vertex on one of those times, + * on the outgoing loop. */ + if (l->v == v) { + l->v = verts[i]; + } + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, e)); + if (i == 0) { + continue; + } + + BLI_assert(e->v1 == v || e->v2 == v); + bmesh_disk_remove_edge(e, v); + bmesh_edge_swapverts(e, v, verts[i]); + bmesh_disk_append_edge(e, verts[i]); + } + + BLI_ghash_free(visithash, NULL, NULL); + BLI_array_free(stack); + + for (i = 0; i < maxindex; i++) { + BM_CHECK_ELEMENT(bm, verts[i]); + } + + if (len != NULL) { + *len = maxindex; + } + + if (vout != NULL) { + *vout = verts; + } + else { + MEM_freeN(verts); + } + + return TRUE; +} + +/* BMESH SPLICE EDGE + * + * splice two unique edges which share the same two vertices into one edge. + * + * edges must already have the same vertices + */ +static int UNUSED_FUNCTION(bmesh_spliceedge)(BMesh *bm, BMEdge *e, BMEdge *etarget) +{ + BMLoop *l; + + if (!BM_vert_in_edge(e, etarget->v1) || !BM_vert_in_edge(e, etarget->v2)) { + /* not the same vertices can't splice */ + return FALSE; + } + + while (e->l) { + l = e->l; + BLI_assert(BM_vert_in_edge(etarget, l->v)); + BLI_assert(BM_vert_in_edge(etarget, l->next->v)); + bmesh_radial_remove_loop(l, e); + bmesh_radial_append(etarget, l); + } + + BLI_assert(bmesh_radial_length(e->l) == 0); + + BM_CHECK_ELEMENT(bm, e); + BM_CHECK_ELEMENT(bm, etarget); + + BM_edge_kill(bm, e); + + return TRUE; +} + +/* + * BMESH CUT EDGE + * + * Cuts a single edge into two edge: the original edge and + * a new edge that has only "cutl" in its radial. + * + * Does nothing if cutl is already the only loop in the + * edge radial. + */ +static int bmesh_cutedge(BMesh *bm, BMEdge *e, BMLoop *cutl) +{ + BMEdge *ne; + int radlen; + + BLI_assert(cutl->e == e); + BLI_assert(e->l); + + radlen = bmesh_radial_length(e->l); + if (radlen < 2) { + /* no cut required */ + return TRUE; + } + + if (cutl == e->l) { + e->l = cutl->radial_next; + } + + ne = BM_edge_create(bm, e->v1, e->v2, e, FALSE); + bmesh_radial_remove_loop(cutl, e); + bmesh_radial_append(ne, cutl); + cutl->e = ne; + + BLI_assert(bmesh_radial_length(e->l) == radlen - 1); + BLI_assert(bmesh_radial_length(ne->l) == 1); + + BM_CHECK_ELEMENT(bm, ne); + BM_CHECK_ELEMENT(bm, e); + + return TRUE; +} + +/* + * BMESH UNGLUE REGION MAKE VERT + * + * Disconnects a face from its vertex fan at loop sl. + */ +static BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *sl) +{ + BMVert **vtar; + int len, i; + BMVert *nv = NULL; + BMVert *sv = sl->v; + + /* peel the face from the edge radials on both sides of the + * loop vert, disconnecting the face from its fan */ + bmesh_cutedge(bm, sl->e, sl); + bmesh_cutedge(bm, sl->prev->e, sl->prev); + + if (bmesh_disk_count(sv) == 2) { + /* If there are still only two edges out of sv, then + * this whole URMV was just a no-op, so exit now. */ + return sv; + } + + /* Update the disk start, so that v->e points to an edge + * not touching the split loop. This is so that bmesh_cutvert + * will leave the original sv on some *other* fan (not the + * one-face fan that holds the unglue face). */ + while (sv->e == sl->e || sv->e == sl->prev->e) { + sv->e = bmesh_disk_nextedge(sv->e, sv); + } + + /* Split all fans connected to the vert, duplicating it for + * each fans. */ + bmesh_cutvert(bm, sv, &vtar, &len); + + /* There should have been at least two fans cut apart here, + * otherwise the early exit would have kicked in. */ + BLI_assert(len >= 2); + + nv = sl->v; + + /* Desired result here is that a new vert should always be + * created for the unglue face. This is so we can glue any + * extras back into the original vert. */ + BLI_assert(nv != sv); + BLI_assert(sv == vtar[0]); + + /* If there are more than two verts as a result, glue together + * all the verts except the one this URMV intended to create */ + if (len > 2) { + for (i = 0; i < len; i++) { + if (vtar[i] == nv) { + break; + } + } + + if (i != len) { + /* Swap the single vert that was needed for the + * unglue into the last array slot */ + SWAP(BMVert *, vtar[i], vtar[len - 1]); + + /* And then glue the rest back together */ + for (i = 1; i < len - 1; i++) { + bmesh_splicevert(bm, vtar[i], vtar[0]); + } + } + } + + MEM_freeN(vtar); + + return nv; +} + +/* + * BMESH UNGLUE REGION MAKE VERT + * + * Disconnects sf from the vertex fan at sv + */ +BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv) +{ + BMLoop *l_first; + BMLoop *l_iter; + + l_iter = l_first = BM_FACE_FIRST_LOOP(sf); + do { + if (l_iter->v == sv) { + break; + } + } while ((l_iter = l_iter->next) != l_first); + + if (l_iter->v != sv) { + /* sv is not part of sf */ + return NULL; + } + + return bmesh_urmv_loop(bm, l_iter); +} diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c new file mode 100644 index 00000000000..19e9971619a --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -0,0 +1,1145 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_opdefines.c + * \ingroup bmesh + * + * BMesh operator definitions. + * + * This file defines (and documents) all bmesh operators (bmops). + * + * Do not rename any operator or slot names! otherwise you must go + * through the code and find all references to them! + * + * A word on slot names: + * + * For geometry input slots, the following are valid names: + * - verts + * - edges + * - faces + * - edgefacein + * - vertfacein + * - vertedgein + * - vertfacein + * - geom + * + * The basic rules are, for single-type geometry slots, use the plural of the + * type name (e.g. edges). for double-type slots, use the two type names plus + * "in" (e.g. edgefacein). for three-type slots, use geom. + * + * for output slots, for single-type geometry slots, use the type name plus "out", + * (e.g. vertout), for double-type slots, use the two type names plus "out", + * (e.g. vertfaceout), for three-type slots, use geom. note that you can also + * use more esohteric names (e.g. skirtout) so long as the comment next to the + * slot definition tells you what types of elements are in it. + * + */ + +#include "bmesh.h" +#include "bmesh_private.h" + +/* ok, I'm going to write a little docgen script. so all + * bmop comments must conform to the following template/rules: + * + * template (py quotes used because nested comments don't work + * on all C compilers): + * + * """ + * Region Extend. + * + * paragraph1, Extends bleh bleh bleh. + * Bleh Bleh bleh. + * + * Another paragraph. + * + * Another paragraph. + * """ + * + * so the first line is the "title" of the bmop. + * subsequent line blocks seperated by blank lines + * are paragraphs. individual descriptions of slots + * would be extracted from comments + * next to them, e.g. + * + * {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, boundary region + * + * the doc generator would automatically detect the presence of "output slot" + * and flag the slot as an output. the same happens for "input slot". also + * note that "edges", "faces", "verts", "loops", and "geometry" are valid + * substitutions for "slot". + * + * note that slots default to being input slots. + */ + +/* + * Vertex Smooth + * + * Smoothes vertices by using a basic vertex averaging scheme. + */ +static BMOpDefine def_vertexsmooth = { + "vertexsmooth", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_INT, "mirror_clip_x"}, //set vertices close to the x axis before the operation to 0 + {BMO_OP_SLOT_INT, "mirror_clip_y"}, //set vertices close to the y axis before the operation to 0 + {BMO_OP_SLOT_INT, "mirror_clip_z"}, //set vertices close to the z axis before the operation to 0 + {BMO_OP_SLOT_FLT, "clipdist"}, //clipping threshod for the above three slots + {0} /* null-terminating sentine */, + }, + bmesh_vertexsmooth_exec, + 0 +}; + +/* + * Right-Hand Faces + * + * Computes an "outside" normal for the specified input faces. + */ + +static BMOpDefine def_righthandfaces = { + "righthandfaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, + {BMO_OP_SLOT_INT, "doflip"}, //internal flag, used by bmesh_rationalize_normals + {0} /* null-terminating sentine */, + }, + bmesh_righthandfaces_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Region Extend + * + * used to implement the select more/less tools. + * this puts some geometry surrounding regions of + * geometry in geom into geomout. + * + * if usefaces is 0 then geomout spits out verts and edges, + * otherwise it spits out faces. + */ +static BMOpDefine def_regionextend = { + "regionextend", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, computed boundary geometry. + {BMO_OP_SLOT_INT, "constrict"}, //find boundary inside the regions, not outside. + {BMO_OP_SLOT_INT, "usefaces"}, //extend from faces instead of edges + {0} /* null-terminating sentine */, + }, + bmesh_regionextend_exec, + 0 +}; + +/* + * Edge Rotate + * + * Rotates edges topologically. Also known as "spin edge" to some people. + * Simple example: [/] becomes [|] then [\]. + */ +static BMOpDefine def_edgerotate = { + "edgerotate", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //newly spun edges + {BMO_OP_SLOT_INT, "ccw"}, //rotate edge counter-clockwise if true, othewise clockwise + {0} /* null-terminating sentine */, + }, + bmesh_edgerotate_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Reverse Faces + * + * Reverses the winding (vertex order) of faces. This has the effect of + * flipping the normal. + */ +static BMOpDefine def_reversefaces = { + "reversefaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces + {0} /* null-terminating sentine */, + }, + bmesh_reversefaces_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Edge Bisect + * + * Splits input edges (but doesn't do anything else). + * This creates a 2-valence vert. + */ +static BMOpDefine def_edgebisect = { + "edgebisect", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges + {BMO_OP_SLOT_INT, "numcuts"}, //number of cuts + {BMO_OP_SLOT_ELEMENT_BUF, "outsplit"}, //newly created vertices and edges + {0} /* null-terminating sentine */, + }, + esplit_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Mirror + * + * Mirrors geometry along an axis. The resulting geometry is welded on using + * mergedist. Pairs of original/mirrored vertices are welded using the mergedist + * parameter (which defines the minimum distance for welding to happen). + */ + +static BMOpDefine def_mirror = { + "mirror", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry + {BMO_OP_SLOT_MAT, "mat"}, //matrix defining the mirror transformation + {BMO_OP_SLOT_FLT, "mergedist"}, //maximum distance for merging. does no merging if 0. + {BMO_OP_SLOT_ELEMENT_BUF, "newout"}, //output geometry, mirrored + {BMO_OP_SLOT_INT, "axis"}, //the axis to use, 0, 1, or 2 for x, y, z + {BMO_OP_SLOT_INT, "mirror_u"}, //mirror UVs across the u axis + {BMO_OP_SLOT_INT, "mirror_v"}, //mirror UVs across the v axis + {0, /* null-terminating sentine */}}, + bmesh_mirror_exec, + 0, +}; + +/* + * Find Doubles + * + * Takes input verts and find vertices they should weld to. Outputs a + * mapping slot suitable for use with the weld verts bmop. + */ +static BMOpDefine def_finddoubles = { + "finddoubles", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_ELEMENT_BUF, "keepverts"}, //list of verts to keep + {BMO_OP_SLOT_FLT, "dist"}, //minimum distance + {BMO_OP_SLOT_MAPPING, "targetmapout"}, + {0, /* null-terminating sentine */}}, + bmesh_finddoubles_exec, + 0, +}; + +/* + * Remove Doubles + * + * Finds groups of vertices closer then dist and merges them together, + * using the weld verts bmop. + */ +static BMOpDefine def_removedoubles = { + "removedoubles", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts + {BMO_OP_SLOT_FLT, "dist"}, //minimum distance + {0, /* null-terminating sentine */}}, + bmesh_removedoubles_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Auto Merge + * + * Finds groups of vertices closer then dist and merges them together, + * using the weld verts bmop. The merges must go from a vert not in + * verts to one in verts. + */ +static BMOpDefine def_automerge = { + "automerge", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts + {BMO_OP_SLOT_FLT, "dist"}, //minimum distance + {0, /* null-terminating sentine */}}, + bmesh_automerge_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Collapse Connected + * + * Collapses connected vertices + */ +static BMOpDefine def_collapse = { + "collapse", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {0, /* null-terminating sentine */}}, + bmesh_collapse_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + + +/* + * Facedata point Merge + * + * Merge uv/vcols at a specific vertex. + */ +static BMOpDefine def_pointmerge_facedata = { + "pointmerge_facedata", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */ + {BMO_OP_SLOT_ELEMENT_BUF, "snapv"}, /* snap verte */ + {0, /* null-terminating sentine */}}, + bmesh_pointmerge_facedata_exec, + 0, +}; + +/* + * Average Vertices Facevert Data + * + * Merge uv/vcols associated with the input vertices at + * the bounding box center. (I know, it's not averaging but + * the vert_snap_to_bb_center is just too long). + */ +static BMOpDefine def_vert_average_facedata = { + "vert_average_facedata", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */ + {0, /* null-terminating sentine */}}, + bmesh_vert_average_facedata_exec, + 0, +}; + +/* + * Point Merge + * + * Merge verts together at a point. + */ +static BMOpDefine def_pointmerge = { + "pointmerge", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */ + {BMO_OP_SLOT_VEC, "mergeco"}, + {0, /* null-terminating sentine */}}, + bmesh_pointmerge_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Collapse Connected UVs + * + * Collapses connected UV vertices. + */ +static BMOpDefine def_collapse_uvs = { + "collapse_uvs", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {0, /* null-terminating sentine */}}, + bmesh_collapsecon_exec, + 0, +}; + +/* + * Weld Verts + * + * Welds verts together (kindof like remove doubles, merge, etc, all of which + * use or will use this bmop). You pass in mappings from vertices to the vertices + * they weld with. + */ +static BMOpDefine def_weldverts = { + "weldverts", + {{BMO_OP_SLOT_MAPPING, "targetmap"}, /* maps welded vertices to verts they should weld to */ + {0, /* null-terminating sentine */}}, + bmesh_weldverts_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Make Vertex + * + * Creates a single vertex; this bmop was necassary + * for click-create-vertex. + */ +static BMOpDefine def_makevert = { + "makevert", + {{BMO_OP_SLOT_VEC, "co"}, //the coordinate of the new vert + {BMO_OP_SLOT_ELEMENT_BUF, "newvertout"}, //the new vert + {0, /* null-terminating sentine */}}, + bmesh_makevert_exec, + 0, +}; + +/* + * Join Triangles + * + * Tries to intelligently join triangles according + * to various settings and stuff. + */ +static BMOpDefine def_join_triangles = { + "join_triangles", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input geometry. + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //joined faces + {BMO_OP_SLOT_INT, "compare_sharp"}, + {BMO_OP_SLOT_INT, "compare_uvs"}, + {BMO_OP_SLOT_INT, "compare_vcols"}, + {BMO_OP_SLOT_INT, "compare_materials"}, + {BMO_OP_SLOT_FLT, "limit"}, + {0, /* null-terminating sentine */}}, + bmesh_jointriangles_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Contextual Create + * + * This is basically fkey, it creates + * new faces from vertices, makes stuff from edge nets, + * makes wire edges, etc. It also dissolves + * faces. + * + * Three verts become a triangle, four become a quad. Two + * become a wire edge. + */ +static BMOpDefine def_contextual_create = { + "contextual_create", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry. + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //newly-made face(s) + {0, /* null-terminating sentine */}}, + bmesh_contextual_create_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Bridge edge loops with faces + */ +static BMOpDefine def_bridge_loops = { + "bridge_loops", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */ + {0, /* null-terminating sentine */}}, + bmesh_bridge_loops_exec, + 0, +}; + +static BMOpDefine def_edgenet_fill = { + "edgenet_fill", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {BMO_OP_SLOT_MAPPING, "restrict"}, /* restricts edges to groups. maps edges to integer */ + {BMO_OP_SLOT_INT, "use_restrict"}, + {BMO_OP_SLOT_ELEMENT_BUF, "excludefaces"}, /* list of faces to ignore for manifold check */ + {BMO_OP_SLOT_MAPPING, "faceout_groupmap"}, /* maps new faces to the group numbers they came fro */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */ + {0, /* null-terminating sentine */}}, + bmesh_edgenet_fill_exec, + 0, +}; + +/* + * Edgenet Prepare + * + * Identifies several useful edge loop cases and modifies them so + * they'll become a face when edgenet_fill is called. The cases covered are: + * + * - One single loop; an edge is added to connect the ends + * - Two loops; two edges are added to connect the endpoints (based on the + * shortest distance between each endpont). + */ +static BMOpDefine def_edgenet_prepare = { + "edgenet_prepare", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //new edges + {0, /* null-terminating sentine */}}, + bmesh_edgenet_prepare, + 0, +}; + +/* + * Rotate + * + * Rotate vertices around a center, using a 3x3 rotation + * matrix. Equivilent of the old rotateflag function. + */ +static BMOpDefine def_rotate = { + "rotate", + {{BMO_OP_SLOT_VEC, "cent"}, //center of rotation + {BMO_OP_SLOT_MAT, "mat"}, //matrix defining rotation + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_rotate_exec, + 0, +}; + +/* + * Translate + * + * Translate vertices by an offset. Equivelent of the + * old translateflag function. + */ +static BMOpDefine def_translate = { + "translate", + {{BMO_OP_SLOT_VEC, "vec"}, //translation offset + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_translate_exec, + 0, +}; + +/* + * Scale + * + * Scales vertices by an offset. + */ +static BMOpDefine def_scale = { + "scale", + {{BMO_OP_SLOT_VEC, "vec"}, //scale factor + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_scale_exec, + 0, +}; + + +/* + * Transform + * + * Transforms a set of vertices by a matrix. Multiplies + * the vertex coordinates with the matrix. + */ +static BMOpDefine def_transform = { + "transform", + {{BMO_OP_SLOT_MAT, "mat"}, //transform matrix + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_transform_exec, + 0, +}; + +/* + * Object Load BMesh + * + * Loads a bmesh into an object/mesh. This is a "private" + * bmop. + */ +static BMOpDefine def_object_load_bmesh = { + "object_load_bmesh", + {{BMO_OP_SLOT_PNT, "scene"}, + {BMO_OP_SLOT_PNT, "object"}, + {0, /* null-terminating sentine */}}, + object_load_bmesh_exec, + 0, +}; + + +/* + * BMesh to Mesh + * + * Converts a bmesh to a Mesh. This is reserved for exiting editmode. + */ +static BMOpDefine def_bmesh_to_mesh = { + "bmesh_to_mesh", + {{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a mesh structure to fill in + {BMO_OP_SLOT_PNT, "object"}, //pointer to an object structure + {BMO_OP_SLOT_INT, "notesselation"}, //don't calculate mfaces + {0, /* null-terminating sentine */}}, + bmesh_to_mesh_exec, + 0, +}; + +/* + * Mesh to BMesh + * + * Load the contents of a mesh into the bmesh. this bmop is private, it's + * reserved exclusively for entering editmode. + */ +static BMOpDefine def_mesh_to_bmesh = { + "mesh_to_bmesh", + {{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a Mesh structure + {BMO_OP_SLOT_PNT, "object"}, //pointer to an Object structure + {BMO_OP_SLOT_INT, "set_shapekey"}, //load active shapekey coordinates into verts + {0, /* null-terminating sentine */}}, + mesh_to_bmesh_exec, + 0 +}; + +/* + * Individual Face Extrude + * + * Extrudes faces individually. + */ +static BMOpDefine def_extrude_indivface = { + "extrude_face_indiv", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //output faces + {BMO_OP_SLOT_ELEMENT_BUF, "skirtout"}, //output skirt geometry, faces and edges + {0} /* null-terminating sentine */}, + bmesh_extrude_face_indiv_exec, + 0 +}; + +/* + * Extrude Only Edges + * + * Extrudes Edges into faces, note that this is very simple, there's no fancy + * winged extrusion. + */ +static BMOpDefine def_extrude_onlyedge = { + "extrude_edge_only", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input vertices + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output geometry + {0} /* null-terminating sentine */}, + bmesh_extrude_onlyedge_exec, + 0 +}; + +/* + * Individual Vertex Extrude + * + * Extrudes wire edges from vertices. + */ +static BMOpDefine def_extrudeverts_indiv = { + "extrude_vert_indiv", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //output wire edges + {BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output vertices + {0} /* null-terminating sentine */}, + extrude_vert_indiv_exec, + 0 +}; + +static BMOpDefine def_connectverts = { + "connectverts", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, + {0} /* null-terminating sentine */}, + connectverts_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_extrudefaceregion = { + "extrudefaceregion", + {{BMO_OP_SLOT_ELEMENT_BUF, "edgefacein"}, + {BMO_OP_SLOT_MAPPING, "exclude"}, + {BMO_OP_SLOT_INT, "alwayskeeporig"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, + {0} /* null-terminating sentine */}, + extrude_edge_context_exec, + 0 +}; + +static BMOpDefine def_dissolvevertsop = { + "dissolveverts", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {0} /* null-terminating sentine */}, + dissolveverts_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolveedgessop = { + "dissolveedges", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {BMO_OP_SLOT_ELEMENT_BUF, "regionout"}, + {BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges. + {0} /* null-terminating sentine */}, + dissolveedges_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolveedgeloopsop = { + "dissolveedgeloop", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {BMO_OP_SLOT_ELEMENT_BUF, "regionout"}, + {0} /* null-terminating sentine */}, + dissolve_edgeloop_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolvefacesop = { + "dissolvefaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, + {BMO_OP_SLOT_ELEMENT_BUF, "regionout"}, + {BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges. + {0} /* null-terminating sentine */}, + dissolvefaces_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolvelimitop = { + "dissolvelimit", + {{BMO_OP_SLOT_FLT, "angle_limit"}, /* total rotation angle (degrees) */ + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {0} /* null-terminating sentine */}, + dissolvelimit_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_triangop = { + "triangulate", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, + {BMO_OP_SLOT_MAPPING, "facemap"}, + {0} /* null-terminating sentine */}, + triangulate_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_subdop = { + "esubd", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {BMO_OP_SLOT_INT, "numcuts"}, + {BMO_OP_SLOT_FLT, "smooth"}, + {BMO_OP_SLOT_FLT, "fractal"}, + {BMO_OP_SLOT_INT, "beauty"}, + {BMO_OP_SLOT_INT, "seed"}, + {BMO_OP_SLOT_MAPPING, "custompatterns"}, + {BMO_OP_SLOT_MAPPING, "edgepercents"}, + + /* these next three can have multiple types of elements in them */ + {BMO_OP_SLOT_ELEMENT_BUF, "outinner"}, + {BMO_OP_SLOT_ELEMENT_BUF, "outsplit"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* contains all output geometr */ + + {BMO_OP_SLOT_INT, "quadcornertype"}, //quad corner type, see bmesh_operators.h + {BMO_OP_SLOT_INT, "gridfill"}, //fill in fully-selected faces with a grid + {BMO_OP_SLOT_INT, "singleedge"}, //tesselate the case of one edge selected in a quad or triangle + + {0} /* null-terminating sentine */, + }, + esubdivide_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_delop = { + "del", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, {BMO_OP_SLOT_INT, "context"}, + {0} /* null-terminating sentine */}, + delop_exec, + 0 +}; + +static BMOpDefine def_dupeop = { + "dupe", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_ELEMENT_BUF, "origout"}, + {BMO_OP_SLOT_ELEMENT_BUF, "newout"}, + /* facemap maps from source faces to dupe + * faces, and from dupe faces to source faces */ + {BMO_OP_SLOT_MAPPING, "facemap"}, + {BMO_OP_SLOT_MAPPING, "boundarymap"}, + {BMO_OP_SLOT_MAPPING, "isovertmap"}, + {BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */ + {0} /* null-terminating sentine */}, + dupeop_exec, + 0 +}; + +static BMOpDefine def_splitop = { + "split", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, + {BMO_OP_SLOT_MAPPING, "boundarymap"}, + {BMO_OP_SLOT_MAPPING, "isovertmap"}, + {BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */ + {0} /* null-terminating sentine */}, + splitop_exec, + 0 +}; + +/* + * Spin + * + * Extrude or duplicate geometry a number of times, + * rotating and possibly translating after each step + */ +static BMOpDefine def_spinop = { + "spin", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_ELEMENT_BUF, "lastout"}, /* result of last step */ + {BMO_OP_SLOT_VEC, "cent"}, /* rotation center */ + {BMO_OP_SLOT_VEC, "axis"}, /* rotation axis */ + {BMO_OP_SLOT_VEC, "dvec"}, /* translation delta per step */ + {BMO_OP_SLOT_FLT, "ang"}, /* total rotation angle (degrees) */ + {BMO_OP_SLOT_INT, "steps"}, /* number of steps */ + {BMO_OP_SLOT_INT, "dupli"}, /* duplicate or extrude? */ + {0} /* null-terminating sentine */}, + spinop_exec, + 0 +}; + + +/* + * Similar faces search + * + * Find similar faces (area/material/perimeter, ...). + */ +static BMOpDefine def_similarfaces = { + "similarfaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* output faces */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /* null-terminating sentine */}, + bmesh_similarfaces_exec, + 0 +}; + +/* + * Similar edges search + * + * Find similar edges (length, direction, edge, seam, ...). + */ +static BMOpDefine def_similaredges = { + "similaredges", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, /* output edges */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /* null-terminating sentine */}, + bmesh_similaredges_exec, + 0 +}; + +/* + * Similar vertices search + * + * Find similar vertices (normal, face, vertex group, ...). + */ +static BMOpDefine def_similarverts = { + "similarverts", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertices */ + {BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /* null-terminating sentine */}, + bmesh_similarverts_exec, + 0 +}; + +/* + * uv rotation + * cycle the uvs + */ +static BMOpDefine def_meshrotateuvs = { + "meshrotateuvs", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_INT, "dir"}, /* direction */ + {0} /* null-terminating sentine */}, + bmesh_rotateuvs_exec, + 0 +}; + +/* + * uv reverse + * reverse the uvs + */ +static BMOpDefine def_meshreverseuvs = { + "meshreverseuvs", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {0} /* null-terminating sentine */}, + bmesh_reverseuvs_exec, + 0 +}; + +/* + * color rotation + * cycle the colors + */ +static BMOpDefine def_meshrotatecolors = { + "meshrotatecolors", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_INT, "dir"}, /* direction */ + {0} /* null-terminating sentine */}, + bmesh_rotatecolors_exec, + 0 +}; + +/* + * color reverse + * reverse the colors + */ +static BMOpDefine def_meshreversecolors = { + "meshreversecolors", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {0} /* null-terminating sentine */}, + bmesh_reversecolors_exec, + 0 +}; + +/* + * Similar vertices search + * + * Find similar vertices (normal, face, vertex group, ...). + */ +static BMOpDefine def_vertexshortestpath = { + "vertexshortestpath", + {{BMO_OP_SLOT_ELEMENT_BUF, "startv"}, /* start vertex */ + {BMO_OP_SLOT_ELEMENT_BUF, "endv"}, /* end vertex */ + {BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {0} /* null-terminating sentine */}, + bmesh_vertexshortestpath_exec, + 0 +}; + +/* + * Edge Split + * + * Disconnects faces along input edges. + */ +static BMOpDefine def_edgesplit = { + "edgesplit", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout1"}, /* old output disconnected edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout2"}, /* new output disconnected edges */ + {0} /* null-terminating sentine */}, + bmesh_edgesplitop_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Create Grid + * + * Creates a grid with a variable number of subdivisions + */ +static BMOpDefine def_create_grid = { + "create_grid", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "xsegments"}, //number of x segments + {BMO_OP_SLOT_INT, "ysegments"}, //number of y segments + {BMO_OP_SLOT_FLT, "size"}, //size of the grid + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with + {0, /* null-terminating sentine */}}, + bmesh_create_grid_exec, + 0, +}; + +/* + * Create UV Sphere + * + * Creates a grid with a variable number of subdivisions + */ +static BMOpDefine def_create_uvsphere = { + "create_uvsphere", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "segments"}, //number of u segments + {BMO_OP_SLOT_INT, "revolutions"}, //number of v segment + {BMO_OP_SLOT_FLT, "diameter"}, //diameter + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_uvsphere_exec, + 0, +}; + +/* + * Create Ico Sphere + * + * Creates a grid with a variable number of subdivisions + */ +static BMOpDefine def_create_icosphere = { + "create_icosphere", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "subdivisions"}, //how many times to recursively subdivide the sphere + {BMO_OP_SLOT_FLT, "diameter"}, //diameter + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with + {0, /* null-terminating sentine */}}, + bmesh_create_icosphere_exec, + 0, +}; + +/* + * Create Suzanne + * + * Creates a monkey. Be wary. + */ +static BMOpDefine def_create_monkey = { + "create_monkey", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_monkey_exec, + 0, +}; + +/* + * Create Cone + * + * Creates a cone with variable depth at both ends + */ +static BMOpDefine def_create_cone = { + "create_cone", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces + {BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons + {BMO_OP_SLOT_INT, "segments"}, + {BMO_OP_SLOT_FLT, "diameter1"}, //diameter of one end + {BMO_OP_SLOT_FLT, "diameter2"}, //diameter of the opposite + {BMO_OP_SLOT_FLT, "depth"}, //distance between ends + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_cone_exec, + 0, +}; + +/* + * Creates a circle + */ +static BMOpDefine def_create_circle = { + "create_circle", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces + {BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons + {BMO_OP_SLOT_INT, "segments"}, + {BMO_OP_SLOT_FLT, "diameter"}, //diameter of one end + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_circle_exec, + 0, +}; + +/* + * Create Cone + * + * Creates a cone with variable depth at both ends + */ +static BMOpDefine def_create_cube = { + "create_cube", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_FLT, "size"}, //size of the cube + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_cube_exec, + 0, +}; + +/* + * Bevel + * + * Bevels edges and vertices + */ +static BMOpDefine def_bevel = { + "bevel", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, /* input edges and vertices */ + {BMO_OP_SLOT_ELEMENT_BUF, "face_spans"}, /* new geometry */ + {BMO_OP_SLOT_ELEMENT_BUF, "face_holes"}, /* new geometry */ + {BMO_OP_SLOT_INT, "use_lengths"}, /* grab edge lengths from a PROP_FLT customdata laye */ + {BMO_OP_SLOT_INT, "use_even"}, /* corner vert placement: use shell/angle calculations */ + {BMO_OP_SLOT_INT, "use_dist"}, /* corner vert placement: evaluate percent as a distance, + * modifier uses this. We could do this as another float setting */ + {BMO_OP_SLOT_INT, "lengthlayer"}, /* which PROP_FLT layer to us */ + {BMO_OP_SLOT_FLT, "percent"}, /* percentage to expand bevelled edge */ + {0} /* null-terminating sentine */}, + bmesh_bevel_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Beautify Fill + * + * Makes triangle a bit nicer + */ +static BMOpDefine def_beautify_fill = { +"beautify_fill", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_ELEMENT_BUF, "constrain_edges"}, /* edges that can't be flipped */ + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new flipped faces and edges */ + {0} /* null-terminating sentine */}, + bmesh_beautify_fill_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Triangle Fill + * + * Fill edges with triangles + */ +static BMOpDefine def_triangle_fill = { + "triangle_fill", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new faces and edges */ + {0} /* null-terminating sentine */}, + bmesh_triangle_fill_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Solidify + * + * Turns a mesh into a shell with thickness + */ +static BMOpDefine def_solidify = { + "solidify", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_FLT, "thickness"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, + {0}}, + bmesh_solidify_face_region_exec, + 0 +}; + +BMOpDefine *opdefines[] = { + &def_splitop, + &def_spinop, + &def_dupeop, + &def_delop, + &def_subdop, + &def_triangop, + &def_dissolvefacesop, + &def_dissolveedgessop, + &def_dissolveedgeloopsop, + &def_dissolvevertsop, + &def_dissolvelimitop, + &def_extrudefaceregion, + &def_connectverts, + //&def_makeprim, + &def_extrudeverts_indiv, + &def_mesh_to_bmesh, + &def_object_load_bmesh, + &def_transform, + &def_translate, + &def_rotate, + &def_edgenet_fill, + &def_contextual_create, + &def_makevert, + &def_weldverts, + &def_removedoubles, + &def_finddoubles, + &def_mirror, + &def_edgebisect, + &def_reversefaces, + &def_edgerotate, + &def_regionextend, + &def_righthandfaces, + &def_vertexsmooth, + &def_extrude_onlyedge, + &def_extrude_indivface, + &def_collapse_uvs, + &def_pointmerge, + &def_collapse, + &def_similarfaces, + &def_similaredges, + &def_similarverts, + &def_pointmerge_facedata, + &def_vert_average_facedata, + &def_meshrotateuvs, + &def_bmesh_to_mesh, + &def_meshreverseuvs, + &def_edgenet_prepare, + &def_meshrotatecolors, + &def_meshreversecolors, + &def_vertexshortestpath, + &def_scale, + &def_edgesplit, + &def_automerge, + &def_create_uvsphere, + &def_create_grid, + &def_create_icosphere, + &def_create_monkey, + &def_create_cube, + &def_create_circle, + &def_create_cone, + &def_join_triangles, + &def_bevel, + &def_beautify_fill, + &def_triangle_fill, + &def_bridge_loops, + &def_solidify, +}; + +int bmesh_total_ops = (sizeof(opdefines) / sizeof(void *)); diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c new file mode 100644 index 00000000000..66222c39c68 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -0,0 +1,1376 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_operators.c + * \ingroup bmesh + * + * BMesh operator access. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_listbase.h" +#include "BLI_array.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* forward declarations */ +static void bmo_flag_layer_alloc(BMesh *bm); +static void bmo_flag_layer_free(BMesh *bm); +static void bmo_flag_layer_clear(BMesh *bm); +static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name); +static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name); +static int bmesh_opname_to_opcode(const char *opname); + +static const char *bmo_error_messages[] = { + NULL, + "Self intersection error", + "Could not dissolve vert", + "Could not connect vertices", + "Could not traverse mesh", + "Could not dissolve faces", + "Could not dissolve vertices", + "Tesselation error", + "Can not deal with non-manifold geometry", + "Invalid selection", + "Internal mesh error", +}; + + +/* operator slot type information - size of one element of the type given. */ +const int BMO_OPSLOT_TYPEINFO[] = { + 0, + sizeof(int), + sizeof(float), + sizeof(void *), + 0, /* unused */ + 0, /* unused */ + 0, /* unused */ + sizeof(void *), /* pointer buffer */ + sizeof(BMOElemMapping) +}; + +/* Dummy slot so there is something to return when slot name lookup fails */ +static BMOpSlot BMOpEmptySlot = {0}; + +void BMO_op_flag_enable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag) +{ + op->flag |= op_flag; +} + +void BMO_op_flag_disable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag) +{ + op->flag &= ~op_flag; +} + +/* + * BMESH OPSTACK PUSH + * + * Pushes the opstack down one level + * and allocates a new flag layer if + * appropriate. + */ +void BMO_push(BMesh *bm, BMOperator *UNUSED(op)) +{ + bm->stackdepth++; + + /* add flag layer, if appropriate */ + if (bm->stackdepth > 1) + bmo_flag_layer_alloc(bm); + else + bmo_flag_layer_clear(bm); +} + +/* + * BMESH OPSTACK POP + * + * Pops the opstack one level + * and frees a flag layer if appropriate + * BMESH_TODO: investigate NOT freeing flag + * layers. + */ +void BMO_pop(BMesh *bm) +{ + if (bm->stackdepth > 1) + bmo_flag_layer_free(bm); + + bm->stackdepth--; +} + +/* + * BMESH OPSTACK INIT OP + * + * Initializes an operator structure + * to a certain type + */ +void BMO_op_init(BMesh *bm, BMOperator *op, const char *opname) +{ + int i, opcode = bmesh_opname_to_opcode(opname); + +#ifdef DEBUG + BM_ELEM_INDEX_VALIDATE(bm, "pre bmo", opname); +#else + (void)bm; +#endif + + if (opcode == -1) { + opcode = 0; /* error!, already printed, have a better way to handle this? */ + } + + memset(op, 0, sizeof(BMOperator)); + op->type = opcode; + op->flag = opdefines[opcode]->flag; + + /* initialize the operator slot types */ + for (i = 0; opdefines[opcode]->slottypes[i].type; i++) { + op->slots[i].slottype = opdefines[opcode]->slottypes[i].type; + op->slots[i].index = i; + } + + /* callback */ + op->exec = opdefines[opcode]->exec; + + /* memarena, used for operator's slot buffers */ + op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmesh operator"); + BLI_memarena_use_calloc (op->arena); +} + +/* + * BMESH OPSTACK EXEC OP + * + * Executes a passed in operator. This handles + * the allocation and freeing of temporary flag + * layers and starting/stopping the modelling + * loop. Can be called from other operators + * exec callbacks as well. + */ +void BMO_op_exec(BMesh *bm, BMOperator *op) +{ + + BMO_push(bm, op); + + if (bm->stackdepth == 2) + bmesh_begin_edit(bm, op->flag); + op->exec(bm, op); + + if (bm->stackdepth == 2) + bmesh_end_edit(bm, op->flag); + + BMO_pop(bm); +} + +/* + * BMESH OPSTACK FINISH OP + * + * Does housekeeping chores related to finishing + * up an operator. + */ +void BMO_op_finish(BMesh *bm, BMOperator *op) +{ + BMOpSlot *slot; + int i; + + for (i = 0; opdefines[op->type]->slottypes[i].type; i++) { + slot = &op->slots[i]; + if (slot->slottype == BMO_OP_SLOT_MAPPING) { + if (slot->data.ghash) + BLI_ghash_free(slot->data.ghash, NULL, NULL); + } + } + + BLI_memarena_free(op->arena); + +#ifdef DEBUG + BM_ELEM_INDEX_VALIDATE(bm, "post bmo", opdefines[op->type]->name); +#else + (void)bm; +#endif +} + +/* + * BMESH OPSTACK HAS SLOT + * + * Returns 1 if the named slot exists on the given operator, + * otherwise returns 0. + */ +int BMO_slot_exists(BMOperator *op, const char *slotname) +{ + int slotcode = bmesh_name_to_slotcode(opdefines[op->type], slotname); + return (slotcode >= 0); +} + +/* + * BMESH OPSTACK GET SLOT + * + * Returns a pointer to the slot of + * type 'slotcode' + */ +BMOpSlot *BMO_slot_get(BMOperator *op, const char *slotname) +{ + int slotcode = bmesh_name_to_slotcode_check(opdefines[op->type], slotname); + + if (slotcode < 0) { + return &BMOpEmptySlot; + } + + return &(op->slots[slotcode]); +} + +/* + * BMESH OPSTACK COPY SLOT + * + * Copies data from one slot to another + */ +void BMO_slot_copy(BMOperator *source_op, BMOperator *dest_op, const char *src, const char *dst) +{ + BMOpSlot *source_slot = BMO_slot_get(source_op, src); + BMOpSlot *dest_slot = BMO_slot_get(dest_op, dst); + + if (source_slot == dest_slot) + return; + + if (source_slot->slottype != dest_slot->slottype) + return; + + if (dest_slot->slottype > BMO_OP_SLOT_VEC) { + if (dest_slot->slottype != BMO_OP_SLOT_MAPPING) { + /* do buffer copy */ + dest_slot->data.buf = NULL; + dest_slot->len = source_slot->len; + if (dest_slot->len) { + const int slot_alloc_size = BMO_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len; + dest_slot->data.buf = BLI_memarena_alloc(dest_op->arena, slot_alloc_size); + memcpy(dest_slot->data.buf, source_slot->data.buf, slot_alloc_size); + } + } + else { + GHashIterator it; + BMOElemMapping *srcmap, *dstmap; + + /* sanity check */ + if (!source_slot->data.ghash) return; + + if (!dest_slot->data.ghash) { + dest_slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash, + BLI_ghashutil_ptrcmp, "bmesh operator 2"); + } + + BLI_ghashIterator_init(&it, source_slot->data.ghash); + for ( ; (srcmap = BLI_ghashIterator_getValue(&it)); + BLI_ghashIterator_step(&it)) + { + dstmap = BLI_memarena_alloc(dest_op->arena, sizeof(*dstmap) + srcmap->len); + + dstmap->element = srcmap->element; + dstmap->len = srcmap->len; + memcpy(dstmap + 1, srcmap + 1, srcmap->len); + + BLI_ghash_insert(dest_slot->data.ghash, dstmap->element, dstmap); + } + } + } + else { + dest_slot->data = source_slot->data; + } +} + +/* + * BMESH OPSTACK SET XXX + * + * Sets the value of a slot depending on it's type + * + */ + +void BMO_slot_float_set(BMOperator *op, const char *slotname, const float f) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_FLT)) + return; + + slot->data.f = f; +} + +void BMO_slot_int_set(BMOperator *op, const char *slotname, const int i) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_INT)) + return; + + slot->data.i = i; +} + +/* only supports square mats */ +void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_MAT)) + return; + + slot->len = 4; + slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float) * 4 * 4); + + if (size == 4) { + memcpy(slot->data.p, mat, sizeof(float) * 4 * 4); + } + else if (size == 3) { + copy_m4_m3(slot->data.p, (float (*)[3])mat); + } + else { + fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size); + + memset(slot->data.p, 0, sizeof(float) * 4 * 4); + } +} + +void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_MAT)) + return; + + copy_m4_m4(r_mat, (float (*)[4])slot->data.p); +} + +void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_MAT)) + return; + + copy_m3_m4(r_mat, slot->data.p); +} + +void BMO_slot_ptr_set(BMOperator *op, const char *slotname, void *p) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_PNT)) + return; + + slot->data.p = p; +} + +void BMO_slot_vec_set(BMOperator *op, const char *slotname, const float vec[3]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_VEC)) + return; + + copy_v3_v3(slot->data.vec, vec); +} + + +float BMO_slot_float_get(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_FLT)) + return 0.0f; + + return slot->data.f; +} + +int BMO_slot_int_get(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_INT)) + return 0; + + return slot->data.i; +} + + +void *BMO_slot_ptr_get(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_PNT)) + return NULL; + + return slot->data.p; +} + +void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_VEC)) + return; + + copy_v3_v3(r_vec, slot->data.vec); +} + +/* + * BMO_COUNTFLAG + * + * Counts the number of elements of a certain type that + * have a specific flag set. + * + */ + +int BMO_mesh_flag_count(BMesh *bm, const short oflag, const char htype) +{ + BMIter elements; + int count = 0; + BMElemF *ele_f; + + if (htype & BM_VERT) { + for (ele_f = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, ele_f, oflag)) + count++; + } + } + if (htype & BM_EDGE) { + for (ele_f = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, ele_f, oflag)) + count++; + } + } + if (htype & BM_FACE) { + for (ele_f = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, ele_f, oflag)) + count++; + } + } + + return count; +} + +void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *UNUSED(op), const char htype, const short oflag) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + + const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE}; + + BMIter iter; + BMElemF *ele; + int i; + + for (i = 0; i < 3; i++) { + if (htype & flag_types[i]) { + BM_ITER(ele, &iter, bm, iter_types[i], NULL) { + BMO_elem_flag_disable(bm, ele, oflag); + } + } + } +} + +int BMO_slot_buf_count(struct BMesh *UNUSED(bm), struct BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /* check if its actually a buffer */ + if (!(slot->slottype > BMO_OP_SLOT_VEC)) + return 0; + + return slot->len; +} + +int BMO_slot_map_count(BMesh *UNUSED(bm), BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /* check if its actually a buffer */ + if (!(slot->slottype == BMO_OP_SLOT_MAPPING)) + return 0; + + return slot->data.ghash ? BLI_ghash_size(slot->data.ghash) : 0; +} + +#if 0 +void *BMO_Grow_Array(BMesh *bm, BMOperator *op, int slotcode, int totadd) +{ + BMOpSlot *slot = &op->slots[slotcode]; + void *tmp; + ssize_t allocsize; + + /* check if its actually a buffer */ + if (!(slot->slottype > BMO_OP_SLOT_VEC)) + return NULL; + + if (slot->flag & BMOS_DYNAMIC_ARRAY) { + if (slot->len >= slot->size) { + slot->size = (slot->size + 1 + totadd) * 2; + + allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->size; + + tmp = slot->data.buf; + slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array"); + memcpy(slot->data.buf, tmp, allocsize); + MEM_freeN(tmp); + } + + slot->len += totadd; + } + else { + slot->flag |= BMOS_DYNAMIC_ARRAY; + slot->len += totadd; + slot->size = slot->len + 2; + + allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->len; + + tmp = slot->data.buf; + slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array"); + memcpy(slot->data.buf, tmp, allocsize); + } + + return slot->data.buf; +} +#endif + +void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op, + const char *slotname, const short oflag) +{ + GHashIterator it; + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMElemF *ele_f; + + /* sanity check */ + if (slot->slottype != BMO_OP_SLOT_MAPPING) return; + if (!slot->data.ghash) return; + + BLI_ghashIterator_init(&it, slot->data.ghash); + for ( ; (ele_f = BLI_ghashIterator_getKey(&it)); BLI_ghashIterator_step(&it)) { + BMO_elem_flag_enable(bm, ele_f, oflag); + } +} + +static void *bmo_slot_buffer_alloc(BMOperator *op, const char *slotname, int len) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /* check if its actually a buffer */ + if (!(slot->slottype > BMO_OP_SLOT_VEC)) + return NULL; + + slot->len = len; + if (len) + slot->data.buf = BLI_memarena_alloc(op->arena, BMO_OPSLOT_TYPEINFO[slot->slottype] * len); + return slot->data.buf; +} + +/* + * BMO_ALL_TO_SLOT + * + * Copies all elements of a certain type into an operator slot. + * + */ + +static void BMO_slot_from_all(BMesh *bm, BMOperator *op, const char *slotname, const char htype) +{ + BMIter elements; + BMHeader *e; + BMOpSlot *output = BMO_slot_get(op, slotname); + int totelement = 0, i = 0; + + if (htype & BM_VERT) totelement += bm->totvert; + if (htype & BM_EDGE) totelement += bm->totedge; + if (htype & BM_FACE) totelement += bm->totface; + + if (totelement) { + bmo_slot_buffer_alloc(op, slotname, totelement); + + if (htype & BM_VERT) { + for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + + if (htype & BM_EDGE) { + for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + + if (htype & BM_FACE) { + for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } +} + +/* + * BMO_HEADERFLAG_TO_SLOT + * + * Copies elements of a certain type, which have a certain header flag set + * into a slot for an operator. + */ + +void BMO_slot_from_hflag(BMesh *bm, BMOperator *op, const char *slotname, + const char hflag, const char htype) +{ + BMIter elements; + BMHeader *e; + BMOpSlot *output = BMO_slot_get(op, slotname); + int totelement = 0, i = 0; + + totelement = BM_mesh_count_flag(bm, htype, hflag, 1); + + if (totelement) { + bmo_slot_buffer_alloc(op, slotname, totelement); + + if (htype & BM_VERT) { + for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } + + if (htype & BM_EDGE) { + for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } + + if (htype & BM_FACE) { + for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } + } + else { + output->len = 0; + } +} + +/* + * BMO_FLAG_TO_SLOT + * + * Copies elements of a certain type, which have a certain flag set + * into an output slot for an operator. + */ +void BMO_slot_from_flag(BMesh *bm, BMOperator *op, const char *slotname, + const short oflag, const char htype) +{ + BMIter elements; + BMHeader *ele; + BMOpSlot *output = BMO_slot_get(op, slotname); + int totelement = BMO_mesh_flag_count(bm, oflag, htype), i = 0; + + if (totelement) { + bmo_slot_buffer_alloc(op, slotname, totelement); + + if (htype & BM_VERT) { + for (ele = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) { + ((BMHeader **)output->data.p)[i] = ele; + i++; + } + } + } + + if (htype & BM_EDGE) { + for (ele = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) { + ((BMHeader **)output->data.p)[i] = ele; + i++; + } + } + } + + if (htype & BM_FACE) { + for (ele = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) { + ((BMHeader **)output->data.p)[i] = ele; + i++; + } + } + } + } + else { + output->len = 0; + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Header Flags elements in a slots buffer, automatically + * using the selection API where appropriate. + */ +void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOperator *op, const char *slotname, + const char hflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, data[i], TRUE); + } + BM_elem_flag_enable(data[i], hflag); + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Removes flags from elements in a slots buffer, automatically + * using the selection API where appropriate. + */ +void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOperator *op, const char *slotname, + const char hflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, data[i], FALSE); + } + + BM_elem_flag_disable(data[i], hflag); + } +} +int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag) +{ + int count = 0; + + if (v->e) { + BMEdge *curedge; + const int len = bmesh_disk_count(v); + int i; + + for (i = 0, curedge = v->e; i < len; i++) { + if (BMO_elem_flag_test(bm, curedge, oflag)) + count++; + curedge = bmesh_disk_nextedge(curedge, v); + } + } + + return count; +} + +/* + * + * BMO_FLAG_BUFFER + * + * Flags elements in a slots buffer + */ +void BMO_slot_buffer_flag_enable(BMesh *bm, BMOperator *op, const char *slotname, + const short oflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + BMO_elem_flag_enable(bm, (BMElemF *)data[i], oflag); + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Removes flags from elements in a slots buffer + */ +void BMO_slot_buffer_flag_disable(BMesh *bm, BMOperator *op, const char *slotname, + const short oflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + BMO_elem_flag_disable(bm, (BMElemF *)data[i], oflag); + } +} + + +/* + * + * ALLOC/FREE FLAG LAYER + * + * Used by operator stack to free/allocate + * private flag data. This is allocated + * using a mempool so the allocation/frees + * should be quite fast. + * + * BMESH_TODO: + * Investigate not freeing flag layers until + * all operators have been executed. This would + * save a lot of realloc potentially. + */ +static void bmo_flag_layer_alloc(BMesh *bm) +{ + BMElemF *ele; + /* set the index values since we are looping over all data anyway, + * may save time later on */ + int i; + + BMIter iter; + BLI_mempool *oldpool = bm->toolflagpool; /* old flag pool */ + BLI_mempool *newpool; + void *oldflags; + + /* store memcpy size for reuse */ + const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); + + bm->totflags++; + + /* allocate new flag poo */ + bm->toolflagpool = newpool = BLI_mempool_create(sizeof(BMFlagLayer)*bm->totflags, 512, 512, FALSE, FALSE); + + /* now go through and memcpy all the flags. Loops don't get a flag layer at this time.. */ + for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, old_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, old_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, old_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + + bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE); + + BLI_mempool_destroy(oldpool); +} + +static void bmo_flag_layer_free(BMesh *bm) +{ + BMElemF *ele; + /* set the index values since we are looping over all data anyway, + * may save time later on */ + int i; + + BMIter iter; + BLI_mempool *oldpool = bm->toolflagpool; + BLI_mempool *newpool; + void *oldflags; + + /* store memcpy size for reuse */ + const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer)); + + /* de-increment the totflags first.. */ + bm->totflags--; + /* allocate new flag poo */ + bm->toolflagpool = newpool = BLI_mempool_create(new_totflags_size, 512, 512, TRUE, FALSE); + + /* now go through and memcpy all the flag */ + for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, new_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, new_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, new_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + + bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE); + + BLI_mempool_destroy(oldpool); +} + +static void bmo_flag_layer_clear(BMesh *bm) +{ + BMElemF *ele; + /* set the index values since we are looping over all data anyway, + * may save time later on */ + int i; + + BMIter iter; + const int totflags_offset = bm->totflags - 1; + + /* now go through and memcpy all the flag */ + for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer)); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer)); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer)); + BM_elem_index_set(ele, i); /* set_inline */ + } + + bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE); +} + +void *BMO_slot_elem_first(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + if (slot->slottype != BMO_OP_SLOT_ELEMENT_BUF) + return NULL; + + return slot->data.buf ? *(void **)slot->data.buf : NULL; +} + +void *BMO_iter_new(BMOIter *iter, BMesh *UNUSED(bm), BMOperator *op, + const char *slotname, const char restrictmask) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + memset(iter, 0, sizeof(BMOIter)); + + iter->slot = slot; + iter->cur = 0; + iter->restrictmask = restrictmask; + + if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) { + if (iter->slot->data.ghash) { + BLI_ghashIterator_init(&iter->giter, slot->data.ghash); + } + else { + return NULL; + } + } + + return BMO_iter_step(iter); +} + +void *BMO_iter_step(BMOIter *iter) +{ + if (iter->slot->slottype == BMO_OP_SLOT_ELEMENT_BUF) { + BMHeader *h; + + if (iter->cur >= iter->slot->len) { + return NULL; + } + + h = ((void **)iter->slot->data.buf)[iter->cur++]; + while (!(iter->restrictmask & h->htype)) { + if (iter->cur >= iter->slot->len) { + return NULL; + } + + h = ((void **)iter->slot->data.buf)[iter->cur++]; + } + + return h; + } + else if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) { + struct BMOElemMapping *map; + void *ret = BLI_ghashIterator_getKey(&iter->giter); + map = BLI_ghashIterator_getValue(&iter->giter); + + iter->val = map + 1; + + BLI_ghashIterator_step(&iter->giter); + + return ret; + } + + return NULL; +} + +/* used for iterating over mapping */ +void *BMO_iter_map_value(BMOIter *iter) +{ + return iter->val; +} + +void *BMO_iter_map_value_p(BMOIter *iter) +{ + return *((void **)iter->val); +} + +float BMO_iter_map_value_f(BMOIter *iter) +{ + return *((float *)iter->val); +} + +/* error syste */ +typedef struct BMOpError { + struct BMOpError *next, *prev; + int errorcode; + BMOperator *op; + const char *msg; +} BMOpError; + +void BMO_error_clear(BMesh *bm) +{ + while (BMO_error_pop(bm, NULL, NULL)); +} + +void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg) +{ + BMOpError *err = MEM_callocN(sizeof(BMOpError), "bmop_error"); + + err->errorcode = errcode; + if (!msg) msg = bmo_error_messages[errcode]; + err->msg = msg; + err->op = owner; + + BLI_addhead(&bm->errorstack, err); +} + +int BMO_error_occurred(BMesh *bm) +{ + return bm->errorstack.first != NULL; +} + +/* returns error code or 0 if no erro */ +int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op) +{ + BMOpError *err = bm->errorstack.first; + if (!err) { + return 0; + } + + if (msg) *msg = err->msg; + if (op) *op = err->op; + + return err->errorcode; +} + +int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op) +{ + int errorcode = BMO_error_get(bm, msg, op); + + if (errorcode) { + BMOpError *err = bm->errorstack.first; + + BLI_remlink(&bm->errorstack, bm->errorstack.first); + MEM_freeN(err); + } + + return errorcode; +} + +/* example: + * BMO_CallOp(bm, "del %d %hv", DEL_ONLYFACES, BM_ELEM_SELECT); + * + * d - int + * i - int + * f - float + * hv - header flagged verts + * he - header flagged edges + * hf - header flagged faces + * fv - flagged verts + * fe - flagged edges + * ff - flagged faces + */ + +#define NEXT_CHAR(fmt) ((fmt)[0] != 0 ? (fmt)[1] : 0) + +static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name) +{ + int i; + + for (i = 0; def->slottypes[i].type; i++) { + if (!strncmp(name, def->slottypes[i].name, MAX_SLOTNAME)) { + return i; + } + } + + return -1; +} + +static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name) +{ + int i = bmesh_name_to_slotcode(def, name); + if (i < 0) { + fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, name); + } + + return i; +} + +static int bmesh_opname_to_opcode(const char *opname) +{ + int i; + + for (i = 0; i < bmesh_total_ops; i++) { + if (!strcmp(opname, opdefines[i]->name)) { + return i; + } + } + + fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, opname); + return -1; +} + +int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *_fmt, va_list vlist) +{ + BMOpDefine *def; + char *opname, *ofmt, *fmt; + char slotname[64] = {0}; + int i /*, n = strlen(fmt) */, stop /*, slotcode = -1 */, ret, type, state; + int noslot = 0; + + + /* basic useful info to help find where bmop formatting strings fail */ + int lineno = -1; +# define GOTO_ERROR { lineno = __LINE__; goto error; } + + + /* we muck around in here, so dup i */ + fmt = ofmt = BLI_strdup(_fmt); + + /* find operator nam */ + i = strcspn(fmt, " \t"); + + opname = fmt; + if (!opname[i]) noslot = 1; + opname[i] = '\0'; + + fmt += i + (noslot ? 0 : 1); + + i = bmesh_opname_to_opcode(opname); + + if (i == -1) { + MEM_freeN(ofmt); + return FALSE; + } + + BMO_op_init(bm, op, opname); + def = opdefines[i]; + + i = 0; + state = 1; /* 0: not inside slotcode name, 1: inside slotcode name */ + + while (*fmt) { + if (state) { + /* jump past leading whitespac */ + i = strspn(fmt, " \t"); + fmt += i; + + /* ignore trailing whitespac */ + if (!fmt[i]) + break; + + /* find end of slot name. currently this is + * a little flexible, allowing "slot=%f", + * "slot %f", "slot%f", and "slot\t%f". */ + i = strcspn(fmt, "= \t%"); + if (!fmt[i]) GOTO_ERROR; + + fmt[i] = 0; + + if (bmesh_name_to_slotcode_check(def, fmt) < 0) GOTO_ERROR; + + BLI_strncpy(slotname, fmt, sizeof(slotname)); + + state = 0; + fmt += i; + } + else { + switch (*fmt) { + case ' ': + case '\t': + case '=': + case '%': + break; + case 'm': { + int size, c; + + c = NEXT_CHAR(fmt); + fmt++; + + if (c == '3') size = 3; + else if (c == '4') size = 4; + else GOTO_ERROR; + + BMO_slot_mat_set(op, slotname, va_arg(vlist, void *), size); + state = 1; + break; + } + case 'v': { + BMO_slot_vec_set(op, slotname, va_arg(vlist, float *)); + state = 1; + break; + } + case 'e': { + BMHeader *ele = va_arg(vlist, void *); + BMOpSlot *slot = BMO_slot_get(op, slotname); + + slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(void *) * 4); + slot->len = 1; + *((void **)slot->data.buf) = ele; + + state = 1; + break; + } + case 's': { + BMOperator *op2 = va_arg(vlist, void *); + const char *slotname2 = va_arg(vlist, char *); + + BMO_slot_copy(op2, op, slotname2, slotname); + state = 1; + break; + } + case 'i': + case 'd': + BMO_slot_int_set(op, slotname, va_arg(vlist, int)); + state = 1; + break; + case 'p': + BMO_slot_ptr_set(op, slotname, va_arg(vlist, void *)); + state = 1; + break; + case 'f': + case 'h': + case 'a': + type = *fmt; + + if (NEXT_CHAR(fmt) == ' ' || NEXT_CHAR(fmt) == '\t' || NEXT_CHAR(fmt) == '\0') { + BMO_slot_float_set(op, slotname, va_arg(vlist, double)); + } + else { + ret = 0; + stop = 0; + while (1) { + switch (NEXT_CHAR(fmt)) { + case 'f': ret |= BM_FACE; break; + case 'e': ret |= BM_EDGE; break; + case 'v': ret |= BM_VERT; break; + default: + stop = 1; + break; + } + if (stop) { + break; + } + + fmt++; + } + + if (type == 'h') { + BMO_slot_from_hflag(bm, op, slotname, va_arg(vlist, int), ret); + } + else if (type == 'a') { + BMO_slot_from_all(bm, op, slotname, ret); + } + else { + BMO_slot_from_flag(bm, op, slotname, va_arg(vlist, int), ret); + } + } + + state = 1; + break; + default: + fprintf(stderr, + "%s: unrecognized bmop format char: %c, %d in '%s'\n", + __func__, *fmt, (int)(fmt - ofmt), ofmt); + break; + } + } + fmt++; + } + + MEM_freeN(ofmt); + return TRUE; +error: + + /* non urgent todo - explain exactly what is failing */ + fprintf(stderr, + "%s: error parsing formatting string, %d in '%s'\n see - %s:%d\n", + __func__, (int)(fmt - ofmt), _fmt, __FILE__, lineno); + MEM_freeN(ofmt); + + BMO_op_finish(bm, op); + return FALSE; + +#undef GOTO_ERROR + +} + + +int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + if (!BMO_op_vinitf(bm, op, fmt, list)) { + printf("%s: failed\n", __func__); + va_end(list); + return FALSE; + } + va_end(list); + + return TRUE; +} + +int BMO_op_callf(BMesh *bm, const char *fmt, ...) +{ + va_list list; + BMOperator op; + + va_start(list, fmt); + if (!BMO_op_vinitf(bm, &op, fmt, list)) { + printf("%s: failed, format is:\n \"%s\"\n", __func__, fmt); + va_end(list); + return FALSE; + } + + BMO_op_exec(bm, &op); + BMO_op_finish(bm, &op); + + va_end(list); + return TRUE; +} diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h new file mode 100644 index 00000000000..4cc338bfdd0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -0,0 +1,106 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_OPERATORS_PRIVATE_H__ +#define __BMESH_OPERATORS_PRIVATE_H__ + +/** \file blender/bmesh/intern/bmesh_operators_private.h + * \ingroup bmesh + */ + +struct BMesh; +struct BMOperator; + +void BMO_push(BMesh *bm, BMOperator *op); +void BMO_pop(BMesh *bm); + +void splitop_exec(BMesh *bm, BMOperator *op); +void spinop_exec(BMesh *bm, BMOperator *op); +void dupeop_exec(BMesh *bm, BMOperator *op); +void delop_exec(BMesh *bm, BMOperator *op); +void esubdivide_exec(BMesh *bmesh, BMOperator *op); +void edit2bmesh_exec(BMesh *bmesh, BMOperator *op); +void bmesh2edit_exec(BMesh *bmesh, BMOperator *op); +void triangulate_exec(BMesh *bmesh, BMOperator *op); +void dissolvefaces_exec(BMesh *bmesh, BMOperator *op); +void dissolveverts_exec(BMesh *bmesh, BMOperator *op); +void dissolvelimit_exec(BMesh *bmesh, BMOperator *op); +void bmesh_make_fgons_exec(BMesh *bmesh, BMOperator *op); +void extrude_edge_context_exec(BMesh *bm, BMOperator *op); +void connectverts_exec(BMesh *bm, BMOperator *op); +void makeprim_exec(BMesh *bm, BMOperator *op); +void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op); +void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op); +void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op); +void bmesh_translate_exec(BMesh *bm, BMOperator *op); +void bmesh_transform_exec(BMesh *bm, BMOperator *op); +void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op); +void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op); +void bmesh_rotate_exec(BMesh *bm, BMOperator *op); +void bmesh_makevert_exec(BMesh *bm, BMOperator *op); +void dissolveedges_exec(BMesh *bm, BMOperator *op); +void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op); +void bmesh_weldverts_exec(BMesh *bm, BMOperator *op); +void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op); +void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op); +void bmesh_mirror_exec(BMesh *bm, BMOperator *op); +void esplit_exec(BMesh *bm, BMOperator *op); +void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op); +void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op); +void bmesh_regionextend_exec(BMesh *bm, BMOperator *op); +void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op); +void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op); +void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op); +void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op); +void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op); +void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op); +void bmesh_collapse_exec(BMesh *bm, BMOperator *op); +void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op); +void bmesh_similaredges_exec(BMesh *bm, BMOperator *op); +void bmesh_similarverts_exec(BMesh *bm, BMOperator *op); +void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op); +void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op); +void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op); +void object_load_bmesh_exec(BMesh *bm, BMOperator *op); +void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op); +void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op); +void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op); +void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op); +void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op); +void bmesh_scale_exec(BMesh *bm, BMOperator *op); +void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op); +void bmesh_automerge_exec(BMesh *bm, BMOperator *op); +void bmesh_create_cone_exec(BMesh *bm, BMOperator *op); +void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op); +void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op); +void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op); +void bmesh_create_grid_exec(BMesh *bm, BMOperator *op); +void bmesh_create_cube_exec(BMesh *bm, BMOperator *op); +void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op); +void bmesh_bevel_exec(BMesh *bm, BMOperator *op); +void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op); +void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op); +void bmesh_create_circle_exec(BMesh *bm, BMOperator *op); +void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op); +void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op); + +#endif /* __BMESH_OPERATORS_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c new file mode 100644 index 00000000000..07945466a0c --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -0,0 +1,1069 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_polygon.c + * \ingroup bmesh + * + * This file contains code for dealing + * with polygons (normal/area calculation, + * tesselation, etc) + * + * BMESH_TODO: + * - Add in Tesselator frontend that creates + * BMTriangles from copied faces + * + * - Add in Function that checks for and flags + * degenerate faces. + */ + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "MEM_guardedalloc.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * TEST EDGE SIDE and POINT IN TRIANGLE + * + * Point in triangle tests stolen from scanfill.c. + * Used for tesselator + * + */ + +static short testedgeside(const double v1[2], const double v2[2], const double v3[2]) +{ + /* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */ + double inp; + + //inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]); + inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]); + + if (inp < 0.0) { + return FALSE; + } + else if (inp == 0) { + if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE; + if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE; + } + return TRUE; +} + +static short testedgesidef(const float v1[2], const float v2[2], const float v3[2]) +{ + /* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */ + double inp; + + //inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]); + inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]); + + if (inp < 0.0) { + return FALSE; + } + else if (inp == 0) { + if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE; + if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE; + } + return TRUE; +} + +static int point_in_triangle(const double v1[2], const double v2[2], const double v3[2], const double pt[2]) +{ + if (testedgeside(v1, v2, pt) && testedgeside(v2, v3, pt) && testedgeside(v3, v1, pt)) { + return TRUE; + } + return FALSE; +} + +/* + * COMPUTE POLY NORMAL + * + * Computes the normal of a planar + * polygon See Graphics Gems for + * computing newell normal. + * + */ + +static void compute_poly_normal(float normal[3], float (*verts)[3], int nverts) +{ + + float u[3], v[3], w[3]; /*, *w, v1[3], v2[3]; */ + float n[3] = {0.0f, 0.0f, 0.0f} /*, l, v1[3], v2[3] */; + /* double l2; */ + int i /*, s = 0 */; + + /* this fixes some weird numerical erro */ + add_v3_fl(verts[0], 0.0001f); + + for (i = 0; i < nverts; i++) { + copy_v3_v3(u, verts[i]); + copy_v3_v3(v, verts[(i + 1) % nverts]); + copy_v3_v3(w, verts[(i + 2) % nverts]); + +#if 0 + sub_v3_v3v3(v1, w, v); + sub_v3_v3v3(v2, v, u); + normalize_v3(v1); + normalize_v3(v2); + + l = dot_v3v3(v1, v2); + if (fabsf(l - 1.0) < 0.1f) { + continue; + } +#endif + /* newell's method + + so thats?: + (a[1] - b[1]) * (a[2] + b[2]); + a[1]*b[2] - b[1]*a[2] - b[1]*b[2] + a[1]*a[2] + + odd. half of that is the cross product. . .what's the + other half? + + also could be like a[1]*(b[2] + a[2]) - b[1]*(a[2] - b[2]) + */ + + n[0] += (u[1] - v[1]) * (u[2] + v[2]); + n[1] += (u[2] - v[2]) * (u[0] + v[0]); + n[2] += (u[0] - v[0]) * (u[1] + v[1]); + } + + if (normalize_v3_v3(normal, n) == 0.0f) { + normal[2] = 1.0f; /* other axis set to 0.0 */ + } + +#if 0 + l = len_v3(n); + /* fast square root, newton/babylonian method: + l2 = l * 0.1; + + l2 = (l / l2 + l2) * 0.5; + l2 = (l / l2 + l2) * 0.5; + l2 = (l / l2 + l2) * 0.5; + */ + + if (l == 0.0) { + normal[0] = 0.0f; + normal[1] = 0.0f; + normal[2] = 1.0f; + + return; + } + else { + l = 1.0f / l; + } + + mul_v3_fl(n, l); + + copy_v3_v3(normal, n); +#endif +} + +/* + * COMPUTE POLY CENTER + * + * Computes the centroid and + * area of a polygon in the X/Y + * plane. + * + */ + +static int compute_poly_center(float center[3], float *r_area, float (*verts)[3], int nverts) +{ + int i, j; + float atmp = 0.0f, xtmp = 0.0f, ytmp = 0.0f, ai; + + zero_v3(center); + + if (nverts < 3) + return FALSE; + + i = nverts - 1; + j = 0; + + while (j < nverts) { + ai = verts[i][0] * verts[j][1] - verts[j][0] * verts[i][1]; + atmp += ai; + xtmp += (verts[j][0] + verts[i][0]) * ai; + ytmp += (verts[j][1] + verts[i][1]) * ai; + i = j; + j += 1; + } + + if (r_area) + *r_area = atmp / 2.0f; + + if (atmp != 0) { + center[0] = xtmp / (3.0f * atmp); + center[1] = xtmp / (3.0f * atmp); + return TRUE; + } + return FALSE; +} + +float BM_face_area_calc(BMesh *bm, BMFace *f) +{ + BMLoop *l; + BMIter iter; + float (*verts)[3]; + float center[3]; + float area = 0.0f; + int i; + + BLI_array_fixedstack_declare(verts, BM_NGON_STACK_SIZE, f->len, __func__); + + i = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + copy_v3_v3(verts[i], l->v->co); + i++; + } + + compute_poly_center(center, &area, verts, f->len); + + BLI_array_fixedstack_free(verts); + + return area; +} + +/* + * computes center of face in 3d. uses center of bounding box. + */ +void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float r_cent[3]) +{ + BMIter iter; + BMLoop *l; + float min[3], max[3]; + int i; + + INIT_MINMAX(min, max); + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for (i = 0; l; l = BM_iter_step(&iter), i++) { + DO_MINMAX(l->v->co, min, max); + } + + mid_v3_v3v3(r_cent, min, max); +} + +void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float r_cent[3]) +{ + BMIter iter; + BMLoop *l; + int i; + + zero_v3(r_cent); + + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for (i = 0; l; l = BM_iter_step(&iter), i++) { + add_v3_v3(r_cent, l->v->co); + } + + if (f->len) mul_v3_fl(r_cent, 1.0f / (float)f->len); +} + +/* + * COMPUTE POLY PLANE + * + * Projects a set polygon's vertices to + * a plane defined by the average + * of its edges cross products + * + */ + +void compute_poly_plane(float (*verts)[3], int nverts) +{ + + float avgc[3], norm[3], mag, avgn[3]; + float *v1, *v2, *v3; + int i; + + if (nverts < 3) + return; + + zero_v3(avgn); + zero_v3(avgc); + + for (i = 0; i < nverts; i++) { + v1 = verts[i]; + v2 = verts[(i + 1) % nverts]; + v3 = verts[(i + 2) % nverts]; + normal_tri_v3(norm, v1, v2, v3); + + add_v3_v3(avgn, norm); + } + + /* what was this bit for */ + if (is_zero_v3(avgn)) { + avgn[0] = 0.0f; + avgn[1] = 0.0f; + avgn[2] = 1.0f; + } + else { + /* XXX, why is this being divided and _then_ normalized + * division could be removed? - campbell */ + avgn[0] /= nverts; + avgn[1] /= nverts; + avgn[2] /= nverts; + normalize_v3(avgn); + } + + for (i = 0; i < nverts; i++) { + v1 = verts[i]; + mag = dot_v3v3(v1, avgn); + madd_v3_v3fl(v1, avgn, -mag); + } +} + +/* + * BM LEGAL EDGES + * + * takes in a face and a list of edges, and sets to NULL any edge in + * the list that bridges a concave region of the face or intersects + * any of the faces's edges. + */ +#if 0 /* needs BLI math double versions of these functions */ +static void shrink_edged(double *v1, double *v2, double fac) +{ + double mid[3]; + + mid_v3_v3v3(mid, v1, v2); + + sub_v3_v3v3(v1, v1, mid); + sub_v3_v3v3(v2, v2, mid); + + mul_v3_fl(v1, fac); + mul_v3_fl(v2, fac); + + add_v3_v3v3(v1, v1, mid); + add_v3_v3v3(v2, v2, mid); +} +#endif + +static void shrink_edgef(float v1[3], float v2[3], const float fac) +{ + float mid[3]; + + mid_v3_v3v3(mid, v1, v2); + + sub_v3_v3v3(v1, v1, mid); + sub_v3_v3v3(v2, v2, mid); + + mul_v3_fl(v1, fac); + mul_v3_fl(v2, fac); + + add_v3_v3v3(v1, v1, mid); + add_v3_v3v3(v2, v2, mid); +} + + +/* + * POLY ROTATE PLANE + * + * Rotates a polygon so that it's + * normal is pointing towards the mesh Z axis + * + */ + +void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts) +{ + + float up[3] = {0.0f, 0.0f, 1.0f}, axis[3], q[4]; + float mat[3][3]; + double angle; + int i; + + cross_v3_v3v3(axis, normal, up); + + angle = saacos(dot_v3v3(normal, up)); + + if (angle == 0.0) return; + + axis_angle_to_quat(q, axis, (float)angle); + quat_to_mat3(mat, q); + + for (i = 0; i < nverts; i++) + mul_m3_v3(mat, verts[i]); +} + +/* + * BMESH UPDATE FACE NORMAL + * + * Updates the stored normal for the + * given face. Requires that a buffer + * of sufficient length to store projected + * coordinates for all of the face's vertices + * is passed in as well. + * + */ + +void BM_face_normal_update(BMesh *bm, BMFace *f) +{ + if (f->len >= 3) { + float (*proj)[3]; + + BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__); + + bmesh_update_face_normal(bm, f, f->no, proj); + + BLI_array_fixedstack_free(proj); + } +} +/* same as BM_face_normal_update but takes vertex coords */ +void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3]) +{ + if (f->len >= 3) { + float (*proj)[3]; + + BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__); + + bmesh_update_face_normal_vertex_cos(bm, f, no, proj, vertexCos); + + BLI_array_fixedstack_free(proj); + } +} + +void BM_edge_normals_update(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMFace *f; + + f = BM_iter_new(&iter, bm, BM_FACES_OF_EDGE, e); + for ( ; f; f = BM_iter_step(&iter)) { + BM_face_normal_update(bm, f); + } + + BM_vert_normal_update(bm, e->v1); + BM_vert_normal_update(bm, e->v2); +} + +void BM_vert_normal_update(BMesh *bm, BMVert *v) +{ + /* TODO, we can normalize each edge only once, then compare with previous edge */ + + BMIter eiter, liter; + BMEdge *e; + BMLoop *l; + float vec1[3], vec2[3], fac; + int len = 0; + + zero_v3(v->no); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + if (l->v == v) { + /* Same calculation used in BM_mesh_normals_update */ + sub_v3_v3v3(vec1, l->v->co, l->prev->v->co); + sub_v3_v3v3(vec2, l->next->v->co, l->v->co); + normalize_v3(vec1); + normalize_v3(vec2); + + fac = saacos(-dot_v3v3(vec1, vec2)); + + madd_v3_v3fl(v->no, l->f->no, fac); + + len++; + } + } + } + + if (len) { + normalize_v3(v->no); + } +} + +void BM_vert_normal_update_all(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMFace *f; + int len = 0; + + f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&iter), len++) { + BM_face_normal_update(bm, f); + } + + BM_vert_normal_update(bm, v); +} + +void bmesh_update_face_normal(BMesh *bm, BMFace *f, float no[3], + float (*projectverts)[3]) +{ + BMLoop *l; + + /* common cases first */ + switch (f->len) { + case 4: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l = l->next)->v; + BMVert *v4 = (l->next)->v; + normal_quad_v3(no, v1->co, v2->co, v3->co, v4->co); + break; + } + case 3: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l->next)->v; + normal_tri_v3(no, v1->co, v2->co, v3->co); + break; + } + case 0: + { + zero_v3(no); + break; + } + default: + { + BMIter iter; + int i = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + copy_v3_v3(projectverts[i], l->v->co); + i += 1; + } + compute_poly_normal(no, projectverts, f->len); + break; + } + } +} +/* exact same as 'bmesh_update_face_normal' but accepts vertex coords */ +void bmesh_update_face_normal_vertex_cos(BMesh *bm, BMFace *f, float no[3], + float (*projectverts)[3], float (*vertexCos)[3]) +{ + BMLoop *l; + + /* must have valid index data */ + BLI_assert((bm->elem_index_dirty & BM_VERT) == 0); + + /* common cases first */ + switch (f->len) { + case 4: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l = l->next)->v; + BMVert *v4 = (l->next)->v; + normal_quad_v3(no, + vertexCos[BM_elem_index_get(v1)], + vertexCos[BM_elem_index_get(v2)], + vertexCos[BM_elem_index_get(v3)], + vertexCos[BM_elem_index_get(v4)]); + break; + } + case 3: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l->next)->v; + normal_tri_v3(no, + vertexCos[BM_elem_index_get(v1)], + vertexCos[BM_elem_index_get(v2)], + vertexCos[BM_elem_index_get(v3)]); + break; + } + case 0: + { + zero_v3(no); + break; + } + default: + { + BMIter iter; + int i = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + copy_v3_v3(projectverts[i], vertexCos[BM_elem_index_get(l->v)]); + i += 1; + } + compute_poly_normal(no, projectverts, f->len); + break; + } + } +} + +/* + * BMESH FLIP NORMAL + * + * Reverses the winding of a face. + * Note that this updates the calculated + * normal. + */ +void BM_face_normal_flip(BMesh *bm, BMFace *f) +{ + bmesh_loop_reverse(bm, f); + negate_v3(f->no); +} + +/* detects if two line segments cross each other (intersects). + * note, there could be more winding cases then there needs to be. */ +static int UNUSED_FUNCTION(linecrosses)(const double v1[2], const double v2[2], const double v3[2], const double v4[2]) +{ + int w1, w2, w3, w4, w5; + + /* w1 = winding(v1, v3, v4); + w2 = winding(v2, v3, v4); + w3 = winding(v3, v1, v2); + w4 = winding(v4, v1, v2); + + return (w1 == w2) && (w3 == w4); */ + + w1 = testedgeside(v1, v3, v2); + w2 = testedgeside(v2, v4, v1); + w3 = !testedgeside(v1, v2, v3); + w4 = testedgeside(v3, v2, v4); + w5 = !testedgeside(v3, v1, v4); + return w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5; +} + +/* detects if two line segments cross each other (intersects). + * note, there could be more winding cases then there needs to be. */ +static int linecrossesf(const float v1[2], const float v2[2], const float v3[2], const float v4[2]) +{ + int w1, w2, w3, w4, w5 /*, re */; + float mv1[2], mv2[2], mv3[2], mv4[2]; + + /* now test windin */ + w1 = testedgesidef(v1, v3, v2); + w2 = testedgesidef(v2, v4, v1); + w3 = !testedgesidef(v1, v2, v3); + w4 = testedgesidef(v3, v2, v4); + w5 = !testedgesidef(v3, v1, v4); + + if (w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5) { + return TRUE; + } + +#define GETMIN2_AXIS(a, b, ma, mb, axis) ma[axis] = MIN2(a[axis], b[axis]), mb[axis] = MAX2(a[axis], b[axis]) +#define GETMIN2(a, b, ma, mb) GETMIN2_AXIS(a, b, ma, mb, 0); GETMIN2_AXIS(a, b, ma, mb, 1); + + GETMIN2(v1, v2, mv1, mv2); + GETMIN2(v3, v4, mv3, mv4); + + /* do an interval test on the x and y axe */ + /* first do x axi */ +#define T FLT_EPSILON * 15 + if ( ABS(v1[1] - v2[1]) < T && + ABS(v3[1] - v4[1]) < T && + ABS(v1[1] - v3[1]) < T) + { + return (mv4[0] >= mv1[0] && mv3[0] <= mv2[0]); + } + + /* now do y axi */ + if ( ABS(v1[0] - v2[0]) < T && + ABS(v3[0] - v4[0]) < T && + ABS(v1[0] - v3[0]) < T) + { + return (mv4[1] >= mv1[1] && mv3[1] <= mv2[1]); + } + + return FALSE; +} + +/* + * BM POINT IN FACE + * + * Projects co onto face f, and returns true if it is inside + * the face bounds. Note that this uses a best-axis projection + * test, instead of projecting co directly into f's orientation + * space, so there might be accuracy issues. + */ +int BM_face_point_inside_test(BMesh *bm, BMFace *f, const float co[3]) +{ + int ax, ay; + float co2[3], cent[3] = {0.0f, 0.0f, 0.0f}, out[3] = {FLT_MAX * 0.5f, FLT_MAX * 0.5f, 0}; + BMLoop *l_iter; + BMLoop *l_first; + int crosses = 0; + float eps = 1.0f + (float)FLT_EPSILON * 150.0f; + + if (dot_v3v3(f->no, f->no) <= FLT_EPSILON * 10) + BM_face_normal_update(bm, f); + + /* find best projection of face XY, XZ or YZ: barycentric weights of + * the 2d projected coords are the same and faster to compute + * + * this probably isn't all that accurate, but it has the advantage of + * being fast (especially compared to projecting into the face orientation) + */ + axis_dominant_v3(&ax, &ay, f->no); + + co2[0] = co[ax]; + co2[1] = co[ay]; + co2[2] = 0; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + cent[0] += l_iter->v->co[ax]; + cent[1] += l_iter->v->co[ay]; + } while ((l_iter = l_iter->next) != l_first); + + mul_v2_fl(cent, 1.0f / (float)f->len); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float v1[3], v2[3]; + + v1[0] = (l_iter->prev->v->co[ax] - cent[ax]) * eps + cent[ax]; + v1[1] = (l_iter->prev->v->co[ay] - cent[ay]) * eps + cent[ay]; + v1[2] = 0.0f; + + v2[0] = (l_iter->v->co[ax] - cent[ax]) * eps + cent[ax]; + v2[1] = (l_iter->v->co[ay] - cent[ay]) * eps + cent[ay]; + v2[2] = 0.0f; + + crosses += linecrossesf(v1, v2, co2, out) != 0; + } while ((l_iter = l_iter->next) != l_first); + + return crosses % 2 != 0; +} + +static int goodline(float (*projectverts)[3], BMFace *f, int v1i, + int v2i, int v3i, int UNUSED(nvert)) +{ + BMLoop *l_iter; + BMLoop *l_first; + double v1[3], v2[3], v3[3], pv1[3], pv2[3]; + int i; + + VECCOPY(v1, projectverts[v1i]); + VECCOPY(v2, projectverts[v2i]); + VECCOPY(v3, projectverts[v3i]); + + if (testedgeside(v1, v2, v3)) { + return FALSE; + } + + //for (i = 0; i < nvert; i++) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + i = BM_elem_index_get(l_iter->v); + if (i == v1i || i == v2i || i == v3i) { + continue; + } + + VECCOPY(pv1, projectverts[BM_elem_index_get(l_iter->v)]); + VECCOPY(pv2, projectverts[BM_elem_index_get(l_iter->next->v)]); + + //if (linecrosses(pv1, pv2, v1, v3)) return FALSE; + + if ( point_in_triangle(v1, v2, v3, pv1) || + point_in_triangle(v3, v2, v1, pv1)) + { + return FALSE; + } + } while ((l_iter = l_iter->next) != l_first); + return TRUE; +} +/* + * FIND EAR + * + * Used by tesselator to find + * the next triangle to 'clip off' + * of a polygon while tesselating. + * + */ + +static BMLoop *find_ear(BMesh *UNUSED(bm), BMFace *f, float (*verts)[3], const int nvert) +{ + BMVert *v1, *v2, *v3; + BMLoop *bestear = NULL; + + BMLoop *l_iter; + BMLoop *l_first; + /* float angle, bestangle = 180.0f; */ + int isear /*, i = 0 */; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + isear = 1; + + v1 = l_iter->prev->v; + v2 = l_iter->v; + v3 = l_iter->next->v; + + if (BM_edge_exists(v1, v3)) { + isear = 0; + } + else if (!goodline(verts, f, BM_elem_index_get(v1), BM_elem_index_get(v2), BM_elem_index_get(v3), nvert)) { + isear = 0; + } + + if (isear) { +#if 0 + angle = angle_v3v3v3(verts[v1->head.eflag2], verts[v2->head.eflag2], verts[v3->head.eflag2]); + if (!bestear || ABS(angle-45.0f) < bestangle) { + bestear = l; + bestangle = ABS(45.0f - angle); + } + + if (angle > 20 && angle < 90) break; + if (angle < 100 && i > 5) break; + i += 1; +#endif + + bestear = l_iter; + break; + } + } while ((l_iter = l_iter->next) != l_first); + + return bestear; +} + +/* + * BMESH TRIANGULATE FACE + * + * Triangulates a face using a + * simple 'ear clipping' algorithm + * that tries to favor non-skinny + * triangles (angles less than + * 90 degrees). If the triangulator + * has bits left over (or cannot + * triangulate at all) it uses a + * simple fan triangulation + * + * newfaces, if non-null, must be an array of BMFace pointers, + * with a length equal to f->len. it will be filled with the new + * triangles, and will be NULL-terminated. + */ +void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3], + const short newedge_oflag, const short newface_oflag, BMFace **newfaces) +{ + int i, done, nvert, nf_i = 0; + BMLoop *newl, *nextloop; + BMLoop *l_iter; + BMLoop *l_first; + /* BMVert *v; */ /* UNUSED */ + + /* copy vertex coordinates to vertspace arra */ + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + copy_v3_v3(projectverts[i], l_iter->v->co); + BM_elem_index_set(l_iter->v, i); /* set dirty! */ + i++; + } while ((l_iter = l_iter->next) != l_first); + + bm->elem_index_dirty |= BM_VERT; /* see above */ + + ///bmesh_update_face_normal(bm, f, f->no, projectverts); + + compute_poly_normal(f->no, projectverts, f->len); + poly_rotate_plane(f->no, projectverts, i); + + nvert = f->len; + + //compute_poly_plane(projectverts, i); + for (i = 0; i < nvert; i++) { + projectverts[i][2] = 0.0f; + } + + done = 0; + while (!done && f->len > 3) { + done = 1; + l_iter = find_ear(bm, f, projectverts, nvert); + if (l_iter) { + done = 0; + /* v = l->v; */ /* UNUSED */ + f = BM_face_split(bm, l_iter->f, l_iter->prev->v, + l_iter->next->v, + &newl, NULL); + copy_v3_v3(f->no, l_iter->f->no); + + if (!f) { + fprintf(stderr, "%s: triangulator failed to split face! (bmesh internal error)\n", __func__); + break; + } + + BMO_elem_flag_enable(bm, newl->e, newedge_oflag); + BMO_elem_flag_enable(bm, f, newface_oflag); + + if (newfaces) newfaces[nf_i++] = f; + + /* l = f->loopbase; + do { + if (l->v == v) { + f->loopbase = l; + break; + } + l = l->next; + } while (l != f->loopbase); */ + } + } + + if (f->len > 3) { + l_iter = BM_FACE_FIRST_LOOP(f); + while (l_iter->f->len > 3) { + nextloop = l_iter->next->next; + f = BM_face_split(bm, l_iter->f, l_iter->v, nextloop->v, + &newl, NULL); + if (!f) { + printf("triangle fan step of triangulator failed.\n"); + + /* NULL-terminat */ + if (newfaces) newfaces[nf_i] = NULL; + return; + } + + if (newfaces) newfaces[nf_i++] = f; + + BMO_elem_flag_enable(bm, newl->e, newedge_oflag); + BMO_elem_flag_enable(bm, f, newface_oflag); + l_iter = nextloop; + } + } + + /* NULL-terminat */ + if (newfaces) newfaces[nf_i] = NULL; +} + +/* each pair of loops defines a new edge, a split. this function goes + * through and sets pairs that are geometrically invalid to null. a + * split is invalid, if it forms a concave angle or it intersects other + * edges in the face, or it intersects another split. in the case of + * intersecting splits, only the first of the set of intersecting + * splits survives */ +void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len) +{ + BMIter iter; + BMLoop *l; + float v1[3], v2[3], v3[3]/*, v4[3 */, no[3], mid[3], *p1, *p2, *p3, *p4; + float out[3] = {-234324.0f, -234324.0f, 0.0f}; + float (*projverts)[3]; + float (*edgeverts)[3]; + float fac1 = 1.0000001f, fac2 = 0.9f; //9999f; //0.999f; + int i, j, a = 0, clen; + + BLI_array_fixedstack_declare(projverts, BM_NGON_STACK_SIZE, f->len, "projvertsb"); + BLI_array_fixedstack_declare(edgeverts, BM_NGON_STACK_SIZE * 2, len * 2, "edgevertsb"); + + i = 0; + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&iter)) { + BM_elem_index_set(l, i); /* set_loop */ + copy_v3_v3(projverts[i], l->v->co); + i++; + } + + for (i = 0; i < len; i++) { + copy_v3_v3(v1, loops[i][0]->v->co); + copy_v3_v3(v2, loops[i][1]->v->co); + + shrink_edgef(v1, v2, fac2); + + copy_v3_v3(edgeverts[a], v1); + a++; + copy_v3_v3(edgeverts[a], v2); + a++; + } + + compute_poly_normal(no, projverts, f->len); + poly_rotate_plane(no, projverts, f->len); + poly_rotate_plane(no, edgeverts, len * 2); + + for (i = 0, l = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l = l->next) { + p1 = projverts[i]; + out[0] = MAX2(out[0], p1[0]) + 0.01f; + out[1] = MAX2(out[1], p1[1]) + 0.01f; + out[2] = 0.0f; + p1[2] = 0.0f; + + //copy_v3_v3(l->v->co, p1); + } + + for (i = 0; i < len; i++) { + edgeverts[i * 2][2] = 0.0f; + edgeverts[i * 2 + 1][2] = 0.0f; + } + + /* do convexity test */ + for (i = 0; i < len; i++) { + copy_v3_v3(v2, edgeverts[i * 2]); + copy_v3_v3(v3, edgeverts[i * 2 + 1]); + + mid_v3_v3v3(mid, v2, v3); + + clen = 0; + for (j = 0; j < f->len; j++) { + p1 = projverts[j]; + p2 = projverts[(j + 1) % f->len]; + + copy_v3_v3(v1, p1); + copy_v3_v3(v2, p2); + + shrink_edgef(v1, v2, fac1); + + if (linecrossesf(p1, p2, mid, out)) clen++; + } + + if (clen % 2 == 0) { + loops[i][0] = NULL; + } + } + + /* do line crossing test */ + for (i = 0; i < f->len; i++) { + p1 = projverts[i]; + p2 = projverts[(i + 1) % f->len]; + + copy_v3_v3(v1, p1); + copy_v3_v3(v2, p2); + + shrink_edgef(v1, v2, fac1); + + for (j = 0; j < len; j++) { + if (!loops[j][0]) continue; + + p3 = edgeverts[j * 2]; + p4 = edgeverts[j * 2 + 1]; + + if (linecrossesf(v1, v2, p3, p4)) { + loops[j][0] = NULL; + } + } + } + + for (i = 0; i < len; i++) { + for (j = 0; j < len; j++) { + if (j == i) continue; + if (!loops[i][0]) continue; + if (!loops[j][0]) continue; + + p1 = edgeverts[i * 2]; + p2 = edgeverts[i * 2 + 1]; + p3 = edgeverts[j * 2]; + p4 = edgeverts[j * 2 + 1]; + + copy_v3_v3(v1, p1); + copy_v3_v3(v2, p2); + + shrink_edgef(v1, v2, fac1); + + if (linecrossesf(v1, v2, p3, p4)) { + loops[i][0] = NULL; + } + } + } + + BLI_array_fixedstack_free(projverts); + BLI_array_fixedstack_free(edgeverts); +} diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h new file mode 100644 index 00000000000..694d68549cd --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -0,0 +1,98 @@ +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_PRIVATE_H__ +#define __BMESH_PRIVATE_H__ + +/** \file blender/bmesh/intern/bmesh_private.h + * \ingroup bmesh + * + * Private function prototypes for bmesh public API. + * This file is a grab-bag of functions from various + * parts of the bmesh internals. + */ + +struct Link; +struct BMLoop; + +/* returns positive nonzero on error */ +int bmesh_check_element(BMesh *bm, void *element, const char htype); + +#define BM_CHECK_ELEMENT(bm, el) \ + if (bmesh_check_element(bm, el, ((BMHeader*)el)->htype)) { \ + printf("check_element failure, with code %i on line %i in file\n" \ + " \"%s\"\n\n", \ + bmesh_check_element(bm, el, ((BMHeader*)el)->htype), \ + __LINE__, __FILE__); \ + } + +#define BM_EDGE_DISK_LINK_GET(e, v) ( \ + ((v) == ((BMEdge*)(e))->v1) ? \ + &((e)->v1_disk_link) : \ + &((e)->v2_disk_link) \ + ) + +int bmesh_radial_length(struct BMLoop *l); +int bmesh_disk_count(BMVert *v); + +/* internal selection flushing */ +void bmesh_selectmode_flush(struct BMesh *bm); + +/*internal filter API*/ +void *bmesh_get_filter_callback(int type); +int bmesh_get_filter_argtype(int type); + +/* NOTE: ensure different parts of the API do not conflict + * on using these internal flags!*/ +#define _FLAG_JF 1 /* join faces */ +#define _FLAG_MF 2 /* make face */ + +#define BM_ELEM_API_FLAG_ENABLE(element, f) ((element)->oflags[0].pflag |= (f)) +#define BM_ELEM_API_FLAG_DISABLE(element, f) ((element)->oflags[0].pflag &= ~(f)) +#define BM_ELEM_API_FLAG_TEST(element, f) ((element)->oflags[0].pflag & (f)) + +/* Polygon Utilities ? FIXME... where do these each go? */ +/* newedgeflag sets a flag layer flag, obviously not the header flag. */ +void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3], + const short newedge_oflag, const short newface_oflag, BMFace **newfaces); +void bmesh_update_face_normal(struct BMesh *bm, struct BMFace *f, float no[3], + float (*projectverts)[3]); +void bmesh_update_face_normal_vertex_cos(struct BMesh *bm, struct BMFace *f, float no[3], + float (*projectverts)[3], float (*vertexCos)[3]); + +void compute_poly_plane(float (*verts)[3], int nverts); +void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts); +void bmesh_flip_normal(struct BMesh *bm, struct BMFace *f); + +BMEdge *bmesh_disk_next(BMEdge *e, BMVert *v); +BMEdge *bmesh_disk_prev(BMEdge *e, BMVert *v); + +/* include the rest of our private declarations */ +#include "bmesh_structure.h" +#include "bmesh_operators_private.h" + +#endif /* __BMESH_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c new file mode 100644 index 00000000000..089bc79e25d --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -0,0 +1,658 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_queries.c + * \ingroup bmesh + * + * This file contains functions for answering common + * Topological and geometric queries about a mesh, such + * as, "What is the angle between these two faces?" or, + * "How many faces are incident upon this vertex?" Tool + * authors should use the functions in this file instead + * of inspecting the mesh structure directly. + */ + +#include "BLI_math.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#define BM_OVERLAP (1 << 13) + +/* + * BMESH COUNT ELEMENT + * + * Return the amount of element of + * type 'type' in a given bmesh. + */ + +int BM_mesh_elem_count(BMesh *bm, const char htype) +{ + if (htype == BM_VERT) return bm->totvert; + else if (htype == BM_EDGE) return bm->totedge; + else if (htype == BM_FACE) return bm->totface; + + return 0; +} + + +/* + * BMESH VERT IN EDGE + * + * Returns whether or not a given vertex is + * is part of a given edge. + * + */ + +int BM_vert_in_edge(BMEdge *e, BMVert *v) +{ + return bmesh_vert_in_edge(e, v); +} + +/* + * BMESH OTHER EDGE IN FACE SHARING A VERTEX + * + * Returns an opposing loop that shares the same face. + * + */ + +BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v) +{ + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + + do { + if (l_iter->e == e) { + break; + } + } while ((l_iter = l_iter->next) != l_first); + + return l_iter->v == v ? l_iter->prev : l_iter->next; +} + +/* + * BMESH VERT IN FACE + * + * Returns whether or not a given vertex is + * is part of a given face. + * + */ + +int BM_vert_in_face(BMFace *f, BMVert *v) +{ + BMLoop *l_iter, *l_first; + +#ifdef USE_BMESH_HOLES + BMLoopList *lst; + for (lst = f->loops.first; lst; lst = lst->next) +#endif + { +#ifdef USE_BMESH_HOLES + l_iter = l_first = lst->first; +#else + l_iter = l_first = f->l_first; +#endif + do { + if (l_iter->v == v) { + return TRUE; + } + } while ((l_iter = l_iter->next) != l_first); + } + + return FALSE; +} + +/* + * BMESH VERTS IN FACE + * + * Compares the number of vertices in an array + * that appear in a given face + * + */ +int BM_verts_in_face(BMesh *bm, BMFace *f, BMVert **varr, int len) +{ + BMLoop *l_iter, *l_first; + +#ifdef USE_BMESH_HOLES + BMLoopList *lst; +#endif + + int i, count = 0; + + for (i = 0; i < len; i++) BMO_elem_flag_enable(bm, varr[i], BM_OVERLAP); + +#ifdef USE_BMESH_HOLES + for (lst = f->loops.first; lst; lst = lst->next) +#endif + { + +#ifdef USE_BMESH_HOLES + l_iter = l_first = lst->first; +#else + l_iter = l_first = f->l_first; +#endif + + do { + if (BMO_elem_flag_test(bm, l_iter->v, BM_OVERLAP)) { + count++; + } + + } while ((l_iter = l_iter->next) != l_first); + } + + for (i = 0; i < len; i++) BMO_elem_flag_disable(bm, varr[i], BM_OVERLAP); + + return count; +} + +/* + * BMESH EDGE IN FACE + * + * Returns whether or not a given edge is + * is part of a given face. + * + */ + +int BM_edge_in_face(BMFace *f, BMEdge *e) +{ + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + + do { + if (l_iter->e == e) { + return TRUE; + } + } while ((l_iter = l_iter->next) != l_first); + + return FALSE; +} + +/* + * BMESH VERTS IN EDGE + * + * Returns whether or not two vertices are in + * a given edge + * + */ + +int BM_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e) +{ + return bmesh_verts_in_edge(v1, v2, e); +} + +/* + * BMESH GET OTHER EDGEVERT + * + * Given a edge and one of its vertices, returns + * the other vertex. + * + */ + +BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v) +{ + return bmesh_edge_getothervert(e, v); +} + +/* + * BMESH VERT EDGECOUNT + * + * Returns the number of edges around this vertex. + */ + +int BM_vert_edge_count(BMVert *v) +{ + return bmesh_disk_count(v); +} + +/* + * BMESH EDGE FACECOUNT + * + * Returns the number of faces around this edge + */ + +int BM_edge_face_count(BMEdge *e) +{ + int count = 0; + BMLoop *curloop = NULL; + + if (e->l) { + curloop = e->l; + do { + count++; + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != e->l); + } + + return count; +} + +/* + * BMESH VERT FACECOUNT + * + * Returns the number of faces around this vert + */ + +int BM_vert_face_count(BMVert *v) +{ + int count = 0; + BMLoop *l; + BMIter iter; + + BM_ITER(l, &iter, NULL, BM_LOOPS_OF_VERT, v) { + count++; + } + + return count; +#if 0 //this code isn't working + BMEdge *curedge = NULL; + + if (v->e) { + curedge = v->e; + do { + if (curedge->l) count += BM_edge_face_count(curedge); + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + } + return count; +#endif +} + +/* + * BMESH WIRE VERT + * + * Tests whether or not the vertex is part of a wire edge. + * (ie: has no faces attached to it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_vert_is_wire(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *curedge; + + if (v->e == NULL) { + return FALSE; + } + + curedge = v->e; + do { + if (curedge->l) { + return FALSE; + } + + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + + return TRUE; +} + +/* + * BMESH WIRE EDGE + * + * Tests whether or not the edge is part of a wire. + * (ie: has no faces attached to it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_edge_is_wire(BMesh *UNUSED(bm), BMEdge *e) +{ + return (e->l) ? FALSE : TRUE; +} + +/* + * BMESH NONMANIFOLD VERT + * + * A vertex is non-manifold if it meets the following conditions: + * 1: Loose - (has no edges/faces incident upon it) + * 2: Joins two distinct regions - (two pyramids joined at the tip) + * 3: Is part of a non-manifold edge (edge with more than 2 faces) + * 4: Is part of a wire edge + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_vert_is_manifold(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *e, *oe; + BMLoop *l; + int len, count, flag; + + if (v->e == NULL) { + /* loose vert */ + return FALSE; + } + + /* count edges while looking for non-manifold edges */ + oe = v->e; + for (len = 0, e = v->e; e != oe || (e == oe && len == 0); len++, e = bmesh_disk_nextedge(e, v)) { + if (e->l == NULL) { + /* loose edge */ + return FALSE; + } + + if (bmesh_radial_length(e->l) > 2) { + /* edge shared by more than two faces */ + return FALSE; + } + } + + count = 1; + flag = 1; + e = NULL; + oe = v->e; + l = oe->l; + while (e != oe) { + l = (l->v == v) ? l->prev : l->next; + e = l->e; + count++; /* count the edges */ + + if (flag && l->radial_next == l) { + /* we've hit the edge of an open mesh, reset once */ + flag = 0; + count = 1; + oe = e; + e = NULL; + l = oe->l; + } + else if (l->radial_next == l) { + /* break the loop */ + e = oe; + } + else { + l = l->radial_next; + } + } + + if (count < len) { + /* vert shared by multiple regions */ + return FALSE; + } + + return TRUE; +} + +/* + * BMESH NONMANIFOLD EDGE + * + * Tests whether or not this edge is manifold. + * A manifold edge either has 1 or 2 faces attached + * to it. + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_edge_is_manifold(BMesh *UNUSED(bm), BMEdge *e) +{ + int count = BM_edge_face_count(e); + if (count != 2 && count != 1) { + return FALSE; + } + return TRUE; +} + +/* + * BMESH BOUNDARY EDGE + * + * Tests whether or not an edge is on the boundary + * of a shell (has one face associated with it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_edge_is_boundry(BMEdge *e) +{ + int count = BM_edge_face_count(e); + if (count == 1) { + return TRUE; + } + return FALSE; +} + +/* + * BMESH FACE SHAREDEDGES + * + * Counts the number of edges two faces share (if any) + * + * BMESH_TODO: + * Move this to structure, and wrap. + * + * Returns - + * Integer + */ + +int BM_face_share_edges(BMFace *f1, BMFace *f2) +{ + BMLoop *l_iter; + BMLoop *l_first; + int count = 0; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f1); + do { + if (bmesh_radial_find_face(l_iter->e, f2)) { + count++; + } + } while ((l_iter = l_iter->next) != l_first); + + return count; +} + +/* + * + * BMESH EDGE SHARE FACES + * + * Tests to see if e1 shares any faces with e2 + * + */ + +int BM_edge_share_faces(BMEdge *e1, BMEdge *e2) +{ + BMLoop *l; + BMFace *f; + + if (e1->l && e2->l) { + l = e1->l; + do { + f = l->f; + if (bmesh_radial_find_face(e2, f)) { + return TRUE; + } + l = l->radial_next; + } while (l != e1->l); + } + return FALSE; +} + +/** + * + * BMESH EDGE SHARE A VERTEX + * + * Tests to see if e1 shares a vertex with e2 + * + */ + +int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2) +{ + return (e1->v1 == e2->v1 || + e1->v1 == e2->v2 || + e1->v2 == e2->v1 || + e1->v2 == e2->v2); +} + +/** + * + * BMESH EDGE ORDERED VERTS + * + * Returns the verts of an edge as used in a face + * if used in a face at all, otherwise just assign as used in the edge. + * + * Useful to get a determanistic winding order when calling + * BM_face_create_ngon() on an arbitrary array of verts, + * though be sure to pick an edge which has a face. + * + */ + +void BM_edge_ordered_verts(BMEdge *edge, BMVert **r_v1, BMVert **r_v2) +{ + if ( (edge->l == NULL) || + ( ((edge->l->prev->v == edge->v1) && (edge->l->v == edge->v2)) || + ((edge->l->v == edge->v1) && (edge->l->next->v == edge->v2)) ) + ) + { + *r_v1 = edge->v1; + *r_v2 = edge->v2; + } + else { + *r_v1 = edge->v2; + *r_v2 = edge->v1; + } +} + +/* + * BMESH FACE ANGLE + * + * Calculates the angle between two faces. + * Assumes the face normals are correct. + * + * Returns - + * Float. + */ + +float BM_edge_face_angle(BMesh *UNUSED(bm), BMEdge *e) +{ + if (BM_edge_face_count(e) == 2) { + BMLoop *l1 = e->l; + BMLoop *l2 = e->l->radial_next; + return angle_normalized_v3v3(l1->f->no, l2->f->no); + } + else { + return (float)M_PI / 2.0f; /* acos(0.0) */ + } +} + +/* + * BMESH FACE ANGLE + * + * Calculates the angle a verts 2 edges. + * + * Returns - + * Float. + */ +float BM_vert_edge_angle(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *e1, *e2; + + /* saves BM_vert_edge_count(v) and and edge iterator, + * get the edges and count them both at once */ + + if ((e1 = v->e) && + (e2 = bmesh_disk_nextedge(e1, v)) && + /* make sure we come full circle and only have 2 connected edges */ + (e1 == bmesh_disk_nextedge(e2, v))) + { + BMVert *v1 = BM_edge_other_vert(e1, v); + BMVert *v2 = BM_edge_other_vert(e2, v); + + return M_PI - angle_v3v3v3(v1->co, v->co, v2->co); + } + else { + return (float)M_PI / 2.0f; /* acos(0.0) */ + } +} + +/* + * BMESH EXIST FACE OVERLAPS + * + * Given a set of vertices (varr), find out if + * all those vertices overlap an existing face. + * + * Returns: + * 0 for no overlap + * 1 for overlap + * + * + */ + +int BM_face_exists_overlap(BMesh *bm, BMVert **varr, int len, BMFace **overlapface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + if (overlapface) *overlapface = NULL; + + for (i = 0; i < len; i++) { + f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]); + while (f) { + amount = BM_verts_in_face(bm, f, varr, len); + if (amount >= len) { + if (overlapface) *overlapface = f; + return TRUE; + } + f = BM_iter_step(&vertfaces); + } + } + return FALSE; +} + +/* + * BMESH FACE EXISTS + * + * Given a set of vertices (varr), find out if + * there is a face with exactly those vertices + * (and only those vertices). + * + * Returns: + * 0 for no face found + * 1 for face found + */ + +int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + if (existface) *existface = NULL; + + for (i = 0; i < len; i++) { + f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]); + while (f) { + amount = BM_verts_in_face(bm, f, varr, len); + if (amount == len && amount == f->len) { + if (existface) *existface = f; + return TRUE; + } + f = BM_iter_step(&vertfaces); + } + } + return FALSE; +} diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c new file mode 100644 index 00000000000..268d8bfa346 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -0,0 +1,1103 @@ +/* + * ***** 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) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_structure.c + * \ingroup bmesh + * + * Low level routines for manipulating the BM structure. + */ + +#include "bmesh.h" +#include "bmesh_private.h" + +/** + * MISC utility functions. + * + */ + +int bmesh_vert_in_edge(BMEdge *e, BMVert *v) +{ + if (e->v1 == v || e->v2 == v) return TRUE; + return FALSE; +} +int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e) +{ + if (e->v1 == v1 && e->v2 == v2) return TRUE; + else if (e->v1 == v2 && e->v2 == v1) return TRUE; + return FALSE; +} + +BMVert *bmesh_edge_getothervert(BMEdge *e, BMVert *v) { + if (e->v1 == v) { + return e->v2; + } + else if (e->v2 == v) { + return e->v1; + } + return NULL; +} + +int bmesh_edge_swapverts(BMEdge *e, BMVert *orig, BMVert *newv) +{ + if (e->v1 == orig) { + e->v1 = newv; + e->v1_disk_link.next = e->v1_disk_link.prev = NULL; + return TRUE; + } + else if (e->v2 == orig) { + e->v2 = newv; + e->v2_disk_link.next = e->v2_disk_link.prev = NULL; + return TRUE; + } + return FALSE; +} + +/** + * BMESH CYCLES + * (this is somewhat outdate, though bits of its API are still used) - joeedh + * + * Cycles are circular doubly linked lists that form the basis of adjacency + * information in the BME modeller. Full adjacency relations can be derived + * from examining these cycles very quickly. Although each cycle is a double + * circular linked list, each one is considered to have a 'base' or 'head', + * and care must be taken by Euler code when modifying the contents of a cycle. + * + * The contents of this file are split into two parts. First there are the + * bmesh_cycle family of functions which are generic circular double linked list + * procedures. The second part contains higher level procedures for supporting + * modification of specific cycle types. + * + * The three cycles explicitly stored in the BM data structure are as follows: + * + * 1: The Disk Cycle - A circle of edges around a vertex + * Base: vertex->edge pointer. + * + * This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains + * two bmesh_CycleNode structures to keep track of that edge's membership in the disk cycle + * of each of its vertices. However for any given vertex it may be the first in some edges + * in its disk cycle and the second for others. The bmesh_disk_XXX family of functions contain + * some nice utilities for navigating disk cycles in a way that hides this detail from the + * tool writer. + * + * Note that the disk cycle is completley independant from face data. One advantage of this + * is that wire edges are fully integrated into the topology database. Another is that the + * the disk cycle has no problems dealing with non-manifold conditions involving faces. + * + * Functions relating to this cycle: + * + * bmesh_disk_append_edge + * bmesh_disk_remove_edge + * bmesh_disk_nextedge + * bmesh_disk_getpointer + * + * 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge + * Base: edge->l->radial structure. + * + * The radial cycle is similar to the radial cycle in the radial edge data structure.* + * Unlike the radial edge however, the radial cycle does not require a large amount of memory + * to store non-manifold conditions since BM does not keep track of region/shell + * information. + * + * Functions relating to this cycle: + * + * bmesh_radial_append + * bmesh_radial_remove_loop + * bmesh_radial_nextloop + * bmesh_radial_find_face + * + * + * 3: The Loop Cycle - A circle of face edges around a polygon. + * Base: polygon->lbase. + * + * The loop cycle keeps track of a faces vertices and edges. It should be noted that the + * direction of a loop cycle is either CW or CCW depending on the face normal, and is + * not oriented to the faces editedges. + * + * Functions relating to this cycle: + * + * bmesh_cycle_XXX family of functions. + * + * + * Note that the order of elements in all cycles except the loop cycle is undefined. This + * leads to slightly increased seek time for deriving some adjacency relations, however the + * advantage is that no intrinsic properties of the data structures are dependant upon the + * cycle order and all non-manifold conditions are represented trivially. + * + */ +int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v) +{ + if (!v->e) { + BMDiskLink *dl1 = BM_EDGE_DISK_LINK_GET(e, v); + + v->e = e; + dl1->next = dl1->prev = e; + } + else { + BMDiskLink *dl1, *dl2, *dl3; + + dl1 = BM_EDGE_DISK_LINK_GET(e, v); + dl2 = BM_EDGE_DISK_LINK_GET(v->e, v); + dl3 = dl2->prev ? BM_EDGE_DISK_LINK_GET(dl2->prev, v) : NULL; + + dl1->next = v->e; + dl1->prev = dl2->prev; + + dl2->prev = e; + if (dl3) + dl3->next = e; + } + + return TRUE; +} + +void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v) +{ + BMDiskLink *dl1, *dl2; + + dl1 = BM_EDGE_DISK_LINK_GET(e, v); + if (dl1->prev) { + dl2 = BM_EDGE_DISK_LINK_GET(dl1->prev, v); + dl2->next = dl1->next; + } + + if (dl1->next) { + dl2 = BM_EDGE_DISK_LINK_GET(dl1->next, v); + dl2->prev = dl1->prev; + } + + if (v->e == e) + v->e = (e != (BMEdge *)dl1->next) ? (BMEdge *)dl1->next : NULL; + + dl1->next = dl1->prev = NULL; +} + +struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v) +{ + if (v == e->v1) + return e->v1_disk_link.next; + if (v == e->v2) + return e->v2_disk_link.next; + return NULL; +} + +static BMEdge *bmesh_disk_prevedge(BMEdge *e, BMVert *v) +{ + if (v == e->v1) + return e->v1_disk_link.prev; + if (v == e->v2) + return e->v2_disk_link.prev; + return NULL; +} + +BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2) +{ + BMEdge *curedge, *startedge; + + if (v1->e) { + startedge = v1->e; + curedge = startedge; + do { + if (bmesh_verts_in_edge(v1, v2, curedge)) { + return curedge; + } + + curedge = bmesh_disk_nextedge(curedge, v1); + } while (curedge != startedge); + } + + return NULL; +} + +int bmesh_disk_count(struct BMVert *v) +{ + BMEdge *e = v->e; + int i = 0; + + if (!e) { + return 0; + } + + do { + if (!e) { + return 0; + } + + e = bmesh_disk_nextedge(e, v); + + if (i >= (1 << 20)) { + printf("bmesh error: infinite loop in disk cycle!\n"); + return 0; + } + + i++; + } while (e != v->e); + + return i; +} + +int bmesh_disk_validate(int len, BMEdge *e, BMVert *v) +{ + BMEdge *e2; + + if (!BM_vert_in_edge(e, v)) + return FALSE; + if (bmesh_disk_count(v) != len || len == 0) + return FALSE; + + e2 = e; + do { + if (len != 1 && bmesh_disk_prevedge(e2, v) == e2) { + return FALSE; + } + + e2 = bmesh_disk_nextedge(e2, v); + } while (e2 != e); + + return TRUE; +} + +/* + * BME DISK COUNT FACE VERT + * + * Counts the number of loop users + * for this vertex. Note that this is + * equivalent to counting the number of + * faces incident upon this vertex + */ + +int bmesh_disk_count_facevert(BMVert *v) +{ + BMEdge *curedge; + int count = 0; + + /* is there an edge on this vert at all */ + if (!v->e) + return count; + + /* first, loop around edge */ + curedge = v->e; + do { + if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v); + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + + return count; +} + +struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = e; + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + + return NULL; +} + +struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = bmesh_disk_nextedge(e, v); + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + return e; +} + +/*****radial cycle functions, e.g. loops surrounding edges**** */ +int bmesh_radial_validate(int radlen, BMLoop *l) +{ + BMLoop *l2 = l; + int i = 0; + + if (bmesh_radial_length(l) != radlen) + return FALSE; + + do { + if (!l2) { + bmesh_error(); + return FALSE; + } + + if (l2->e != l->e) + return FALSE; + if (l2->v != l->e->v1 && l2->v != l->e->v2) + return FALSE; + + if (i > BM_LOOP_RADIAL_MAX) { + bmesh_error(); + return FALSE; + } + + i++; + l2 = l2->radial_next; + } while (l2 != l); + + return TRUE; +} + +/* + * BMESH RADIAL REMOVE LOOP + * + * Removes a loop from an radial cycle. If edge e is non-NULL + * it should contain the radial cycle, and it will also get + * updated (in the case that the edge's link into the radial + * cycle was the loop which is being removed from the cycle). + */ +void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e) +{ + /* if e is non-NULL, l must be in the radial cycle of e */ + if (e && e != l->e) { + bmesh_error(); + } + + if (l->radial_next != l) { + if (e && l == e->l) + e->l = l->radial_next; + + l->radial_next->radial_prev = l->radial_prev; + l->radial_prev->radial_next = l->radial_next; + } + else { + if (e) { + if (l == e->l) { + e->l = NULL; + } + else { + bmesh_error(); + } + } + } + + /* l is no longer in a radial cycle; empty the links + * to the cycle and the link back to an edge */ + l->radial_next = l->radial_prev = NULL; + l->e = NULL; +} + + +/* + * BME RADIAL FIND FIRST FACE VERT + * + * Finds the first loop of v around radial + * cycle + */ +BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = l; + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return NULL; +} + +BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = bmesh_radial_nextloop(l); + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return l; +} + +BMLoop *bmesh_radial_nextloop(BMLoop *l) +{ + return l->radial_next; +} + +int bmesh_radial_length(BMLoop *l) +{ + BMLoop *l2 = l; + int i = 0; + + if (!l) + return 0; + + do { + if (!l2) { + /* radial cycle is broken (not a circulat loop) */ + bmesh_error(); + return 0; + } + + i++; + l2 = l2->radial_next; + if (i >= BM_LOOP_RADIAL_MAX) { + bmesh_error(); + return -1; + } + } while (l2 != l); + + return i; +} + +void bmesh_radial_append(BMEdge *e, BMLoop *l) +{ + if (e->l == NULL) { + e->l = l; + l->radial_next = l->radial_prev = l; + } + else { + l->radial_prev = e->l; + l->radial_next = e->l->radial_next; + + e->l->radial_next->radial_prev = l; + e->l->radial_next = l; + + e->l = l; + } + + if (l->e && l->e != e) { + /* l is already in a radial cycle for a different edge */ + bmesh_error(); + } + + l->e = e; +} + +int bmesh_radial_find_face(BMEdge *e, BMFace *f) +{ + BMLoop *curloop; + int i, len; + + len = bmesh_radial_length(e->l); + for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) { + if (curloop->f == f) + return TRUE; + } + return FALSE; +} + +/* + * BME RADIAL COUNT FACE VERT + * + * Returns the number of times a vertex appears + * in a radial cycle + * + */ + +int bmesh_radial_count_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + int count = 0; + curloop = l; + do { + if (curloop->v == v) count++; + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return count; +} + +/*****loop cycle functions, e.g. loops surrounding a face**** */ +int bmesh_loop_validate(BMFace *f) +{ + int i; + int len = f->len; + BMLoop *l_iter, *l_first; + + l_first = BM_FACE_FIRST_LOOP(f); + + if (l_first == NULL) { + return FALSE; + } + + /* Validate that the face loop cycle is the length specified by f->len */ + for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) { + if ( (l_iter->f != f) || + (l_iter == l_first)) + { + return FALSE; + } + } + if (l_iter != l_first) { + return FALSE; + } + + /* Validate the loop->prev links also form a cycle of length f->len */ + for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) { + if (l_iter == l_first) { + return FALSE; + } + } + if (l_iter != l_first) { + return FALSE; + } + + return TRUE; +} + + +#if 0 +void bmesh_cycle_append(void *h, void *nt) +{ + BMNode *oldtail, *head, *newtail; + + head = (BMNode *)h; + newtail = (BMNode *)nt; + + if (head->next == NULL) { + head->next = newtail; + head->prev = newtail; + newtail->next = head; + newtail->prev = head; + } + else { + oldtail = head->prev; + oldtail->next = newtail; + newtail->next = head; + newtail->prev = oldtail; + head->prev = newtail; + + } +} + +/** + * bmesh_cycle_length + * + * Count the nodes in a cycle. + * + * Returns - + * Integer + */ + +int bmesh_cycle_length(BMEdge *e, BMVert *v) +{ + BMEdge *next, *prev, *cur; + int len, vi = v == e->v1 ? 0 : 1; + + /* should skip 2 forward if v is 1, happily reduces to (v * 2) */ + prev = *(&e->v1_prev + vi * 2); + + cur = e; + len = 1; + while (cur != prev) { + vi = cur->v1 == v ? 0 : 1; + + len++; + cur = *(&cur->v1_next + vi * 2); + } + + return len; +} + +/** + * bmesh_cycle_remove + * + * Removes a node from a cycle. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_cycle_remove(void *h, void *remn) +{ + int i, len; + BMNode *head, *remnode, *curnode; + + head = (BMNode *)h; + remnode = (BMNode *)remn; + len = bmesh_cycle_length(h); + + if (len == 1 && head == remnode) { + head->next = NULL; + head->prev = NULL; + return TRUE; + } + else { + for (i = 0, curnode = head; i < len; curnode = curnode->next) { + if (curnode == remnode) { + remnode->prev->next = remnode->next; + remnode->next->prev = remnode->prev; + /* zero out remnode pointers, important */ + //remnode->next = NULL; + //remnode->prev = NULL; + return TRUE; + + } + } + } + return FALSE; +} + +/** + * bmesh_cycle_validate + * + * Validates a cycle. Takes as an argument the expected length of the cycle and + * a pointer to the cycle head or base. + * + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_cycle_validate(int len, void *h) +{ + int i; + BMNode *curnode, *head; + head = (BMNode *)h; + + /* forward validatio */ + for (i = 0, curnode = head; i < len; i++, curnode = curnode->next); + if (curnode != head) { + return FALSE; + } + + /* reverse validatio */ + for (i = 0, curnode = head; i < len; i++, curnode = curnode->prev); + if (curnode != head) { + return FALSE; + } + + return TRUE; +} + +/* Begin Disk Cycle routine */ + +/** + * bmesh_disk_nextedge + * + * Find the next edge in a disk cycle + * + * Returns - + * Pointer to the next edge in the disk cycle for the vertex v. + */ + +BMEdge *bmesh_disk_nextedge(BMEdge *e, BMVert *v) +{ + if (bmesh_vert_in_edge(e, v)) { + if (e->v1 == v) { + return e->d1.next->data; + } + else if (e->v2 == v) { + return e->d2.next->data; + } + } + return NULL; +} + +/** + * bmesh_disk_getpointer + * + * Given an edge and one of its vertices, find the apporpriate CycleNode + * + * Returns - + * Pointer to bmesh_CycleNode. + */ +BMNode *bmesh_disk_getpointer(BMEdge *e, BMVert *v) +{ + /* returns pointer to the cycle node for the appropriate vertex in this dis */ + if (e->v1 == v) { + return &(e->d1); + } + else if (e->v2 == v) { + return &(e->d2); + } + return NULL; +} + +/** + * bmesh_disk_append_edge + * + * Appends edge to the end of a vertex disk cycle. + * + * Returns - + * 1 for success, 0 for failure + */ + +int bmesh_disk_append_edge(BMEdge *e, BMVert *v) +{ + + BMNode *base, *tail; + + /* check to make sure v is in */ + if (bmesh_vert_in_edge(e, v) == 0) { + return FALSE; + } + + /* check for loose vert firs */ + if (v->e == NULL) { + v->e = e; + base = tail = bmesh_disk_getpointer(e, v); + bmesh_cycle_append(base, tail); /* circular reference is ok */ + return TRUE; + } + + /* insert e at the end of disk cycle and make it the new v-> */ + base = bmesh_disk_getpointer(v->e, v); + tail = bmesh_disk_getpointer(e, v); + bmesh_cycle_append(base, tail); + return TRUE; +} + +/** + * bmesh_disk_remove_edge + * + * Removes an edge from a disk cycle. If the edge to be removed is + * at the base of the cycle, the next edge becomes the new base. + * + * + * Returns - + * Nothing + */ + +void bmesh_disk_remove_edge(BMEdge *e, BMVert *v) +{ + BMNode *base, *remnode; + BMEdge *newbase; + int len; + + base = bmesh_disk_getpointer(v->e, v); + remnode = bmesh_disk_getpointer(e, v); + + /* first deal with v->e pointer.. */ + len = bmesh_cycle_length(base); + if (len == 1) newbase = NULL; + else if (v->e == e) newbase = base->next-> data; + else newbase = v->e; + + /* remove and rebas */ + bmesh_cycle_remove(base, remnode); + v->e = newbase; +} + +/** + * bmesh_disk_next_edgeflag + * + * Searches the disk cycle of v, starting with e, for the + * next edge that has either eflag or tflag. + * + * bmesh_Edge pointer. + */ + +BMEdge *bmesh_disk_next_edgeflag(BMEdge *e, BMVert *v, int eflag, int tflag) +{ + + BMNode *diskbase; + BMEdge *curedge; + int len, ok; + + if (eflag && tflag) { + return NULL; + } + + ok = bmesh_vert_in_edge(e, v); + if (ok) { + diskbase = bmesh_disk_getpointer(e, v); + len = bmesh_cycle_length(diskbase); + curedge = bmesh_disk_nextedge(e, v); + while (curedge != e) { + if (eflag) { + if (curedge->head.eflag1 == eflag) { + return curedge; + } + } + + curedge = bmesh_disk_nextedge(curedge, v); + } + } + return NULL; +} + +/** + * bmesh_disk_count_edgeflag + * + * Counts number of edges in this verts disk cycle which have + * either eflag or tflag (but not both!) + * + * Returns - + * Integer. + */ + +int bmesh_disk_count_edgeflag(BMVert *v, int eflag, int tflag) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len = 0, count = 0; + + if (v->e) { + + /* tflag and eflag are reserved for different functions */ + if (eflag && tflag) { + return 0; + } + + diskbase = bmesh_disk_getpointer(v->e, v); + len = bmesh_cycle_length(diskbase); + + for (i = 0, curedge = v->e; i < len; i++) { + if (eflag) { + if (curedge->head.eflag1 == eflag) count++; + } + curedge = bmesh_disk_nextedge(curedge, v); + } + } + return count; +} + + +int bmesh_disk_hasedge(BMVert *v, BMEdge *e) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len = 0; + + if (v->e) { + diskbase = bmesh_disk_getpointer(v->e, v); + len = bmesh_cycle_length(diskbase); + + for (i = 0, curedge = v->e; i < len; i++) { + if (curedge == e) { + return TRUE; + } + else curedge = bmesh_disk_nextedge(curedge, v); + } + } + return FALSE; +} + +BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len = 0; + + if (v1->e) { + diskbase = bmesh_disk_getpointer(v1->e, v1); + len = bmesh_cycle_length(diskbase); + + for (i = 0, curedge = v1->e; i < len; i++, curedge = bmesh_disk_nextedge(curedge, v1)) { + if (bmesh_verts_in_edge(v1, v2, curedge)) { + return curedge; + } + } + } + + return NULL; +} + +/* end disk cycle routine */ +BMLoop *bmesh_radial_nextloop(BMLoop *l) +{ + return l->radial_next; +} + +void bmesh_radial_append(BMEdge *e, BMLoop *l) +{ + if (e->l == NULL) e->l = l; + bmesh_cycle_append(&(e->l->radial), &(l->radial)); +} + +void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e) +{ + BMLoop *newbase; + int len; + + /* deal with edge->l pointe */ + len = bmesh_cycle_length(&(e->l->radial)); + if (len == 1) newbase = NULL; + else if (e->l == l) newbase = e->l->radial_next; + else newbase = e->l; + + /* remove and rebas */ + bmesh_cycle_remove(&(e->l->radial), &(l->radial)); + e->l = newbase; +} + +int bmesh_radial_find_face(BMEdge *e, BMFace *f) +{ + + BMLoop *curloop; + int i, len; + + len = bmesh_cycle_length(&(e->l->radial)); + for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) { + if (curloop->f == f) { + return TRUE; + } + } + return FALSE; +} + + +/* + * BME RADIAL COUNT FACE VERT + * + * Returns the number of times a vertex appears + * in a radial cycle + * + */ + +int bmesh_radial_count_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + int count = 0; + curloop = l; + do { + if (curloop->v == v) count++; + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return count; +} + +/* + * BME DISK COUNT FACE VERT + * + * Counts the number of loop users + * for this vertex. Note that this is + * equivalent to counting the number of + * faces incident upon this vertex + * + */ + +int bmesh_disk_count_facevert(BMVert *v) +{ + BMEdge *curedge; + int count = 0; + + /* is there an edge on this vert at all */ + if (!v->e) + return count; + + /* first, loop around edge */ + curedge = v->e; + do { + if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v); + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + + return count; +} + +/* + * BME RADIAL FIND FIRST FACE VERT + * + * Finds the first loop of v around radial + * cycle + * + */ +BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = l; + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return NULL; +} + +BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = bmesh_radial_nextloop(l); + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return l; +} + + +/* + * BME FIND FIRST FACE EDGE + * + * Finds the first edge in a vertices + * Disk cycle that has one of this + * vert's loops attached + * to it. + */ + +BMEdge *bmesh_disk_find_first_faceedge(BMEdge *e, BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = e; + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + + return NULL; +} + +BMEdge *bmesh_disk_find_next_faceedge(BMEdge *e, BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = bmesh_disk_nextedge(e, v); + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + return e; +} + + + + + +struct BMLoop *bmesh_loop_find_loop(struct BMFace *f, struct BMVert *v) +{ + BMLoop *l; + int i, len; + + len = bmesh_cycle_length(f->lbase); + for (i = 0, l = f->loopbase; i < len; i++, l = l->next) { + if (l->v == v) { + return l; + } + } + return NULL; +} + +#endif diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h new file mode 100644 index 00000000000..aff90dcced7 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -0,0 +1,106 @@ +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_STRUCTURE_H__ +#define __BMESH_STRUCTURE_H__ + +/** \file blender/bmesh/intern/bmesh_structure.h + * \ingroup bmesh + * + * The lowest level of functionality for manipulating bmesh structures. + * None of these functions should ever be exported to the rest of Blender. + * + * in the vast majority of cases thes should not be used directly. + * if absolutely necassary, see function defitions in code for + * descriptive comments. but seriously, don't use this stuff. + */ + +struct ListBase; + +void remove_loop_radial_link(BMLoop *l); + +/*DOUBLE CIRCULAR LINKED LIST FUNCTIONS*/ +void bmesh_cycle_append(void *h, void *nt); +int bmesh_cycle_remove(void *h, void *remn); +int bmesh_cycle_validate(int len, void *h); +int bmesh_cycle_length(void *h); + +/* LOOP CYCLE MANAGEMENT */ +int bmesh_loop_validate(BMFace *f); + +/*DISK CYCLE MANAGMENT*/ +int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v); +void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v); +struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v); +struct BMNode *bmesh_disk_getpointer(struct BMEdge *e, struct BMVert *v); +int bmesh_disk_count_facevert(struct BMVert *v); +struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v); +struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v); + +/*RADIAL CYCLE MANAGMENT*/ +void bmesh_radial_append(struct BMEdge *e, struct BMLoop *l); +void bmesh_radial_remove_loop(struct BMLoop *l, struct BMEdge *e); +int bmesh_radial_find_face(struct BMEdge *e, struct BMFace *f); +struct BMLoop *bmesh_radial_nextloop(struct BMLoop *l); +int bmesh_radial_count_facevert(struct BMLoop *l, struct BMVert *v); +struct BMLoop *bmesh_radial_find_first_facevert(struct BMLoop *l, struct BMVert *v); +struct BMLoop *bmesh_radial_find_next_facevert(struct BMLoop *l, struct BMVert *v); +int bmesh_radial_validate(int radlen, struct BMLoop *l); + +/*EDGE UTILITIES*/ +int bmesh_vert_in_edge(struct BMEdge *e, struct BMVert *v); +int bmesh_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e); +int bmesh_edge_swapverts(struct BMEdge *e, struct BMVert *orig, struct BMVert *newv); /*relink edge*/ +struct BMVert *bmesh_edge_getothervert(struct BMEdge *e, struct BMVert *v); +int bmesh_disk_hasedge(struct BMVert *v, struct BMEdge *e); +struct BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2); +struct BMEdge *bmesh_disk_next_edgeflag(struct BMEdge *e, struct BMVert *v, int eflag, int tflag); +int bmesh_disk_count_edgeflag(struct BMVert *v, int eflag, int tflag); +int bmesh_disk_validate(int len, struct BMEdge *e, struct BMVert *v); + +/*EULER API - For modifying structure*/ +struct BMVert *bmesh_mv(struct BMesh *bm, float *vec); +struct BMEdge *bmesh_me(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2); +struct BMFace *bmesh_mf(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMEdge **elist, int len); +int bmesh_kv(struct BMesh *bm, struct BMVert *v); +int bmesh_ke(struct BMesh *bm, struct BMEdge *e); +int bmesh_kf(struct BMesh *bm, struct BMFace *bply); +struct BMVert *bmesh_semv(struct BMesh *bm, struct BMVert *tv, struct BMEdge *e, struct BMEdge **re); +struct BMFace *bmesh_sfme(struct BMesh *bm, struct BMFace *f, struct BMVert *v1, + struct BMVert *v2, struct BMLoop **rl +#ifdef USE_BMESH_HOLES + , ListBase *holes +#endif + ); +int bmesh_jekv(struct BMesh *bm, struct BMEdge *ke, struct BMVert *kv); +int bmesh_loop_reverse(struct BMesh *bm, struct BMFace *f); +struct BMFace *bmesh_jfke(struct BMesh *bm, struct BMFace *f1, struct BMFace *f2, struct BMEdge *e); + +struct BMVert *bmesh_urmv(struct BMesh *bm, struct BMFace *sf, struct BMVert *sv); +//int *bmesh_grkv(struct BMesh *bm, struct BMFace *sf, struct BMVert *kv); + +#endif /* __BMESH_STRUCTURE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c new file mode 100644 index 00000000000..9cba90c1bf5 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers.c @@ -0,0 +1,266 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_walkers.c + * \ingroup bmesh + * + * BMesh Walker API. + */ + +#include <stdlib.h> + + + +#include "BLI_listbase.h" + +#include "bmesh.h" + +#include "bmesh_walkers_private.h" + +/* - joeedh - + * design notes: + * + * original desing: walkers directly emulation recursive functions. + * functions save their state onto a worklist, and also add new states + * to implement recursive or looping behaviour. generally only one + * state push per call with a specific state is desired. + * + * basic design pattern: the walker step function goes through it's + * list of possible choices for recursion, and recurses (by pushing a new state) + * using the first non-visited one. this choise is the flagged as visited using + * the ghash. each step may push multiple new states onto the worklist at once. + * + * - walkers use tool flags, not header flags + * - walkers now use ghash for storing visited elements, + * rather then stealing flags. ghash can be rewritten + * to be faster if necassary, in the far future :) . + * - tools should ALWAYS have necassary error handling + * for if walkers fail. + */ + +void *BMW_begin(BMWalker *walker, void *start) +{ + walker->begin(walker, start); + + return BMW_current_state(walker) ? walker->step(walker) : NULL; +} + +/* + * BMW_CREATE + * + * Allocates and returns a new mesh walker of + * a given type. The elements visited are filtered + * by the bitmask 'searchmask'. + */ + +void BMW_init(BMWalker *walker, BMesh *bm, int type, + short mask_vert, short mask_edge, short mask_loop, short mask_face, + int layer) +{ + memset(walker, 0, sizeof(BMWalker)); + + walker->layer = layer; + walker->bm = bm; + + walker->mask_vert = mask_vert; + walker->mask_edge = mask_edge; + walker->mask_loop = mask_loop; + walker->mask_face = mask_face; + + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 1"); + + if (type >= BMW_MAXWALKERS || type < 0) { + bmesh_error(); + fprintf(stderr, + "Invalid walker type in BMW_init; type: %d, " + "searchmask: (v:%d, e:%d, l:%d, f:%d), flag: %d\n", + type, mask_vert, mask_edge, mask_loop, mask_face, layer); + } + + if (type != BMW_CUSTOM) { + walker->begin = bm_walker_types[type]->begin; + walker->yield = bm_walker_types[type]->yield; + walker->step = bm_walker_types[type]->step; + walker->structsize = bm_walker_types[type]->structsize; + walker->order = bm_walker_types[type]->order; + walker->valid_mask = bm_walker_types[type]->valid_mask; + + /* safety checks */ + /* if this raises an error either the caller is wrong or + * 'bm_walker_types' needs updating */ + BLI_assert(mask_vert == 0 || (walker->valid_mask & BM_VERT)); + BLI_assert(mask_edge == 0 || (walker->valid_mask & BM_EDGE)); + BLI_assert(mask_loop == 0 || (walker->valid_mask & BM_LOOP)); + BLI_assert(mask_face == 0 || (walker->valid_mask & BM_FACE)); + } + + walker->worklist = BLI_mempool_create(walker->structsize, 100, 100, TRUE, FALSE); + walker->states.first = walker->states.last = NULL; +} + +/* + * BMW_end + * + * Frees a walker's worklist. + */ + +void BMW_end(BMWalker *walker) +{ + BLI_mempool_destroy(walker->worklist); + BLI_ghash_free(walker->visithash, NULL, NULL); +} + + +/* + * BMW_step + */ + +void *BMW_step(BMWalker *walker) +{ + BMHeader *head; + + head = BMW_walk(walker); + + return head; +} + +/* + * BMW_current_depth + * + * Returns the current depth of the walker. + */ + +int BMW_current_depth(BMWalker *walker) +{ + return walker->depth; +} + +/* + * BMW_WALK + * + * Steps a mesh walker forward by one element + * + * BMESH_TODO: + * -add searchmask filtering + */ + +void *BMW_walk(BMWalker *walker) +{ + void *current = NULL; + + while (BMW_current_state(walker)) { + current = walker->step(walker); + if (current) { + return current; + } + } + return NULL; +} + +/* + * BMW_current_state + * + * Returns the first state from the walker state + * worklist. This state is the the next in the + * worklist for processing. + */ + +void *BMW_current_state(BMWalker *walker) +{ + bmesh_walkerGeneric *currentstate = walker->states.first; + if (currentstate) { + /* Automatic update of depth. For most walkers that + * follow the standard "Step" pattern of: + * - read current state + * - remove current state + * - push new states + * - return walk result from just-removed current state + * this simple automatic update should keep track of depth + * just fine. Walkers that deviate from that pattern may + * need to manually update the depth if they care about + * keeping it correct. */ + walker->depth = currentstate->depth + 1; + } + return currentstate; +} + +/* + * BMW_state_remove + * + * Remove and free an item from the end of the walker state + * worklist. + */ + +void BMW_state_remove(BMWalker *walker) +{ + void *oldstate; + oldstate = BMW_current_state(walker); + BLI_remlink(&walker->states, oldstate); + BLI_mempool_free(walker->worklist, oldstate); +} + +/* + * BMW_newstate + * + * Allocate a new empty state and put it on the worklist. + * A pointer to the new state is returned so that the caller + * can fill in the state data. The new state will be inserted + * at the front for depth-first walks, and at the end for + * breadth-first walks. + */ + +void *BMW_state_add(BMWalker *walker) +{ + bmesh_walkerGeneric *newstate; + newstate = BLI_mempool_alloc(walker->worklist); + newstate->depth = walker->depth; + switch (walker->order) + { + case BMW_DEPTH_FIRST: + BLI_addhead(&walker->states, newstate); + break; + case BMW_BREADTH_FIRST: + BLI_addtail(&walker->states, newstate); + break; + default: + BLI_assert(0); + break; + } + return newstate; +} + +/* + * BMW_reset + * + * Frees all states from the worklist, resetting the walker + * for reuse in a new walk. + */ + +void BMW_reset(BMWalker *walker) +{ + while (BMW_current_state(walker)) { + BMW_state_remove(walker); + } + walker->depth = 0; + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 1"); +} diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c new file mode 100644 index 00000000000..e6bcbf405fe --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -0,0 +1,911 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_walkers_impl.c + * \ingroup bmesh + * + * BMesh Walker Code. + */ + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" +#include "bmesh_walkers_private.h" + +/* Shell Walker: + * + * Starts at a vertex on the mesh and walks over the 'shell' it belongs + * to via visiting connected edges. + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * + */ + +static void shellWalker_visitEdge(BMWalker *walker, BMEdge *e) +{ + shellWalker *shellWalk = NULL; + + if (BLI_ghash_haskey(walker->visithash, e)) { + return; + } + + if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, e, walker->mask_edge)) { + return; + } + + shellWalk = BMW_state_add(walker); + shellWalk->curedge = e; + BLI_ghash_insert(walker->visithash, e, NULL); +} + +static void shellWalker_begin(BMWalker *walker, void *data) +{ + BMIter eiter; + BMHeader *h = data; + BMEdge *e; + BMVert *v; + + if (h == NULL) + { + return; + } + + switch (h->htype) { + case BM_VERT: + { + /* starting the walk at a vert, add all the edges + * to the worklist */ + v = (BMVert *)h; + BM_ITER(e, &eiter, walker->bm, BM_EDGES_OF_VERT, v) { + shellWalker_visitEdge(walker, e); + } + break; + } + + case BM_EDGE: + { + /* starting the walk at an edge, add the single edge + * to the worklist */ + e = (BMEdge *)h; + shellWalker_visitEdge(walker, e); + break; + } + } +} + +static void *shellWalker_yield(BMWalker *walker) +{ + shellWalker *shellWalk = BMW_current_state(walker); + return shellWalk->curedge; +} + +static void *shellWalker_step(BMWalker *walker) +{ + shellWalker *swalk = BMW_current_state(walker); + BMEdge *e, *e2; + BMVert *v; + BMIter iter; + int i; + + e = swalk->curedge; + BMW_state_remove(walker); + + for (i = 0; i < 2; i++) { + v = i ? e->v2 : e->v1; + BM_ITER(e2, &iter, walker->bm, BM_EDGES_OF_VERT, v) { + shellWalker_visitEdge(walker, e2); + } + } + + return e; +} + +#if 0 +static void *shellWalker_step(BMWalker *walker) +{ + BMEdge *curedge, *next = NULL; + BMVert *ov = NULL; + int restrictpass = 1; + shellWalker shellWalk = *((shellWalker *)BMW_current_state(walker)); + + if (!BLI_ghash_haskey(walker->visithash, shellWalk.base)) { + BLI_ghash_insert(walker->visithash, shellWalk.base, NULL); + } + + BMW_state_remove(walker); + + + /* find the next edge whose other vertex has not been visite */ + curedge = shellWalk.curedge; + do { + if (!BLI_ghash_haskey(walker->visithash, curedge)) { + if (!walker->restrictflag || + (walker->restrictflag && BMO_elem_flag_test(walker->bm, curedge, walker->restrictflag))) + { + shellWalker *newstate; + + ov = BM_edge_other_vert(curedge, shellWalk.base); + + /* push a new state onto the stac */ + newState = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, curedge, NULL); + + /* populate the new stat */ + + newState->base = ov; + newState->curedge = curedge; + } + } + curedge = bmesh_disk_nextedge(curedge, shellWalk.base); + } while (curedge != shellWalk.curedge); + + return shellWalk.curedge; +} +#endif + +/* Connected Vertex Walker: + * + * Similar to shell walker, but visits vertices instead of edges. + * + */ + +static void connectedVertexWalker_visitVertex(BMWalker *walker, BMVert *v) +{ + connectedVertexWalker *vwalk; + + if (BLI_ghash_haskey(walker->visithash, v)) { + /* already visited */ + return; + } + if (walker->mask_vert && !BMO_elem_flag_test(walker->bm, v, walker->mask_vert)) { + /* not flagged for walk */ + return; + } + + vwalk = BMW_state_add(walker); + vwalk->curvert = v; + BLI_ghash_insert(walker->visithash, v, NULL); +} + +static void connectedVertexWalker_begin(BMWalker *walker, void *data) +{ + BMVert *v = data; + connectedVertexWalker_visitVertex(walker, v); +} + +static void *connectedVertexWalker_yield(BMWalker *walker) +{ + connectedVertexWalker *vwalk = BMW_current_state(walker); + return vwalk->curvert; +} + +static void *connectedVertexWalker_step(BMWalker *walker) +{ + connectedVertexWalker *vwalk = BMW_current_state(walker); + BMVert *v, *v2; + BMEdge *e; + BMIter iter; + + v = vwalk->curvert; + + BMW_state_remove(walker); + + BM_ITER(e, &iter, walker->bm, BM_EDGES_OF_VERT, v) { + v2 = BM_edge_other_vert(e, v); + if (!BLI_ghash_haskey(walker->visithash, v2)) { + connectedVertexWalker_visitVertex(walker, v2); + } + } + + return v; +} + +/* Island Boundary Walker: + * + * Starts at a edge on the mesh and walks over the boundary of an + * island it belongs to. + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * + */ + +static void islandboundWalker_begin(BMWalker *walker, void *data) +{ + BMLoop *l = data; + islandboundWalker *iwalk = NULL; + + iwalk = BMW_state_add(walker); + + iwalk->base = iwalk->curloop = l; + iwalk->lastv = l->v; + + BLI_ghash_insert(walker->visithash, data, NULL); + +} + +static void *islandboundWalker_yield(BMWalker *walker) +{ + islandboundWalker *iwalk = BMW_current_state(walker); + + return iwalk->curloop; +} + +static void *islandboundWalker_step(BMWalker *walker) +{ + islandboundWalker *iwalk = BMW_current_state(walker), owalk; + BMVert *v; + BMEdge *e = iwalk->curloop->e; + BMFace *f; + BMLoop *l = iwalk->curloop; + /* int found = 0; */ + + owalk = *iwalk; + + if (iwalk->lastv == e->v1) v = e->v2; + else v = e->v1; + + if (!BM_vert_is_manifold(walker->bm, v)) { + BMW_reset(walker); + BMO_error_raise(walker->bm, NULL, BMERR_WALKER_FAILED, + "Non-manifold vert " + "while searching region boundary"); + return NULL; + } + + /* pop off current stat */ + BMW_state_remove(walker); + + f = l->f; + + while (1) { + l = BM_face_other_loop(e, f, v); + if (bmesh_radial_nextloop(l) != l) { + l = bmesh_radial_nextloop(l); + f = l->f; + e = l->e; + if (walker->mask_face && !BMO_elem_flag_test(walker->bm, f, walker->mask_face)) { + l = l->radial_next; + break; + } + } + else { + f = l->f; + e = l->e; + break; + } + } + + if (l == owalk.curloop) { + return NULL; + } + else if (BLI_ghash_haskey(walker->visithash, l)) { + return owalk.curloop; + } + + BLI_ghash_insert(walker->visithash, l, NULL); + iwalk = BMW_state_add(walker); + iwalk->base = owalk.base; + + //if (!BMO_elem_flag_test(walker->bm, l->f, walker->restrictflag)) + // iwalk->curloop = l->radial_next; + iwalk->curloop = l; //else iwalk->curloop = l; + iwalk->lastv = v; + + return owalk.curloop; +} + + +/* Island Walker: + * + * Starts at a tool flagged-face and walks over the face region + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * + */ + +static void islandWalker_begin(BMWalker *walker, void *data) +{ + islandWalker *iwalk = NULL; + + if (walker->mask_face && !BMO_elem_flag_test(walker->bm, (BMElemF *)data, walker->mask_face)) { + return; + } + + iwalk = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, data, NULL); + + iwalk->cur = data; +} + +static void *islandWalker_yield(BMWalker *walker) +{ + islandWalker *iwalk = BMW_current_state(walker); + + return iwalk->cur; +} + +static void *islandWalker_step(BMWalker *walker) +{ + islandWalker *iwalk = BMW_current_state(walker); + /* islandWalker *owalk = iwalk; */ /* UNUSED */ + BMIter iter, liter; + BMFace *f, *curf = iwalk->cur; + BMLoop *l; + + BMW_state_remove(walker); + + l = BM_iter_new(&liter, walker->bm, BM_LOOPS_OF_FACE, iwalk->cur); + for ( ; l; l = BM_iter_step(&liter)) { + /* could skip loop here too, but dont add unless we need it */ + if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, l->e, walker->mask_edge)) { + continue; + } + + f = BM_iter_new(&iter, walker->bm, BM_FACES_OF_EDGE, l->e); + for ( ; f; f = BM_iter_step(&iter)) { + if (walker->mask_face && !BMO_elem_flag_test(walker->bm, f, walker->mask_face)) { + continue; + } + if (BLI_ghash_haskey(walker->visithash, f)) continue; + + iwalk = BMW_state_add(walker); + iwalk->cur = f; + BLI_ghash_insert(walker->visithash, f, NULL); + break; + } + } + + return curf; +} + + +/* Edge Loop Walker: + * + * Starts at a tool-flagged edge and walks over the edge loop + * + */ + +static void loopWalker_begin(BMWalker *walker, void *data) +{ + loopWalker *lwalk = NULL, owalk; + BMEdge *e = data; + BMVert *v; + /* int found = 1, val; */ /* UNUSED */ + + v = e->v1; + + /* val = BM_vert_edge_count(v); */ /* UNUSED */ + + lwalk = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, e, NULL); + + lwalk->cur = lwalk->start = e; + lwalk->lastv = lwalk->startv = v; + lwalk->stage2 = 0; + lwalk->startrad = BM_edge_face_count(e); + + /* rewin */ + while (BMW_current_state(walker)) { + owalk = *((loopWalker *)BMW_current_state(walker)); + BMW_walk(walker); + } + + lwalk = BMW_state_add(walker); + *lwalk = owalk; + + if (lwalk->lastv == owalk.cur->v1) lwalk->lastv = owalk.cur->v2; + else lwalk->lastv = owalk.cur->v1; + + lwalk->startv = lwalk->lastv; + + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 2"); + BLI_ghash_insert(walker->visithash, owalk.cur, NULL); +} + +static void *loopWalker_yield(BMWalker *walker) +{ + loopWalker *lwalk = BMW_current_state(walker); + + return lwalk->cur; +} + +static void *loopWalker_step(BMWalker *walker) +{ + loopWalker *lwalk = BMW_current_state(walker), owalk; + BMIter eiter; + BMEdge *e = lwalk->cur, *nexte = NULL; + BMLoop *l, *l2; + BMVert *v; + int val, rlen /* , found = 0 */, i = 0, stopi; + + owalk = *lwalk; + BMW_state_remove(walker); + + l = e->l; + + /* handle wire edge case */ + if (!l) { + + /* match trunk: mark all connected wire edges */ + for (i = 0; i < 2; i++) { + v = i ? e->v2 : e->v1; + + BM_ITER(nexte, &eiter, walker->bm, BM_EDGES_OF_VERT, v) { + if ((nexte->l == NULL) && !BLI_ghash_haskey(walker->visithash, nexte)) { + lwalk = BMW_state_add(walker); + lwalk->cur = nexte; + lwalk->lastv = v; + lwalk->startrad = owalk.startrad; + + BLI_ghash_insert(walker->visithash, nexte, NULL); + } + } + } + + return owalk.cur; + } + + v = (e->v1 == lwalk->lastv) ? e->v2 : e->v1; + + val = BM_vert_edge_count(v); + + rlen = owalk.startrad; + + if (val == 4 || val == 2 || rlen == 1) { + i = 0; + stopi = val / 2; + while (1) { + if (rlen != 1 && i == stopi) break; + + l = BM_face_other_loop(l->e, l->f, v); + + if (!l) + break; + + l2 = bmesh_radial_nextloop(l); + + if (l2 == l) { + break; + } + + l = l2; + i += 1; + } + } + + if (!l) { + return owalk.cur; + } + + if (l != e->l && !BLI_ghash_haskey(walker->visithash, l->e)) { + if (!(rlen != 1 && i != stopi)) { + lwalk = BMW_state_add(walker); + lwalk->cur = l->e; + lwalk->lastv = v; + lwalk->startrad = owalk.startrad; + BLI_ghash_insert(walker->visithash, l->e, NULL); + } + } + + return owalk.cur; +} + +/* Face Loop Walker: + * + * Starts at a tool-flagged face and walks over the face loop + * Conditions for starting and stepping the face loop have been + * tuned in an attempt to match the face loops built by EditMesh + * + */ + +/* Check whether the face loop should includes the face specified + * by the given BMLoop */ +static int faceloopWalker_include_face(BMWalker *walker, BMLoop *l) +{ + /* face must have degree 4 */ + if (l->f->len != 4) { + return FALSE; + } + + /* the face must not have been already visite */ + if (BLI_ghash_haskey(walker->visithash, l->f)) { + return FALSE; + } + + return TRUE; +} + +/* Check whether the face loop can start from the given edge */ +static int faceloopWalker_edge_begins_loop(BMWalker *walker, BMEdge *e) +{ + BMesh *bm = walker->bm; + + /* There is no face loop starting from a wire edge */ + if (BM_edge_is_wire(bm, e)) { + return FALSE; + } + + /* Don't start a loop from a boundary edge if it cannot + * be extended to cover any faces */ + if (BM_edge_face_count(e) == 1) { + if (!faceloopWalker_include_face(walker, e->l)) { + return FALSE; + } + } + + /* Don't start a face loop from non-manifold edges */ + if (!BM_edge_is_manifold(bm, e)) { + return FALSE; + } + + return TRUE; +} + +static void faceloopWalker_begin(BMWalker *walker, void *data) +{ + faceloopWalker *lwalk, owalk; + BMEdge *e = data; + /* BMesh *bm = walker->bm; */ /* UNUSED */ + /* int fcount = BM_edge_face_count(e); */ /* UNUSED */ + + if (!faceloopWalker_edge_begins_loop(walker, e)) + return; + + lwalk = BMW_state_add(walker); + lwalk->l = e->l; + lwalk->nocalc = 0; + BLI_ghash_insert(walker->visithash, lwalk->l->f, NULL); + + /* rewin */ + while (BMW_current_state(walker)) { + owalk = *((faceloopWalker *)BMW_current_state(walker)); + BMW_walk(walker); + } + + lwalk = BMW_state_add(walker); + *lwalk = owalk; + lwalk->nocalc = 0; + + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 3"); + BLI_ghash_insert(walker->visithash, lwalk->l->f, NULL); +} + +static void *faceloopWalker_yield(BMWalker *walker) +{ + faceloopWalker *lwalk = BMW_current_state(walker); + + if (!lwalk) { + return NULL; + } + + return lwalk->l->f; +} + +static void *faceloopWalker_step(BMWalker *walker) +{ + faceloopWalker *lwalk = BMW_current_state(walker); + BMFace *f = lwalk->l->f; + BMLoop *l = lwalk->l, *origl = lwalk->l; + + BMW_state_remove(walker); + + l = l->radial_next; + + if (lwalk->nocalc) + return f; + + if (!faceloopWalker_include_face(walker, l)) { + l = lwalk->l; + l = l->next->next; + if (BM_edge_face_count(l->e) != 2) { + l = l->prev->prev; + } + l = l->radial_next; + } + + if (faceloopWalker_include_face(walker, l)) { + lwalk = BMW_state_add(walker); + lwalk->l = l; + + if (l->f->len != 4) { + lwalk->nocalc = 1; + lwalk->l = origl; + } + else { + lwalk->nocalc = 0; + } + + BLI_ghash_insert(walker->visithash, l->f, NULL); + } + + return f; +} + +/* Edge Ring Walker: + * + * Starts at a tool-flagged edge and walks over the edge ring + * Conditions for starting and stepping the edge ring have been + * tuned in an attempt to match the edge rings built by EditMesh + * + */ + +static void edgeringWalker_begin(BMWalker *walker, void *data) +{ + edgeringWalker *lwalk, owalk; + BMEdge *e = data; + + lwalk = BMW_state_add(walker); + lwalk->l = e->l; + + if (!lwalk->l) { + lwalk->wireedge = e; + return; + } + else { + lwalk->wireedge = NULL; + } + + BLI_ghash_insert(walker->visithash, lwalk->l->e, NULL); + + /* rewin */ + while (BMW_current_state(walker)) { + owalk = *((edgeringWalker *)BMW_current_state(walker)); + BMW_walk(walker); + } + + lwalk = BMW_state_add(walker); + *lwalk = owalk; + + if (lwalk->l->f->len != 4) + lwalk->l = lwalk->l->radial_next; + + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 4"); + BLI_ghash_insert(walker->visithash, lwalk->l->e, NULL); +} + +static void *edgeringWalker_yield(BMWalker *walker) +{ + edgeringWalker *lwalk = BMW_current_state(walker); + + if (!lwalk) { + return NULL; + } + + if (lwalk->l) + return lwalk->l->e; + else + return lwalk->wireedge; +} + +static void *edgeringWalker_step(BMWalker *walker) +{ + edgeringWalker *lwalk = BMW_current_state(walker); + BMEdge *e; + BMLoop *l = lwalk->l /* , *origl = lwalk->l */; + BMesh *bm = walker->bm; + + BMW_state_remove(walker); + + if (!l) + return lwalk->wireedge; + + e = l->e; + if (!BM_edge_is_manifold(bm, e)) { + /* walker won't traverse to a non-manifold edge, but may + * be started on one, and should not traverse *away* from + * a non-manfold edge (non-manifold edges are never in an + * edge ring with manifold edges */ + return e; + } + + l = l->radial_next; + l = l->next->next; + + if ((l->f->len != 4) || !BM_edge_is_manifold(bm, l->e)) { + l = lwalk->l->next->next; + } + + /* only walk to manifold edge */ + if ((l->f->len == 4) && BM_edge_is_manifold(bm, l->e) && + !BLI_ghash_haskey(walker->visithash, l->e)) { + lwalk = BMW_state_add(walker); + lwalk->l = l; + lwalk->wireedge = NULL; + + BLI_ghash_insert(walker->visithash, l->e, NULL); + } + + return e; +} + +static void uvedgeWalker_begin(BMWalker *walker, void *data) +{ + uvedgeWalker *lwalk; + BMLoop *l = data; + + if (BLI_ghash_haskey(walker->visithash, l)) + return; + + lwalk = BMW_state_add(walker); + lwalk->l = l; + BLI_ghash_insert(walker->visithash, l, NULL); +} + +static void *uvedgeWalker_yield(BMWalker *walker) +{ + uvedgeWalker *lwalk = BMW_current_state(walker); + + if (!lwalk) { + return NULL; + } + + return lwalk->l; +} + +static void *uvedgeWalker_step(BMWalker *walker) +{ + uvedgeWalker *lwalk = BMW_current_state(walker); + BMLoop *l, *l2, *l3, *nl, *cl; + BMIter liter; + void *d1, *d2; + int i, j, rlen, type; + + l = lwalk->l; + nl = l->next; + type = walker->bm->ldata.layers[walker->layer].type; + + BMW_state_remove(walker); + + if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, l->e, walker->mask_edge)) + return l; + + /* go over loops around l->v and nl->v and see which ones share l and nl's + * mloopuv's coordinates. in addition, push on l->next if necassary */ + for (i = 0; i < 2; i++) { + cl = i ? nl : l; + BM_ITER(l2, &liter, walker->bm, BM_LOOPS_OF_VERT, cl->v) { + d1 = CustomData_bmesh_get_layer_n(&walker->bm->ldata, + cl->head.data, walker->layer); + + rlen = BM_edge_face_count(l2->e); + for (j = 0; j < rlen; j++) { + if (BLI_ghash_haskey(walker->visithash, l2)) + continue; + if (walker->mask_edge && !(BMO_elem_flag_test(walker->bm, l2->e, walker->mask_edge))) { + if (l2->v != cl->v) + continue; + } + + l3 = l2->v != cl->v ? l2->next : l2; + d2 = CustomData_bmesh_get_layer_n(&walker->bm->ldata, + l3->head.data, walker->layer); + + if (!CustomData_data_equals(type, d1, d2)) + continue; + + lwalk = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, l2, NULL); + + lwalk->l = l2; + + l2 = l2->radial_next; + } + } + } + + return l; +} + +static BMWalker shell_walker_type = { + shellWalker_begin, + shellWalker_step, + shellWalker_yield, + sizeof(shellWalker), + BMW_BREADTH_FIRST, + BM_EDGE, /* valid restrict masks */ +}; + +static BMWalker islandbound_walker_type = { + islandboundWalker_begin, + islandboundWalker_step, + islandboundWalker_yield, + sizeof(islandboundWalker), + BMW_DEPTH_FIRST, + BM_FACE, /* valid restrict masks */ +}; + +static BMWalker island_walker_type = { + islandWalker_begin, + islandWalker_step, + islandWalker_yield, + sizeof(islandWalker), + BMW_BREADTH_FIRST, + BM_EDGE | BM_FACE, /* valid restrict masks */ +}; + +static BMWalker loop_walker_type = { + loopWalker_begin, + loopWalker_step, + loopWalker_yield, + sizeof(loopWalker), + BMW_DEPTH_FIRST, + 0, /* valid restrict masks */ /* could add flags here but so far none are used */ +}; + +static BMWalker faceloop_walker_type = { + faceloopWalker_begin, + faceloopWalker_step, + faceloopWalker_yield, + sizeof(faceloopWalker), + BMW_DEPTH_FIRST, + 0, /* valid restrict masks */ /* could add flags here but so far none are used */ +}; + +static BMWalker edgering_walker_type = { + edgeringWalker_begin, + edgeringWalker_step, + edgeringWalker_yield, + sizeof(edgeringWalker), + BMW_DEPTH_FIRST, + 0, /* valid restrict masks */ /* could add flags here but so far none are used */ +}; + +static BMWalker loopdata_region_walker_type = { + uvedgeWalker_begin, + uvedgeWalker_step, + uvedgeWalker_yield, + sizeof(uvedgeWalker), + BMW_DEPTH_FIRST, + BM_EDGE, /* valid restrict masks */ +}; + +static BMWalker connected_vertex_walker_type = { + connectedVertexWalker_begin, + connectedVertexWalker_step, + connectedVertexWalker_yield, + sizeof(connectedVertexWalker), + BMW_BREADTH_FIRST, + BM_VERT, /* valid restrict masks */ +}; + +BMWalker *bm_walker_types[] = { + &shell_walker_type, /* BMW_SHELL */ + &loop_walker_type, /* BMW_LOOP */ + &faceloop_walker_type, /* BMW_FACELOOP */ + &edgering_walker_type, /* BMW_EDGERING */ + &loopdata_region_walker_type, /* BMW_LOOPDATA_ISLAND */ + &islandbound_walker_type, /* BMW_ISLANDBOUND */ + &island_walker_type, /* BMW_ISLAND */ + &connected_vertex_walker_type, /* BMW_CONNECTED_VERTEX */ +}; + +int bm_totwalkers = sizeof(bm_walker_types) / sizeof(*bm_walker_types); diff --git a/source/blender/bmesh/intern/bmesh_walkers_private.h b/source/blender/bmesh/intern/bmesh_walkers_private.h new file mode 100644 index 00000000000..2d59a940448 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers_private.h @@ -0,0 +1,89 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar, Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_WALKERS_PRIVATE_H__ +#define __BMESH_WALKERS_PRIVATE_H__ + +/** \file blender/bmesh/intern/bmesh_walkers_private.h + * \ingroup bmesh + * + * BMesh walker API. + */ + +extern BMWalker *bm_walker_types[]; +extern int bm_totwalkers; + + +/* Pointer hiding*/ +typedef struct bmesh_walkerGeneric{ + Link link; + int depth; +} bmesh_walkerGeneric; + + +typedef struct shellWalker{ + bmesh_walkerGeneric header; + BMEdge *curedge; +} shellWalker; + +typedef struct islandboundWalker { + bmesh_walkerGeneric header; + BMLoop *base; + BMVert *lastv; + BMLoop *curloop; +} islandboundWalker; + +typedef struct islandWalker { + bmesh_walkerGeneric header; + BMFace *cur; +} islandWalker; + +typedef struct loopWalker { + bmesh_walkerGeneric header; + BMEdge *cur, *start; + BMVert *lastv, *startv; + int startrad, stage2; +} loopWalker; + +typedef struct faceloopWalker { + bmesh_walkerGeneric header; + BMLoop *l; + int nocalc; +} faceloopWalker; + +typedef struct edgeringWalker { + bmesh_walkerGeneric header; + BMLoop *l; + BMEdge *wireedge; +} edgeringWalker; + +typedef struct uvedgeWalker { + bmesh_walkerGeneric header; + BMLoop *l; +} uvedgeWalker; + +typedef struct connectedVertexWalker { + bmesh_walkerGeneric header; + BMVert *curvert; +} connectedVertexWalker; + +#endif /* __BMESH_WALKERS_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/in-progress/BME_conversions.c b/source/blender/bmesh/intern/in-progress/BME_conversions.c new file mode 100644 index 00000000000..279d8f7fd0e --- /dev/null +++ b/source/blender/bmesh/intern/in-progress/BME_conversions.c @@ -0,0 +1,479 @@ +#if 0 + +/* + * ***** 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) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_utildefines.h" +#include "BKE_mesh.h" +#include "bmesh.h" +#include "BKE_global.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" + +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_edgehash.h" +#include "bmesh_private.h" + + + +/* + * BMESH DERIVED MESH CONVERSION FUNCTIONS + * + * The functions in this file provides + * methods for converting to and from + * a bmesh. + * +*/ + + +/* + * DMCORNERS TO LOOPS + * + * Function to convert derived mesh per-face + * corner data (uvs, vertex colors), to n-gon + * per-loop data. + * +*/ + +static void DMcorners_to_loops(BMMesh *bm, CustomData *facedata, int index, BMFace *f, int numCol, int numTex) +{ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i< numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texpoly->tpage = texface[index].tpage; + texpoly->flag = texface[index].flag; + texpoly->transp = texface[index].transp; + texpoly->mode = texface[index].mode; + texpoly->tile = texface[index].tile; + texpoly->unwrap = texface[index].unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + mloopuv->uv[0] = texface[index].uv[j][0]; + mloopuv->uv[1] = texface[index].uv[j][1]; + j++; + l = l->next; + }while(l!=f->loopbase); + } + + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata, CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mloopcol->r = mcol[(index*4)+j].r; + mloopcol->g = mcol[(index*4)+j].g; + mloopcol->b = mcol[(index*4)+j].b; + mloopcol->a = mcol[(index*4)+j].a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +/* + * LOOPS TO DMCORNERS + * + * Function to convert n-gon per-loop data + * (uvs, vertex colors, ect)to derived mesh + * face corner data. + * +*/ + +static void loops_to_DMcorners(BMMesh *bm, CustomData *facedata, int index, BMFace *f,int numCol, int numTex) +{ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texface[index].tpage = texpoly->tpage; + texface[index].flag = texpoly->flag; + texface[index].transp = texpoly->transp; + texface[index].mode = texpoly->mode; + texface[index].tile = texpoly->tile; + texface[index].unwrap = texpoly->unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + texface[index].uv[j][0] = mloopuv->uv[0]; + texface[index].uv[j][1] = mloopuv->uv[1]; + j++; + l = l->next; + }while(l!=f->loopbase); + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata,CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mcol[(index*4) + j].r = mloopcol->r; + mcol[(index*4) + j].g = mloopcol->g; + mcol[(index*4) + j].b = mloopcol->b; + mcol[(index*4) + j].a = mloopcol->a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +/* + * MVERT TO BMESHVERT + * + * Converts a MVert to a BMVert + * +*/ +static BMVert *mvert_to_bmeshvert(BMMesh *bm, BMVert **vert_array, int index, MVert *mv, CustomData *data) +{ + BMVert *v = NULL; + + v = bmesh_make_vert(bm, mv->co, NULL); + vert_array[index] = v; + if(mv->flag & SELECT) bmesh_set_flag(v, BMESH_SELECT); + v->bweight = mv->bweight/255.0f; + CustomData_to_bmesh_block(data, &bm->vdata, index, &v->data); + + return v; +} + +/* + * MEDGE TO BMESHEDGE + * + * Converts a MEdge to a BMEdge + * +*/ + +static BMEdge *medge_to_bmeshedge(BMMesh *bm, BMVert **vert_array, int index, MEdge *me, CustomData *data, Edge_Hash *edge_hash) +{ + BMVert *v1, *v2; + BMEdge *e = NULL; + + v1 = vert_array[me->v1]; + v2 = vert_array[me->v2]; + e = bmesh_make_edge(bm, v1, v2, NULL, 0); + e->crease = me->crease/255.0f; + e->bweight = me->bweight/255.0f; + if(me->flag & 1) bmesh_set_flag(e, BMESH_SELECT); + if(me->flag & ME_SEAM) bmesh_set_flag(e, BMESH_SEAM); + BLI_edgehash_insert(edge_hash,me->v1,me->v2,e); + CustomData_to_bmesh_block(data, &bm->edata, index, &e->data); + + return e; +} + +/* + * MFACE TO BMESHFACE + * + * Converts a MFace to a BMFace. + * Note that this will fail on eekadoodle + * faces. + * +*/ + +static BMFace *mface_to_bmeshface(BMMesh *bm, BMVert **vert_array, int index, MFace *mf, CustomData *data, Edge_Hash *edge_hash) +{ + BMVert *v1, *v2; + BMEdge *edar[4]; + BMFace *f = NULL; + int len; + + if(mf->v4) len = 4; + else len = 3; + + edar[0] = BLI_edgehash_lookup(edge_hash,mf->v1,mf->v2); + edar[1] = BLI_edgehash_lookup(edge_hash,mf->v2,mf->v3); + if(len == 4){ + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v4); + edar[3] = BLI_edgehash_lookup(edge_hash,mf->v4,mf->v1); + } + else + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v1); + + /*find v1 and v2*/ + v1 = vert_array[mf->v1]; + v2 = vert_array[mf->v2]; + + f = bmesh_make_ngon(bm, v1, v2, edar, len, 0); + f->mat_nr = mf->mat_nr; + if(mf->flag & 1) bmesh_set_flag(f, BMESH_SELECT); + if(mf->flag & ME_HIDE) bmesh_set_flag(f, BMESH_HIDDEN); + CustomData_to_bmesh_block(data, &bm->pdata, index, &f->data); + + return f; +} + +/* + * DERIVEDMESH TO BMESH + * + * Converts a derived mesh to a bmesh. + * +*/ + +BMMesh *derivedmesh_to_bmesh(DerivedMesh *dm) +{ + + BMMesh *bm; + BMVert **vert_array; + BMFace *f=NULL; + + MVert *mvert, *mv; + MEdge *medge, *me; + MFace *mface, *mf; + + int totface,totedge,totvert,i,len, numTex, numCol; + int allocsize[4] = {512,512,2048,512}; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + /*allocate a new bmesh*/ + bm = bmesh_make_mesh(allocsize); + + /*copy custom data layout*/ + CustomData_copy(&dm->vertData, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->edgeData, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->faceData, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*copy face corner data*/ + CustomData_to_bmeshpoly(&dm->faceData, &bm->pdata, &bm->ldata); + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); + CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); + + /*needed later*/ + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + totvert = dm->getNumVerts(dm); + totedge = dm->getNumEdges(dm); + totface = dm->getNumTessFaces(dm); + mvert = dm->getVertArray(dm); + medge = dm->getEdgeArray(dm); + mface = dm->getTessFaceArray(dm); + + vert_array = MEM_mallocN(sizeof(BMVert *)* totvert,"derivedmesh to bmesh vertex pointer array"); + + bmesh_begin_edit(bm); + /*add verts*/ + for(i=0, mv = mvert; i < totvert; i++, mv++) + mvert_to_bmeshvert(bm, vert_array, i, mv, &dm->vertData); + + /*add edges*/ + for(i=0, me = medge; i < totedge; i++, me++) + medge_to_bmeshedge(bm, vert_array, i, me, &dm->edgeData, edge_hash); + + /*add faces.*/ + for(i=0, mf = mface; i < totface; i++, mf++){ + f = mface_to_bmeshface(bm, vert_array, mf, &dm->faceData, edge_hash); + if(f) DMcorners_to_loops(bm, &dm->faceData, i, f, numCol, numTex); + } + + bmesh_end__edit(bm); + BLI_edgehash_free(edge_hash, NULL); + MEM_freeN(vert_array); + return bm; +} + +static void bmeshvert_to_mvert(BMMesh *bm, BMVert *v, MVert *mv, int index, CustomData *data) +{ + copy_v3_v3(mv->co, v->co); + if(bmesh_test_flag(v, BMESH_SELECT)) mv->flag |= 1; + if(bmesh_test_flag(v, BMESH_HIDDEN)) mv->flag |= ME_HIDE; + mv->bweight = (char)(255.0*v1->bweight); + CustomData_from_bmesh_block(&bm->vdata, data, &v1->data, index); +} + +static int bmeshedge_to_medge(BMMesh *bm, BMEdge *e, MEdge *me, int index, CustomData *data) +{ + if(e->head.eflag2){ + if(e->v1->head.eflag1 < e->v2->head.eflag1){ + me->v1 = e->v1->head.eflag1; + me->v2 = e->v2->head.eflag1; + } + else{ + me->v1 = e->v2->head.eflag1; + me->v2 = e->v1->eflag1; + } + + me->crease = (char)(255.0*e->crease); + me->bweight = (char)(255.0*e->bweight); + if(bmesh_test_flag(e, BMESH_SELECT)) me->flag |= 1; + if(bmesh_test_flag(e, BMESH_HIDDEN)) me->flag |= ME_HIDE; + CustomData_from_bmesh_block(&bm->edata, data, &e->data, index); + return 1; + } + return 0; +} + +static int bmeshface_to_mface(BMMesh *bm, BMFace *f, MFace *mf, int index, CustomData *data) +{ + if(f->len==3 || f->len==4){ + mf->v1 = f->loopbase->v->head.eflag1; + mf->v2 = f->loopbase->next->v->head.eflag1; + mf->v3 = f->loopbase->next->next->v->head.eflag1; + if(len == 4){ + mf->v4 = f->loopbase->prev->v->head.eflag1; + } + /* test and rotate indexes if necessary so that verts 3 and 4 aren't index 0 */ + if(mf->v3 == 0 || (f->len == 4 && mf->v4 == 0)){ + test_index_face(mf, NULL, index, f->len); + } + mf->mat_nr = (unsigned char)f->mat_nr; + if(bmesh_test_flag(f, BMESH_SELECT)) mf->flag |= 1; + if(bmesh_test_flag(f, BMESH_HIDDEN)) mf->flag |= ME_HIDE; + CustomData_from_bmesh_block(&bm->pdata, data, &f->data, index); + return TRUE; + } + return FALSE; +} + +/* + * BMESH TO DERIVEDMESH + * + * Converts a bmesh to a derived mesh. + * +*/ + +DerivedMesh *bmesh_to_derivedmesh(BMMesh *bm, DerivedMesh *dm) +{ + MFace *mface = NULL, *mf = NULL; + MEdge *medge = NULL, *me = NULL; + MVert *mvert = NULL, *mv = NULL; + DerivedMesh *result = NULL; + + BMVert *v=NULL; + BMEdge *e=NULL, *oe=NULL; + BMFace *f=NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + + int totface = 0,totedge = 0,totvert = 0,i = 0, numTex, numCol; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + /*get element counts*/ + totvert = bmesh_count_element(bm, BMESH_VERT); + + /*store element indices. Note that the abuse of eflag here should NOT be duplicated!*/ + for(i=0, v = bmeshIterator_init(verts, BM_VERTS, bm, 0); v; v = bmeshIterator_step(verts), i++) + v->head.eflag1 = i; + + /*we cannot have double edges in a derived mesh!*/ + for(e = bmeshIterator_init(edges, BM_EDGES, bm, 0); e; e = bmeshIterator_step(edges)){ + oe = BLI_edgehash_lookup(edge_hash,e->v1->head.eflag1, e->v2->head.eflag1); + if(!oe){ + totedge++; + BLI_edgehash_insert(edge_hash,e->v1->head.eflag1,e->v2->head.eflag1,e); + e->head.eflag2 = 1; + } + else{ + e->head.eflag2 = 0; + } + } + + /*count quads and tris*/ + for(f = bmeshIterator_init(faces, BM_FACES, bm, 0); f; f = bmeshIterator_step(faces)){ + if(f->len == 3 || f->len == 4) totface++; + } + + /*Allocate derivedmesh and copy custom data*/ + result = CDDM_from_template(dm,totvert,totedge,totface); + CustomData_merge(&bm->vdata, &result->vertData, CD_MASK_BMESH, CD_CALLOC, totvert); + CustomData_merge(&bm->edata, &result->edgeData, CD_MASK_BMESH, CD_CALLOC, totedge); + CustomData_merge(&bm->pdata, &result->faceData, CD_MASK_BMESH, CD_CALLOC, totface); + CustomData_from_bmeshpoly(&result->faceData, &bm->pdata, &bm->ldata,totface); + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + /*Make Verts*/ + mvert = CDDM_get_verts(result); + for(i = 0, v = bmeshIterator_init(verts, BM_VERTS, bm, 0); v; v = bmeshIterator_step(verts), i++, mv++){ + bmeshvert_to_mvert(bm,v,mv,i,&result->vertData); + } + + /*Make Edges*/ + medge = CDDM_get_edges(result); + i=0; + for(e = bmeshIterator_init(edges, BM_EDGES, bm, 0); e; e = bmeshIterator_step(edges)){ + me = &medge[i]; + if(bmeshedge_to_medge(bm, e, me, i, &result->edgeData){ + me++; + i++; + } + } + /*Make Faces*/ + if(totface){ + mface = CDDM_get_faces(result); + i=0; + for(f = bmeshIterator_init(faces, BM_FACES, bm, 0); f; f = bmeshIterator_step(faces)){ + mf = &mface[i]; + if(bmeshface_to_mface(bm, f, mf, i, &result->faceData)){ + loops_to_DMcorners(bm, &result->faceData, i, f, numCol, numTex); + i++; + } + } + } + BLI_edgehash_free(edge_hash, NULL); + return result; +} + +#endif diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c new file mode 100644 index 00000000000..98e9a510126 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_bevel.c @@ -0,0 +1,881 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_math.h" +#include "BLI_smallhash.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" + +#define BEVEL_FLAG 1 +#define BEVEL_DEL 2 +#define FACE_NEW 4 +#define EDGE_OLD 8 +#define FACE_OLD 16 +#define VERT_OLD 32 +#define FACE_SPAN 64 +#define FACE_HOLE 128 + +typedef struct LoopTag { + BMVert *newv; +} LoopTag; + +typedef struct EdgeTag { + BMVert *newv1, *newv2; +} EdgeTag; + +static void calc_corner_co(BMesh *bm, BMLoop *l, const float fac, float r_co[3], + const short do_dist, const short do_even) +{ + float no[3], l_vec_prev[3], l_vec_next[3], l_co_prev[3], l_co[3], l_co_next[3], co_ofs[3]; + int is_concave; + + /* first get the prev/next verts */ + if (l->f->len > 2) { + copy_v3_v3(l_co_prev, l->prev->v->co); + copy_v3_v3(l_co, l->v->co); + copy_v3_v3(l_co_next, l->next->v->co); + + /* calculate norma */ + sub_v3_v3v3(l_vec_prev, l_co_prev, l_co); + sub_v3_v3v3(l_vec_next, l_co_next, l_co); + + cross_v3_v3v3(no, l_vec_prev, l_vec_next); + is_concave = dot_v3v3(no, l->f->no) > 0.0f; + } + else { + BMIter iter; + BMLoop *l2; + float up[3] = {0.0f, 0.0f, 1.0f}; + + copy_v3_v3(l_co_prev, l->prev->v->co); + copy_v3_v3(l_co, l->v->co); + + BM_ITER(l2, &iter, bm, BM_LOOPS_OF_VERT, l->v) { + if (l2->f != l->f) { + copy_v3_v3(l_co_next, BM_edge_other_vert(l2->e, l2->next->v)->co); + break; + } + } + + sub_v3_v3v3(l_vec_prev, l_co_prev, l_co); + sub_v3_v3v3(l_vec_next, l_co_next, l_co); + + cross_v3_v3v3(no, l_vec_prev, l_vec_next); + if (dot_v3v3(no, no) == 0.0f) { + no[0] = no[1] = 0.0f; no[2] = -1.0f; + } + + is_concave = dot_v3v3(no, up) < 0.0f; + } + + + /* now calculate the new location */ + if (do_dist) { /* treat 'fac' as distance */ + + normalize_v3(l_vec_prev); + normalize_v3(l_vec_next); + + add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next); + if (UNLIKELY(normalize_v3(co_ofs) == 0.0f)) { /* edges form a straignt line */ + cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no); + } + + if (do_even) { + negate_v3(l_vec_next); + mul_v3_fl(co_ofs, fac * shell_angle_to_dist(0.5f * angle_normalized_v3v3(l_vec_prev, l_vec_next))); + /* negate_v3(l_vec_next); */ /* no need unless we use again */ + } + else { + mul_v3_fl(co_ofs, fac); + } + } + else { /* treat as 'fac' as a factor (0 - 1) */ + + /* not strictly necessary, balance vectors + * so the longer edge doesn't skew the result, + * gives nicer, move even output. + * + * Use the minimum rather then the middle value so skinny faces don't flip along the short axis */ + float min_fac = minf(normalize_v3(l_vec_prev), normalize_v3(l_vec_next)); + float angle; + + if (do_even) { + negate_v3(l_vec_next); + angle = angle_normalized_v3v3(l_vec_prev, l_vec_next); + negate_v3(l_vec_next); /* no need unless we use again */ + } + else { + angle = 0.0f; + } + + mul_v3_fl(l_vec_prev, min_fac); + mul_v3_fl(l_vec_next, min_fac); + + add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next); + + if (UNLIKELY(is_zero_v3(co_ofs))) { + cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no); + normalize_v3(co_ofs); + mul_v3_fl(co_ofs, min_fac); + } + + /* done */ + if (do_even) { + mul_v3_fl(co_ofs, (fac * 0.5) * shell_angle_to_dist(0.5f * angle)); + } + else { + mul_v3_fl(co_ofs, fac * 0.5); + } + } + + /* apply delta vec */ + if (is_concave) + negate_v3(co_ofs); + + add_v3_v3v3(r_co, co_ofs, l->v->co); +} + + +#define ETAG_SET(e, v, nv) ( \ + (v) == (e)->v1 ? \ + (etags[BM_elem_index_get((e))].newv1 = (nv)) : \ + (etags[BM_elem_index_get((e))].newv2 = (nv)) \ + ) + +#define ETAG_GET(e, v) ( \ + (v) == (e)->v1 ? \ + (etags[BM_elem_index_get((e))].newv1) : \ + (etags[BM_elem_index_get((e))].newv2) \ + ) + +void bmesh_bevel_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMEdge *e; + BMVert *v; + BMFace **faces = NULL, *f; + LoopTag *tags = NULL, *tag; + EdgeTag *etags = NULL; + BMVert **verts = NULL; + BMEdge **edges = NULL; + BLI_array_declare(faces); + BLI_array_declare(tags); + BLI_array_declare(etags); + BLI_array_declare(verts); + BLI_array_declare(edges); + SmallHash hash; + float fac = BMO_slot_float_get(op, "percent"); + const short do_even = BMO_slot_int_get(op, "use_even"); + const short do_dist = BMO_slot_int_get(op, "use_dist"); + int i, li, has_elens, HasMDisps = CustomData_has_layer(&bm->ldata, CD_MDISPS); + + has_elens = CustomData_has_layer(&bm->edata, CD_PROP_FLT) && BMO_slot_int_get(op, "use_lengths"); + if (has_elens) { + li = BMO_slot_int_get(op, "lengthlayer"); + } + + BLI_smallhash_init(&hash); + + BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) { + BMO_elem_flag_enable(bm, e, BEVEL_FLAG|BEVEL_DEL); + BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL); + BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL); + + if (BM_edge_face_count(e) < 2) { + BMO_elem_flag_disable(bm, e, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL); + } +#if 0 + if (BM_edge_face_count(e) == 0) { + BMVert *verts[2] = {e->v1, e->v2}; + BMEdge *edges[2] = {e, BM_edge_create(bm, e->v1, e->v2, e, 0)}; + + BMO_elem_flag_enable(bm, edges[1], BEVEL_FLAG); + BM_face_create(bm, verts, edges, 2, FALSE); + } +#endif + } + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, v, VERT_OLD); + } + +#if 0 + //a bit of cleaner code that, alas, doens't work. + /* build edge tag */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e->v1, BEVEL_FLAG) || BMO_elem_flag_test(bm, e->v2, BEVEL_FLAG)) { + BMIter liter; + BMLoop *l; + + if (!BMO_elem_flag_test(bm, e, EDGE_OLD)) { + BM_elem_index_set(e, BLI_array_count(etags)); /* set_dirty! */ + BLI_array_growone(etags); + + BMO_elem_flag_enable(bm, e, EDGE_OLD); + } + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + BMLoop *l2; + BMIter liter2; + + if (BMO_elem_flag_test(bm, l->f, BEVEL_FLAG)) + continue; + + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) { + BM_elem_index_set(l2, BLI_array_count(tags)); /* set_loop */ + BLI_array_growone(tags); + + if (!BMO_elem_flag_test(bm, l2->e, EDGE_OLD)) { + BM_elem_index_set(l2->e, BLI_array_count(etags)); /* set_dirty! */ + BLI_array_growone(etags); + + BMO_elem_flag_enable(bm, l2->e, EDGE_OLD); + } + } + + BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG); + BLI_array_append(faces, l->f); + } + } + else { + BM_elem_index_set(e, -1); /* set_dirty! */ + } + } +#endif + + /* create and assign looptag structure */ + BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) { + BMLoop *l; + BMIter liter; + + BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL); + BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL); + + if (BM_edge_face_count(e) < 2) { + BMO_elem_flag_disable(bm, e, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL); + } + + if (!BLI_smallhash_haskey(&hash, (intptr_t)e)) { + BLI_array_growone(etags); + BM_elem_index_set(e, BLI_array_count(etags) - 1); /* set_dirty! */ + BLI_smallhash_insert(&hash, (intptr_t)e, NULL); + BMO_elem_flag_enable(bm, e, EDGE_OLD); + } + + /* find all faces surrounding e->v1 and, e->v2 */ + for (i = 0; i < 2; i++) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, i ? e->v2:e->v1) { + BMLoop *l2; + BMIter liter2; + + /* see if we've already processed this loop's fac */ + if (BLI_smallhash_haskey(&hash, (intptr_t)l->f)) + continue; + + /* create tags for all loops in l-> */ + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) { + BLI_array_growone(tags); + BM_elem_index_set(l2, BLI_array_count(tags) - 1); /* set_loop */ + + if (!BLI_smallhash_haskey(&hash, (intptr_t)l2->e)) { + BLI_array_growone(etags); + BM_elem_index_set(l2->e, BLI_array_count(etags) - 1); /* set_dirty! */ + BLI_smallhash_insert(&hash, (intptr_t)l2->e, NULL); + BMO_elem_flag_enable(bm, l2->e, EDGE_OLD); + } + } + + BLI_smallhash_insert(&hash, (intptr_t)l->f, NULL); + BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG); + BLI_array_append(faces, l->f); + } + } + } + + bm->elem_index_dirty |= BM_EDGE; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMIter eiter; + + if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG)) + continue; + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && !ETAG_GET(e, v)) { + BMVert *v2; + float co[3]; + + v2 = BM_edge_other_vert(e, v); + sub_v3_v3v3(co, v2->co, v->co); + if (has_elens) { + float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, e->head.data, CD_PROP_FLT, li); + + normalize_v3(co); + mul_v3_fl(co, elen); + } + + mul_v3_fl(co, fac); + add_v3_v3(co, v->co); + + v2 = BM_vert_create(bm, co, v); + ETAG_SET(e, v, v2); + } + } + } + + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + + BMO_elem_flag_enable(bm, faces[i], FACE_OLD); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) { + float co[3]; + + if (BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) { + if (BMO_elem_flag_test(bm, l->prev->e, BEVEL_FLAG)) + { + tag = tags + BM_elem_index_get(l); + calc_corner_co(bm, l, fac, co, do_dist, do_even); + tag->newv = BM_vert_create(bm, co, l->v); + } + else { + tag = tags + BM_elem_index_get(l); + tag->newv = ETAG_GET(l->prev->e, l->v); + + if (!tag->newv) { + sub_v3_v3v3(co, l->prev->v->co, l->v->co); + if (has_elens) { + float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, + CD_PROP_FLT, li); + + normalize_v3(co); + mul_v3_fl(co, elen); + } + + mul_v3_fl(co, fac); + add_v3_v3(co, l->v->co); + + tag->newv = BM_vert_create(bm, co, l->v); + + ETAG_SET(l->prev->e, l->v, tag->newv); + } + } + } + else if (BMO_elem_flag_test(bm, l->v, BEVEL_FLAG)) { + tag = tags + BM_elem_index_get(l); + tag->newv = ETAG_GET(l->e, l->v); + + if (!tag->newv) { + sub_v3_v3v3(co, l->next->v->co, l->v->co); + if (has_elens) { + float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->e->head.data, CD_PROP_FLT, li); + + normalize_v3(co); + mul_v3_fl(co, elen); + } + + mul_v3_fl(co, fac); + add_v3_v3(co, l->v->co); + + tag = tags + BM_elem_index_get(l); + tag->newv = BM_vert_create(bm, co, l->v); + + ETAG_SET(l->e, l->v, tag->newv); + } + } + else { + tag = tags + BM_elem_index_get(l); + tag->newv = l->v; + BMO_elem_flag_disable(bm, l->v, BEVEL_DEL); + } + } + } + + /* create new faces inset from original face */ + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + BMFace *f; + BMVert *lastv = NULL, *firstv = NULL; + + BMO_elem_flag_enable(bm, faces[i], BEVEL_DEL); + + BLI_array_empty(verts); + BLI_array_empty(edges); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) { + BMVert *v2; + + tag = tags + BM_elem_index_get(l); + BLI_array_append(verts, tag->newv); + + if (!firstv) + firstv = tag->newv; + + if (lastv) { + e = BM_edge_create(bm, lastv, tag->newv, l->e, TRUE); + BM_elem_attrs_copy(bm, bm, l->prev->e, e); + BLI_array_append(edges, e); + } + lastv = tag->newv; + + v2 = ETAG_GET(l->e, l->next->v); + + tag = &tags[BM_elem_index_get(l->next)]; + if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG) && v2 && v2 != tag->newv) { + BLI_array_append(verts, v2); + + e = BM_edge_create(bm, lastv, v2, l->e, TRUE); + BM_elem_attrs_copy(bm, bm, l->e, e); + + BLI_array_append(edges, e); + lastv = v2; + } + } + + e = BM_edge_create(bm, firstv, lastv, BM_FACE_FIRST_LOOP(faces[i])->e, TRUE); + if (BM_FACE_FIRST_LOOP(faces[i])->prev->e != e) { + BM_elem_attrs_copy(bm, bm, BM_FACE_FIRST_LOOP(faces[i])->prev->e, e); + } + BLI_array_append(edges, e); + + f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), FALSE); + if (!f) { + printf("%s: could not make face!\n", __func__); + continue; + } + + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + int j; + + /* create quad spans between split edge */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) { + BMVert *v1 = NULL, *v2 = NULL, *v3 = NULL, *v4 = NULL; + + if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) + continue; + + v1 = tags[BM_elem_index_get(l)].newv; + v2 = tags[BM_elem_index_get(l->next)].newv; + if (l->radial_next != l) { + v3 = tags[BM_elem_index_get(l->radial_next)].newv; + if (l->radial_next->next->v == l->next->v) { + v4 = v3; + v3 = tags[BM_elem_index_get(l->radial_next->next)].newv; + } + else { + v4 = tags[BM_elem_index_get(l->radial_next->next)].newv; + } + } + else { + /* the loop is on a boundar */ + v3 = l->next->v; + v4 = l->v; + + for (j = 0; j < 2; j++) { + BMIter eiter; + BMVert *v = j ? v4 : v3; + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BM_vert_in_edge(e, v3) || !BM_vert_in_edge(e, v4)) + continue; + + if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && BMO_elem_flag_test(bm, e, EDGE_OLD)) { + BMVert *vv; + + vv = ETAG_GET(e, v); + if (!vv || BMO_elem_flag_test(bm, vv, BEVEL_FLAG)) + continue; + + if (j) + v1 = vv; + else + v2 = vv; + break; + } + } + } + + BMO_elem_flag_disable(bm, v3, BEVEL_DEL); + BMO_elem_flag_disable(bm, v4, BEVEL_DEL); + } + + if (v1 != v2 && v2 != v3 && v3 != v4) { + BMIter liter2; + BMLoop *l2, *l3; + BMEdge *e1, *e2; + float d1, d2, *d3; + + f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, l->f, TRUE); + + e1 = BM_edge_exists(v4, v3); + e2 = BM_edge_exists(v2, v1); + BM_elem_attrs_copy(bm, bm, l->e, e1); + BM_elem_attrs_copy(bm, bm, l->e, e2); + + /* set edge lengths of cross edges as the average of the cross edges they're based o */ + if (has_elens) { + /* angle happens not to be used. why? - not sure it just isnt - campbell. + * leave this in incase we need to use it later */ +#if 0 + float ang; +#endif + e1 = BM_edge_exists(v1, v4); + e2 = BM_edge_exists(v2, v3); + + if (l->radial_next->v == l->v) { + l2 = l->radial_next->prev; + l3 = l->radial_next->next; + } + else { + l2 = l->radial_next->next; + l3 = l->radial_next->prev; + } + + d3 = CustomData_bmesh_get_n(&bm->edata, e1->head.data, CD_PROP_FLT, li); + d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li); + d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l2->e->head.data, CD_PROP_FLT, li); +#if 0 + ang = angle_v3v3v3(l->prev->v->co, l->v->co, BM_edge_other_vert(l2->e, l->v)->co); +#endif + *d3 = (d1 + d2) * 0.5f; + + d3 = CustomData_bmesh_get_n(&bm->edata, e2->head.data, CD_PROP_FLT, li); + d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->next->e->head.data, CD_PROP_FLT, li); + d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l3->e->head.data, CD_PROP_FLT, li); +#if 0 + ang = angle_v3v3v3(BM_edge_other_vert(l->next->e, l->next->v)->co, l->next->v->co, + BM_edge_other_vert(l3->e, l->next->v)->co); +#endif + *d3 = (d1 + d2) * 0.5f; + } + + if (!f) { + fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__); + continue; + } + + BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_SPAN); + + /* un-tag edges in f for deletio */ + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, f) { + BMO_elem_flag_disable(bm, l2->e, BEVEL_DEL); + } + } + else { + f = NULL; + } + } + } + + /* fill in holes at vertices */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMIter eiter; + BMVert *vv, *vstart = NULL, *lastv = NULL; + SmallHash tmphash; + int rad, insorig = 0, err = 0; + + BLI_smallhash_init(&tmphash); + + if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG)) + continue; + + BLI_array_empty(verts); + BLI_array_empty(edges); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BMIter liter; + BMVert *v1 = NULL, *v2 = NULL; + BMLoop *l; + + if (BM_edge_face_count(e) < 2) + insorig = 1; + + if (BM_elem_index_get(e) == -1) + continue; + + rad = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + if (!BMO_elem_flag_test(bm, l->f, FACE_OLD)) + continue; + + rad++; + + tag = tags + BM_elem_index_get((l->v == v) ? l : l->next); + + if (!v1) + v1 = tag->newv; + else if (!v2) + v2 = tag->newv; + } + + if (rad < 2) + insorig = 1; + + if (!v1) + v1 = ETAG_GET(e, v); + if (!v2 || v1 == v2) + v2 = ETAG_GET(e, v); + + if (v1) { + if (!BLI_smallhash_haskey(&tmphash, (intptr_t)v1)) { + BLI_array_append(verts, v1); + BLI_smallhash_insert(&tmphash, (intptr_t)v1, NULL); + } + + if (v2 && v1 != v2 && !BLI_smallhash_haskey(&tmphash, (intptr_t)v2)) { + BLI_array_append(verts, v2); + BLI_smallhash_insert(&tmphash, (intptr_t)v2, NULL); + } + } + } + + if (!BLI_array_count(verts)) + continue; + + if (insorig) { + BLI_array_append(verts, v); + BLI_smallhash_insert(&tmphash, (intptr_t)v, NULL); + } + + /* find edges that exist between vertices in verts. this is basically + * a topological walk of the edges connecting them */ + vstart = vstart ? vstart : verts[0]; + vv = vstart; + do { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) { + BMVert *vv2 = BM_edge_other_vert(e, vv); + + if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) { + /* if we've go over the same vert twice, break out of outer loop */ + if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) { + e = NULL; + err = 1; + break; + } + + /* use self pointer as ta */ + BLI_smallhash_remove(&tmphash, (intptr_t)vv2); + BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2); + + lastv = vv; + BLI_array_append(edges, e); + vv = vv2; + break; + } + } + + if (e == NULL) { + break; + } + } while (vv != vstart); + + if (err) { + continue; + } + + /* there may not be a complete loop of edges, so start again and make + * final edge afterwards. in this case, the previous loop worked to + * find one of the two edges at the extremes. */ + if (vv != vstart) { + /* undo previous taggin */ + for (i = 0; i < BLI_array_count(verts); i++) { + BLI_smallhash_remove(&tmphash, (intptr_t)verts[i]); + BLI_smallhash_insert(&tmphash, (intptr_t)verts[i], NULL); + } + + vstart = vv; + lastv = NULL; + BLI_array_empty(edges); + do { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) { + BMVert *vv2 = BM_edge_other_vert(e, vv); + + if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) { + /* if we've go over the same vert twice, break out of outer loo */ + if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) { + e = NULL; + err = 1; + break; + } + + /* use self pointer as ta */ + BLI_smallhash_remove(&tmphash, (intptr_t)vv2); + BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2); + + lastv = vv; + BLI_array_append(edges, e); + vv = vv2; + break; + } + } + if (e == NULL) + break; + } while (vv != vstart); + + if (!err) { + e = BM_edge_create(bm, vv, vstart, NULL, TRUE); + BLI_array_append(edges, e); + } + } + + if (err) + continue; + + if (BLI_array_count(edges) >= 3) { + BMFace *f; + + if (BM_face_exists(bm, verts, BLI_array_count(verts), &f)) + continue; + + f = BM_face_create_ngon(bm, lastv, vstart, edges, BLI_array_count(edges), FALSE); + if (!f) { + fprintf(stderr, "%s: in bevel vert fill! (bmesh internal error)\n", __func__); + } + else { + BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_HOLE); + } + } + BLI_smallhash_release(&tmphash); + } + + /* copy over customdat */ + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + BMFace *f = faces[i]; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BMLoop *l2; + BMIter liter2; + + tag = tags + BM_elem_index_get(l); + if (!tag->newv) + continue; + + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_VERT, tag->newv) { + if (!BMO_elem_flag_test(bm, l2->f, FACE_NEW) || (l2->v != tag->newv && l2->v != l->v)) + continue; + + if (tag->newv != l->v || HasMDisps) { + BM_elem_attrs_copy(bm, bm, l->f, l2->f); + BM_loop_interp_from_face(bm, l2, l->f, TRUE, TRUE); + } + else { + BM_elem_attrs_copy(bm, bm, l->f, l2->f); + BM_elem_attrs_copy(bm, bm, l, l2); + } + + if (HasMDisps) { + BMLoop *l3; + BMIter liter3; + + BM_ITER(l3, &liter3, bm, BM_LOOPS_OF_FACE, l2->f) { + BM_loop_interp_multires(bm, l3, l->f); + } + } + } + } + } + + /* handle vertices along boundary edge */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_OLD) && + BMO_elem_flag_test(bm, v, BEVEL_FLAG) && + !BMO_elem_flag_test(bm, v, BEVEL_DEL)) + { + BMLoop *l; + BMLoop *lorig = NULL; + BMIter liter; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + // BMIter liter2; + // BMLoop *l2 = l->v == v ? l : l->next, *l3; + + if (BMO_elem_flag_test(bm, l->f, FACE_OLD)) { + lorig = l; + break; + } + } + + if (!lorig) + continue; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + BMLoop *l2 = l->v == v ? l : l->next; + + BM_elem_attrs_copy(bm, bm, lorig->f, l2->f); + BM_elem_attrs_copy(bm, bm, lorig, l2); + } + } + } +#if 0 + /* clean up any remaining 2-edged face */ + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (f->len == 2) { + BMFace *faces[2] = {f, BM_FACE_FIRST_LOOP(f)->radial_next->f}; + + if (faces[0] == faces[1]) + BM_face_kill(bm, f); + else + BM_faces_join(bm, faces, 2); + } + } +#endif + + BMO_op_callf(bm, "del geom=%fv context=%i", BEVEL_DEL, DEL_VERTS); + + /* clean up any edges that might not get properly delete */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, EDGE_OLD) && !e->l) + BMO_elem_flag_enable(bm, e, BEVEL_DEL); + } + + BMO_op_callf(bm, "del geom=%fe context=%i", BEVEL_DEL, DEL_EDGES); + BMO_op_callf(bm, "del geom=%ff context=%i", BEVEL_DEL, DEL_FACES); + + BLI_smallhash_release(&hash); + BLI_array_free(tags); + BLI_array_free(etags); + BLI_array_free(verts); + BLI_array_free(edges); + BLI_array_free(faces); + + BMO_slot_from_flag(bm, op, "face_spans", FACE_SPAN, BM_FACE); + BMO_slot_from_flag(bm, op, "face_holes", FACE_HOLE, BM_FACE); +} diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c new file mode 100644 index 00000000000..6ab6d28008b --- /dev/null +++ b/source/blender/bmesh/operators/bmo_connect.c @@ -0,0 +1,414 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + + +#include "bmesh.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#define VERT_INPUT 1 +#define EDGE_OUT 1 +#define FACE_NEW 2 +#define EDGE_MARK 4 +#define EDGE_DONE 8 + +void connectverts_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter, liter; + BMFace *f, *nf; + BMLoop **loops = NULL, *lastl = NULL; + BLI_array_declare(loops); + BMLoop *l, *nl; + BMVert **verts = NULL; + BLI_array_declare(verts); + int i; + + BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_INPUT, BM_VERT); + + for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) { + BLI_array_empty(loops); + BLI_array_empty(verts); + + if (BMO_elem_flag_test(bm, f, FACE_NEW)) continue; + + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + lastl = NULL; + for ( ; l; l = BM_iter_step(&liter)) { + if (BMO_elem_flag_test(bm, l->v, VERT_INPUT)) { + if (!lastl) { + lastl = l; + continue; + } + + if (lastl != l->prev && lastl != l->next) { + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = lastl; + + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = l; + + } + lastl = l; + } + } + + if (BLI_array_count(loops) == 0) continue; + + if (BLI_array_count(loops) > 2) { + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = loops[BLI_array_count(loops) - 2]; + + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = loops[0]; + } + + BM_face_legal_splits(bm, f, (BMLoop *(*)[2])loops, BLI_array_count(loops) / 2); + + for (i = 0; i < BLI_array_count(loops) / 2; i++) { + if (loops[i * 2] == NULL) continue; + + BLI_array_growone(verts); + verts[BLI_array_count(verts) - 1] = loops[i * 2]->v; + + BLI_array_growone(verts); + verts[BLI_array_count(verts) - 1] = loops[i * 2 + 1]->v; + } + + for (i = 0; i < BLI_array_count(verts) / 2; i++) { + nf = BM_face_split(bm, f, verts[i * 2], verts[i * 2 + 1], &nl, NULL); + f = nf; + + if (!nl || !nf) { + BMO_error_raise(bm, op, BMERR_CONNECTVERT_FAILED, NULL); + BLI_array_free(loops); + return; + } + BMO_elem_flag_enable(bm, nf, FACE_NEW); + BMO_elem_flag_enable(bm, nl->e, EDGE_OUT); + } + } + + BMO_slot_from_flag(bm, op, "edgeout", EDGE_OUT, BM_EDGE); + + BLI_array_free(loops); + BLI_array_free(verts); +} + +static BMVert *get_outer_vert(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMEdge *e2; + int i; + + i = 0; + BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, e->v1) { + if (BMO_elem_flag_test(bm, e2, EDGE_MARK)) { + i++; + } + } + + return (i == 2) ? e->v2 : e->v1; +} + +/* Clamp x to the interval {0..len-1}, with wrap-around */ +static int clamp_index(const int x, const int len) +{ + return (x < 0) ? (len - (-x % len)) : (x % len); +} + +/* There probably is a better way to swap BLI_arrays, or if there + * isn't there should be... */ +#define ARRAY_SWAP(elemtype, arr1, arr2) \ + { \ + int i; \ + elemtype *arr_tmp = NULL; \ + BLI_array_declare(arr_tmp); \ + for (i = 0; i < BLI_array_count(arr1); i++) { \ + BLI_array_append(arr_tmp, arr1[i]); \ + } \ + BLI_array_empty(arr1); \ + for (i = 0; i < BLI_array_count(arr2); i++) { \ + BLI_array_append(arr1, arr2[i]); \ + } \ + BLI_array_empty(arr2); \ + for (i = 0; i < BLI_array_count(arr_tmp); i++) { \ + BLI_array_append(arr2, arr_tmp[i]); \ + } \ + BLI_array_free(arr_tmp); \ + } + +void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op) +{ + BMEdge **ee1 = NULL, **ee2 = NULL; + BMVert **vv1 = NULL, **vv2 = NULL; + BLI_array_declare(ee1); + BLI_array_declare(ee2); + BLI_array_declare(vv1); + BLI_array_declare(vv2); + BMOIter siter; + BMIter iter; + BMEdge *e, *nexte; + int c = 0, cl1 = 0, cl2 = 0; + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + if (!BMO_elem_flag_test(bm, e, EDGE_DONE)) { + BMVert *v, *ov; + /* BMEdge *e2, *e3, *oe = e; */ /* UNUSED */ + BMEdge *e2, *e3; + + if (c > 2) { + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Select only two edge loops"); + goto cleanup; + } + + e2 = e; + v = e->v1; + do { + v = BM_edge_other_vert(e2, v); + nexte = NULL; + BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) { + if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK)) { + if (nexte == NULL) { + nexte = e3; + } + else { + /* edges do not form a loop: there is a disk + * with more than two marked edges. */ + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, + "Selection must only contain edges from two edge loops"); + goto cleanup; + } + } + } + + if (nexte) + e2 = nexte; + } while (nexte && e2 != e); + + if (!e2) + e2 = e; + + e = e2; + ov = v; + do { + if (c == 0) { + BLI_array_append(ee1, e2); + BLI_array_append(vv1, v); + } + else { + BLI_array_append(ee2, e2); + BLI_array_append(vv2, v); + } + + BMO_elem_flag_enable(bm, e2, EDGE_DONE); + + v = BM_edge_other_vert(e2, v); + BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) { + if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK) && !BMO_elem_flag_test(bm, e3, EDGE_DONE)) { + break; + } + } + if (e3) + e2 = e3; + } while (e3 && e2 != e); + + if (v && !e3) { + if (c == 0) { + if (BLI_array_count(vv1) && v == vv1[BLI_array_count(vv1) - 1]) { + printf("%s: internal state waning *TODO DESCRIPTION!*\n", __func__); + } + BLI_array_append(vv1, v); + } + else { + BLI_array_append(vv2, v); + } + } + + /* test for connected loops, and set cl1 or cl2 if so */ + if (v == ov) { + if (c == 0) { + cl1 = 1; + } + else { + cl2 = 1; + } + } + + c++; + } + } + + if (ee1 && ee2) { + int i, j; + BMVert *v1, *v2, *v3, *v4; + int starti = 0, dir1 = 1, wdir = 0, lenv1, lenv2; + + /* Simplify code below by avoiding the (!cl1 && cl2) case */ + if (!cl1 && cl2) { + SWAP(int, cl1, cl2); + ARRAY_SWAP(BMVert *, vv1, vv2); + ARRAY_SWAP(BMEdge *, ee1, ee2); + } + + lenv1 = lenv2 = BLI_array_count(vv1); + + /* Below code assumes vv1/vv2 each have at least two verts. should always be + * a safe assumption, since ee1/ee2 are non-empty and an edge has two verts. */ + BLI_assert((lenv1 > 1) && (lenv2 > 1)); + + /* BMESH_TODO: Would be nice to handle cases where the edge loops + * have different edge counts by generating triangles & quads for + * the bridge instead of quads only. */ + if (BLI_array_count(ee1) != BLI_array_count(ee2)) { + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, + "Selected loops must have equal edge counts"); + goto cleanup; + } + + j = 0; + if (vv1[0] == vv1[lenv1 - 1]) { + lenv1--; + } + if (vv2[0] == vv2[lenv2 - 1]) { + lenv2--; + } + + /* Find starting point and winding direction for two unclosed loops */ + if (!cl1 && !cl2) { + /* First point of loop 1 */ + v1 = get_outer_vert(bm, ee1[0]); + /* Last point of loop 1 */ + v2 = get_outer_vert(bm, ee1[clamp_index(-1, BLI_array_count(ee1))]); + /* First point of loop 2 */ + v3 = get_outer_vert(bm, ee2[0]); + /* Last point of loop 2 */ + v4 = get_outer_vert(bm, ee2[clamp_index(-1, BLI_array_count(ee2))]); + + /* If v1 is a better match for v4 than v3, AND v2 is a better match + * for v3 than v4, the loops are in opposite directions, so reverse + * the order of reads from vv1. We can avoid sqrt for comparison */ + if (len_squared_v3v3(v1->co, v3->co) > len_squared_v3v3(v1->co, v4->co) && + len_squared_v3v3(v2->co, v4->co) > len_squared_v3v3(v2->co, v3->co)) + { + dir1 = -1; + starti = clamp_index(-1, lenv1); + } + } + + /* Find the shortest distance from a vert in vv1 to vv2[0]. Use that + * vertex in vv1 as a starting point in the first loop, while starting + * from vv2[0] in the second loop. This is a simplistic attempt to get + * a better edge-to-edge match between the two loops. */ + if (cl1) { + int previ, nexti; + float min = 1e32; + + /* BMESH_TODO: Would be nice to do a more thorough analysis of all + * the vertices in both loops to find a more accurate match for the + * starting point and winding direction of the bridge generation. */ + + for (i = 0; i < BLI_array_count(vv1); i++) { + if (len_v3v3(vv1[i]->co, vv2[0]->co) < min) { + min = len_v3v3(vv1[i]->co, vv2[0]->co); + starti = i; + } + } + + /* Reverse iteration order for the first loop if the distance of + * the (starti - 1) vert from vv1 is a better match for vv2[1] than + * the (starti + 1) vert. + * + * This is not always going to be right, but it will work better in + * the average case. + */ + previ = clamp_index(starti - 1, lenv1); + nexti = clamp_index(starti + 1, lenv1); + + /* avoid sqrt for comparison */ + if (len_squared_v3v3(vv1[nexti]->co, vv2[1]->co) > len_squared_v3v3(vv1[previ]->co, vv2[1]->co)) { + /* reverse direction for reading vv1 (1 is forward, -1 is backward) */ + dir1 = -1; + } + } + + /* Vert rough attempt to determine proper winding for the bridge quads: + * just uses the first loop it finds for any of the edges of ee2 or ee1 */ + if (wdir == 0) { + for (i = 0; i < BLI_array_count(ee2); i++) { + if (ee2[i]->l) { + wdir = (ee2[i]->l->v == vv2[i]) ? (-1) : (1); + break; + } + } + } + if (wdir == 0) { + for (i = 0; i < BLI_array_count(ee1); i++) { + j = clamp_index((i * dir1) + starti, BLI_array_count(ee1)); + if (ee1[j]->l && ee2[j]->l) { + wdir = (ee2[j]->l->v == vv2[j]) ? (1) : (-1); + break; + } + } + } + + /* Generate the bridge quads */ + for (i = 0; i < BLI_array_count(ee1) && i < BLI_array_count(ee2); i++) { + BMFace *f; + int i1, i1next, i2, i2next; + + i1 = clamp_index(i * dir1 + starti, lenv1); + i1next = clamp_index((i + 1) * dir1 + starti, lenv1); + i2 = i; + i2next = clamp_index(i + 1, lenv2); + + if (vv1[i1] == vv1[i1next]) { + continue; + } + + if (wdir < 0) { + SWAP(int, i1, i1next); + SWAP(int, i2, i2next); + } + + f = BM_face_create_quad_tri(bm, + vv1[i1], + vv2[i2], + vv2[i2next], + vv1[i1next], + NULL, TRUE); + if (!f || f->len != 4) { + fprintf(stderr, "%s: in bridge! (bmesh internal error)\n", __func__); + } + } + } + +cleanup: + BLI_array_free(ee1); + BLI_array_free(ee2); + BLI_array_free(vv1); + BLI_array_free(vv2); +} diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c new file mode 100644 index 00000000000..d90e5c36c80 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_create.c @@ -0,0 +1,1412 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_heap.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_smallhash.h" +#include "BLI_rand.h" + +#include "bmesh.h" + + +#define EDGE_MARK 1 +#define EDGE_VIS 2 + +#define FACE_NEW 1 + +#define ELE_NEW 1 +#define ELE_OUT 2 +#define ELE_ORIG 4 + +#define FACE_IGNORE 16 + +typedef struct EPathNode { + struct EPathNode *next, *prev; + BMVert *v; + BMEdge *e; + BMEdge *cure; +} EPathNode; + +typedef struct EPath { + ListBase nodes; + float weight; + int group; +} EPath; + +typedef struct PathBase { + BLI_mempool *nodepool, *pathpool; +} PathBase; + +typedef struct EdgeData { + int tag; + int ftag; + BMDiskLink v1_disk_link, v2_disk_link; +} EdgeData; + +typedef struct VertData { + BMEdge *e; + float no[3], offco[3], sco[3]; /* offco is vertex coordinate slightly offset randoml */ + int tag; +} VertData; + +static int count_edge_faces(BMesh *bm, BMEdge *e); + +/**** rotation system code * */ + +#define RS_GET_EDGE_LINK(e, v, e_data) ( \ + (v) == ((BMEdge *)(e))->v1 ? \ + &(((EdgeData *)(e_data))->v1_disk_link) : \ + &(((EdgeData *)(e_data))->v2_disk_link) \ + ) + + +static int rotsys_append_edge(struct BMEdge *e, struct BMVert *v, + EdgeData *edata, VertData *vdata) +{ + EdgeData *ed = &edata[BM_elem_index_get(e)]; + VertData *vd = &vdata[BM_elem_index_get(v)]; + + if (!vd->e) { + Link *e1 = (Link *)RS_GET_EDGE_LINK(e, v, ed); + + vd->e = e; + e1->next = e1->prev = (Link *)e; + } + else { + BMDiskLink *dl1, *dl2, *dl3; + EdgeData *ved = &edata[BM_elem_index_get(vd->e)]; + + dl1 = RS_GET_EDGE_LINK(e, v, ed); + dl2 = RS_GET_EDGE_LINK(vd->e, v, ved); + dl3 = dl2->prev ? RS_GET_EDGE_LINK(dl2->prev, v, &edata[BM_elem_index_get(dl2->prev)]) : NULL; + + dl1->next = vd->e; + dl1->prev = dl2->prev; + + dl2->prev = e; + if (dl3) { + dl3->next = e; + } + } + + return TRUE; +} + +static void UNUSED_FUNCTION(rotsys_remove_edge)(struct BMEdge *e, struct BMVert *v, + EdgeData *edata, VertData *vdata) +{ + EdgeData *ed = edata + BM_elem_index_get(e); + VertData *vd = vdata + BM_elem_index_get(v); + BMDiskLink *e1, *e2; + + e1 = RS_GET_EDGE_LINK(e, v, ed); + if (e1->prev) { + e2 = RS_GET_EDGE_LINK(e1->prev, v, ed); + e2->next = e1->next; + } + + if (e1->next) { + e2 = RS_GET_EDGE_LINK(e1->next, v, ed); + e2->prev = e1->prev; + } + + if (vd->e == e) + vd->e = (e != (BMEdge *)e1->next) ? (BMEdge *)e1->next : NULL; + + e1->next = e1->prev = NULL; +} + +static struct BMEdge *rotsys_nextedge(struct BMEdge *e, struct BMVert *v, + EdgeData *edata, VertData *UNUSED(vdata)) +{ + if (v == e->v1) + return edata[BM_elem_index_get(e)].v1_disk_link.next; + if (v == e->v2) + return edata[BM_elem_index_get(e)].v2_disk_link.next; + return NULL; +} + +static BMEdge *rotsys_prevedge(BMEdge *e, BMVert *v, + EdgeData *edata, VertData *UNUSED(vdata)) +{ + if (v == e->v1) + return edata[BM_elem_index_get(e)].v1_disk_link.prev; + if (v == e->v2) + return edata[BM_elem_index_get(e)].v2_disk_link.prev; + return NULL; +} + +static void rotsys_reverse(struct BMEdge *UNUSED(e), struct BMVert *v, EdgeData *edata, VertData *vdata) +{ + BMEdge **edges = NULL; + BMEdge *e_first; + BMEdge *e; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + int i, totedge; + + e = e_first = vdata[BM_elem_index_get(v)].e; + do { + BLI_array_append(edges, e); + e = rotsys_nextedge(e, v, edata, vdata); + } while (e != e_first); + + totedge = BLI_array_count(edges); + for (i = 0; i < totedge / 2; i++) { + SWAP(BMEdge *, edges[i], edges[totedge - 1 - i]); + } + + vdata[BM_elem_index_get(v)].e = NULL; + for (i = 0; i < totedge; i++) { + rotsys_append_edge(edges[i], v, edata, vdata); + } + + BLI_array_free(edges); +} + +static int UNUSED_FUNCTION(rotsys_count)(struct BMVert *v, EdgeData *edata, VertData *vdata) +{ + BMEdge *e = vdata[BM_elem_index_get(v)].e; + int i = 0; + + if (!e) + return 0; + + do { + if (!e) + return 0; + e = rotsys_nextedge(e, v, edata, vdata); + + if (i >= (1 << 20)) { + printf("bmesh error: infinite loop in disk cycle!\n"); + return 0; + } + + i += 1; + } while (e != vdata[BM_elem_index_get(v)].e); + + return i; +} + +static int UNUSED_FUNCTION(rotsys_fill_faces)(BMesh *bm, EdgeData *edata, VertData *vdata) +{ + BMIter iter; + BMEdge *e, **edges = NULL; + BLI_array_declare(edges); + BMVert *v, **verts = NULL; + BMFace *f; + BLI_array_declare(verts); + SmallHash visithash, *hash = &visithash; + int i; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMEdge *e2, *starte; + BMVert *startv; + int rad, ok; + + rad = count_edge_faces(bm, e); + + if (rad < 2) + starte = e; + else + continue; + + /* do two passes, going forward then backwar */ + for (i = 0; i < 2; i++) { + BLI_smallhash_init(hash); + + BLI_array_empty(verts); + BLI_array_empty(edges); + + startv = v = starte->v1; + e2 = starte; + ok = 1; + if (!v || !e2) + continue; + + do { + if (BLI_smallhash_haskey(hash, (intptr_t)e2) || + BLI_smallhash_haskey(hash, (intptr_t)v)) + { + ok = 0; + break; + } + + BLI_array_append(verts, v); + BLI_array_append(edges, e2); + + BLI_smallhash_insert(hash, (intptr_t)e2, NULL); + + v = BM_edge_other_vert(e2, v); + e2 = i ? rotsys_prevedge(e2, v, edata, vdata) : rotsys_nextedge(e2, v, edata, vdata); + } while (e2 != starte && v != startv); + + BLI_smallhash_release(hash); + + if (!ok || BLI_array_count(edges) < 3) + continue; + + f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), TRUE); + if (!f) + continue; + } + } + + return 0; +} + +static void rotsys_make_consistent(BMesh *bm, EdgeData *edata, VertData *vdata) +{ + BMIter iter; + BMEdge *e; + BMVert *v, **stack = NULL; + BLI_array_declare(stack); + int i; + + for (i = 0; i < bm->totvert; i++) { + vdata[i].tag = 0; + } + + while (1) { + VertData *vd; + BMVert *startv = NULL; + float dis; + + v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + for (i = 0; i < bm->totvert; i++, BM_iter_step(&iter)) { + vd = vdata + BM_elem_index_get(v); + + if (vd->tag) + continue; + + if (!startv || dot_v3v3(vd->offco, vd->offco) > dis) { + dis = dot_v3v3(vd->offco, vd->offco); + startv = v; + } + } + + if (!startv) + break; + + vd = vdata + BM_elem_index_get(startv); + + BLI_array_empty(stack); + BLI_array_append(stack, startv); + + vd->tag = 1; + + while (BLI_array_count(stack)) { + v = BLI_array_pop(stack); + vd = vdata + BM_elem_index_get(v); + + if (!vd->e) + continue; + + e = vd->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + VertData *vd2 = vdata + BM_elem_index_get(v2); + + if (dot_v3v3(vd->no, vd2->no) < 0.0f + FLT_EPSILON * 2) { + rotsys_reverse(e, v2, edata, vdata); + mul_v3_fl(vd2->no, -1.0f); + } + + if (!vd2->tag) { + BLI_array_append(stack, v2); + vd2->tag = 1; + } + + e = rotsys_nextedge(e, v, edata, vdata); + } while (e != vd->e); + } + } + + BLI_array_free(stack); +} + +static void init_rotsys(BMesh *bm, EdgeData *edata, VertData *vdata) +{ + BMIter iter; + BMEdge *e; + BMEdge **edges = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BMVert *v; + /* BMVert **verts = NULL; */ + /* BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); */ /* UNUSE */ + int i; + +#define SIGN(n) ((n)<0.0f) + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMIter eiter; + float no[3], cent[3]; + int j, k = 0, totedge = 0; + + if (BM_elem_index_get(v) == -1) + continue; + + BLI_array_empty(edges); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + BLI_array_append(edges, e); + totedge++; + } + } + + copy_v3_v3(cent, v->co); + + zero_v3(no); + for (i = 0; i < totedge; i++) { + BMEdge *e1, *e2; + float cno[3], vec1[3], vec2[3]; + + e1 = edges[i]; + e2 = edges[(i + 1) % totedge]; + + sub_v3_v3v3(vec1, (BM_edge_other_vert(e1, v))->co, v->co); + sub_v3_v3v3(vec2, (BM_edge_other_vert(e2, v))->co, v->co); + + cross_v3_v3v3(cno, vec1, vec2); + normalize_v3(cno); + + if (i && dot_v3v3(cno, no) < 0.0f + FLT_EPSILON * 10) + mul_v3_fl(cno, -1.0f); + + add_v3_v3(no, cno); + normalize_v3(no); + } + + /* generate plane-flattened coordinate */ + for (i = 0; i < totedge; i++) { + BMEdge *e1; + BMVert *v2; + float cvec[3], vec1[3]; + + e1 = edges[i]; + v2 = BM_edge_other_vert(e1, v); + + sub_v3_v3v3(vec1, v2->co, v->co); + + cross_v3_v3v3(cvec, vec1, no); + cross_v3_v3v3(vec1, cvec, no); + normalize_v3(vec1); + + mul_v3_fl(vec1, len_v3v3(v2->co, v->co)); + add_v3_v3(vec1, v->co); + + copy_v3_v3(vdata[BM_elem_index_get(v2)].sco, vec1); + } + + BLI_srandom(0); + + /* first, ensure no 0 or 180 angles between adjacent + * (and that adjacent's adjacent) edges */ + for (i = 0, k = 0; i < totedge; i++) { + BMEdge *e1, *e2, *e3 = NULL; + BMVert *v1, *v2, *v3; + VertData *vd1, *vd2, *vd3; + float vec1[3], vec2[3], vec3[3], size; + int s1, s2, s3; + + if (totedge < 3) + continue; + + e1 = edges[(i + totedge - 1) % totedge]; + e2 = edges[i]; + e3 = edges[(i + 1) % totedge]; + + v1 = BM_edge_other_vert(e1, v); + v2 = BM_edge_other_vert(e2, v); + v3 = BM_edge_other_vert(e3, v); + + vd1 = vdata + BM_elem_index_get(v1); + vd2 = vdata + BM_elem_index_get(v2); + vd3 = vdata + BM_elem_index_get(v3); + + sub_v3_v3v3(vec1, vd1->sco, cent); + sub_v3_v3v3(vec2, vd2->sco, cent); + sub_v3_v3v3(vec3, vd3->sco, cent); + + size = (len_v3(vec1) + len_v3(vec3)) * 0.01f; + normalize_v3(vec1); normalize_v3(vec2); normalize_v3(vec3); + +#ifdef STRAIGHT +#undef STRAIGHT +#endif +#define STRAIGHT(vec11, vec22) (fabsf(dot_v3v3((vec11), (vec22))) > 1.0f - ((float)FLT_EPSILON * 1000.0f)) + + s1 = STRAIGHT(vec1, vec2); s2 = STRAIGHT(vec2, vec3); s3 = STRAIGHT(vec1, vec3); + + if (s1 || s2 || s3) { + copy_v3_v3(cent, v->co); + + for (j = 0; j < 3; j++) { + float fac = (BLI_frand() - 0.5f)*size; + cent[j] += fac; + } + + if (k < 2000) { + i = 0; + k++; + continue; + } + else { + k++; + continue; + } + + } + } + + copy_v3_v3(vdata[BM_elem_index_get(v)].offco, cent); + //copy_v3_v3(v->co, cent); + + /* now, sort edges so the triangle fan of all edges + * has a consistent normal. this is the same as + * sorting by polar coordinates along a group normal */ + for (j = 0; j < totedge; j++) { + for (i = 0; i < totedge; i++) { + BMEdge *e1, *e2, *e3 = NULL; + BMVert *v1, *v2, *v3; + VertData *vd1, *vd2, *vd3; + float vec1[3], vec2[3], vec3[3], n1[3], n2[3], n3[3]; + int s1, s2, s3; + + e1 = edges[(i + totedge - 1) % totedge]; + e2 = edges[i]; + e3 = edges[(i + 1) % totedge]; + + v1 = BM_edge_other_vert(e1, v); + v2 = BM_edge_other_vert(e2, v); + v3 = BM_edge_other_vert(e3, v); + + vd1 = vdata + BM_elem_index_get(v1); + vd2 = vdata + BM_elem_index_get(v2); + vd3 = vdata + BM_elem_index_get(v3); + + sub_v3_v3v3(vec1, vd1->sco, cent); + sub_v3_v3v3(vec2, vd2->sco, cent); + sub_v3_v3v3(vec3, vd3->sco, cent); + + cross_v3_v3v3(n1, vec1, vec2); + cross_v3_v3v3(n2, vec2, vec3); + cross_v3_v3v3(n3, vec1, vec3); + + /* Other way to determine if two vectors approach are (nearly) parallel: the + * cross product of the two vectors will approach zero */ + s1 = (dot_v3v3(n1, n1) < (0.0f + FLT_EPSILON * 10)); + s2 = (dot_v3v3(n2, n2) < (0.0f + FLT_EPSILON * 10)); + s3 = (totedge < 3) ? 0 : (dot_v3v3(n3, n3) < (0.0f + FLT_EPSILON * 10)); + + normalize_v3(n1); normalize_v3(n2); normalize_v3(n3); + + if (s1 || s2 || s3) { + fprintf(stderr, "%s: s1: %d, s2: %d, s3: %dx (bmesh internal error)\n", __func__, s1, s2, s3); + } + if (dot_v3v3(n1, n2) < 0.0f) { + if (dot_v3v3(n1, n3) >= 0.0f + FLT_EPSILON * 10) { + SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]); + } + else { + SWAP(BMEdge *, edges[(i + totedge - 1) % totedge], edges[(i + 1) % totedge]); + SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]); + } + } + } + } + +#undef STRAIGHT + + zero_v3(no); + + /* yay, edges are sorted */ + for (i = 0; i < totedge; i++) { + BMEdge *e1 = edges[i], *e2 = edges[(i + 1) % totedge]; + float eno[3]; + + normal_tri_v3(eno, BM_edge_other_vert(e1, v)->co, v->co, BM_edge_other_vert(e2, v)->co); + add_v3_v3(no, eno); + + rotsys_append_edge(edges[i], v, edata, vdata); + } + + normalize_v3(no); + copy_v3_v3(vdata[BM_elem_index_get(v)].no, no); + } + + /* now, make sure rotation system is topologically consistent + * (e.g. vert normals consistently point either inside or outside) */ + rotsys_make_consistent(bm, edata, vdata); + + //rotsys_fill_faces(bm, edata, vdata); + +#if 0 + /* create visualizing geometr */ + BMVert *lastv; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMVert *v2; + BMFace *f; + int totedge = BM_vert_edge_count(v); + + if (BM_elem_index_get(v) == -1) + continue; + + //cv = BM_vert_create(bm, cent, v); + //BM_elem_index_set(cv, -1); /* set_dirty! */ + i = 0; + e = vdata[BM_elem_index_get(v)].e; + lastv = NULL; + do { + BMEdge *e2; + BMVert *v2; + float f = ((float)i / (float)totedge) * 0.35 + 0.05; + float co[3]; + + if (!e) + break; + + if (!BM_edge_other_vert(e, v)) + continue; + + sub_v3_v3v3(co, (BM_edge_other_vert(e, v))->co, vdata[BM_elem_index_get(v)].offco); + mul_v3_fl(co, f); + add_v3_v3(co, vdata[BM_elem_index_get(v)].offco); + + v2 = BM_vert_create(bm, co, NULL); + BM_elem_index_set(v2, -1); /* set_dirty! */ + //BM_edge_create(bm, cv, v2, NULL, FALSE); + + BM_elem_select_set(bm, v2, TRUE); + if (lastv) { + e2 = BM_edge_create(bm, lastv, v2, NULL, FALSE); + BM_elem_select_set(bm, e2, TRUE); + } + + lastv = v2; + + e = rotsys_nextedge(e, v, edata, vdata); + i++; + } while (e != vdata[BM_elem_index_get(v)].e); + } +#endif + + BLI_array_free(edges); +} + +static PathBase *edge_pathbase_new(void) +{ + PathBase *pb = MEM_callocN(sizeof(PathBase), "PathBase"); + + pb->nodepool = BLI_mempool_create(sizeof(EPathNode), 1, 512, TRUE, FALSE); + pb->pathpool = BLI_mempool_create(sizeof(EPath), 1, 512, TRUE, FALSE); + + return pb; +} + +static void edge_pathbase_free(PathBase *pathbase) +{ + BLI_mempool_destroy(pathbase->nodepool); + BLI_mempool_destroy(pathbase->pathpool); + MEM_freeN(pathbase); +} + +static EPath *edge_copy_add_path(PathBase *pb, EPath *path, BMVert *appendv, BMEdge *e) +{ + EPath *path2; + EPathNode *node, *node2; + + path2 = BLI_mempool_alloc(pb->pathpool); + path2->nodes.first = path2->nodes.last = NULL; + path2->weight = 0.0f; + path2->group = path->group; + + for (node = path->nodes.first; node; node = node->next) { + node2 = BLI_mempool_alloc(pb->nodepool); + *node2 = *node; + BLI_addtail(&path2->nodes, node2); + } + + node2 = BLI_mempool_alloc(pb->nodepool); + node2->v = appendv; + node2->e = e; + node2->cure = NULL; + + BLI_addtail(&path2->nodes, node2); + + return path2; +} + +static EPath *edge_path_new(PathBase *pb, BMVert *start, BMEdge *starte) +{ + EPath *path; + EPathNode *node; + + path = BLI_mempool_alloc(pb->pathpool); + node = BLI_mempool_alloc(pb->nodepool); + + path->nodes.first = path->nodes.last = NULL; + + node->v = start; + node->e = starte; + node->cure = NULL; + + BLI_addtail(&path->nodes, node); + path->weight = 0.0f; + + return path; +} + +static float edge_weight_path(EPath *path, EdgeData *edata, VertData *UNUSED(vdata)) +{ + EPathNode *node, *first = path->nodes.first; + float w = 0.0; + + for (node = path->nodes.first; node; node = node->next) { + if (node->e && node != path->nodes.first) { + w += edata[BM_elem_index_get(node->e)].ftag; + if (node->prev) { + /* BMESH_TOD */ + (void)first; + //w += len_v3v3(node->v->co, first->e->v1->co) * 0.0001f; + //w += len_v3v3(node->v->co, first->e->v2->co) * 0.0001f; + } + } + + w += 1.0f; + } + + return w; +} + + +static void edge_free_path(PathBase *pathbase, EPath *path) +{ + EPathNode *node, *next; + + for (node = path->nodes.first; node; node = next) { + next = node->next; + BLI_mempool_free(pathbase->nodepool, node); + } + + BLI_mempool_free(pathbase->pathpool, path); +} + +static EPath *edge_find_shortest_path(BMesh *bm, BMOperator *op, BMEdge *edge, EdgeData *edata, + VertData *vdata, PathBase *pathbase, int group) +{ + BMEdge *e; + GHash *gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "createops find shortest path"); + BMVert *v1, *v2; + BMVert **verts = NULL; + BLI_array_staticdeclare(verts, 1024); + Heap *heap = BLI_heap_new(); + EPath *path = NULL, *path2; + BMVert *startv; + BMVert *endv; + EPathNode *node; + int i, use_restrict = BMO_slot_int_get(op, "use_restrict"); + + startv = edata[BM_elem_index_get(edge)].ftag ? edge->v2 : edge->v1; + endv = edata[BM_elem_index_get(edge)].ftag ? edge->v1 : edge->v2; + + path = edge_path_new(pathbase, startv, edge); + BLI_ghash_insert(gh, startv, NULL); + BLI_heap_insert(heap, path->weight, path); + path->group = group; + + while (BLI_heap_size(heap)) { + VertData *vd; + EPathNode *last; + BMFace *f = NULL; + + path = BLI_heap_popmin(heap); + last = path->nodes.last; + v1 = last->v; + + if (v1 == endv) { + /* make sure this path loop doesn't already exist */ + i = 0; + BLI_array_empty(verts); + for (i = 0, node = path->nodes.first; node; node = node->next, i++) { + BLI_array_growone(verts); + verts[i] = node->v; + } + + if (BM_face_exists(bm, verts, i, &f)) { + if (!BMO_elem_flag_test(bm, f, FACE_IGNORE)) { + BLI_ghash_remove(gh, endv, NULL, NULL); + continue; + } + } + break; + } + + vd = vdata + BM_elem_index_get(v1); + if (!vd->e) + continue; + + v2 = NULL; + while (1) { + if (!last->cure) { + last->cure = e = vdata[BM_elem_index_get(last->v)].e; + } + else { + last->cure = e = rotsys_nextedge(last->cure, last->v, edata, vdata); + if (last->cure == vdata[BM_elem_index_get(last->v)].e) { + v2 = NULL; + break; + } + } + + if (e == edge || !BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + v2 = BM_edge_other_vert(e, last->v); + + if (BLI_ghash_haskey(gh, v2)) { + v2 = NULL; + continue; + } + + if (use_restrict && BMO_slot_map_contains(bm, op, "restrict", e)) { + int group = BMO_slot_map_int_get(bm, op, "restrict", e); + + if (!(group & path->group)) { + v2 = NULL; + continue; + } + } + + break; + } + + if (!v2) { + if (path) { + edge_free_path(pathbase, path); + path = NULL; + } + continue; + } + + /* add path back into hea */ + BLI_heap_insert(heap, path->weight, path); + + /* put v2 in gh ma */ + BLI_ghash_insert(gh, v2, NULL); + + path2 = edge_copy_add_path(pathbase, path, v2, e); + path2->weight = edge_weight_path(path2, edata, vdata); + + BLI_heap_insert(heap, path2->weight, path2); + } + + if (path && ((EPathNode *)path->nodes.last)->v != endv) { + edge_free_path(pathbase, path); + path = NULL; + } + + BLI_array_free(verts); + BLI_heap_free(heap, NULL); + BLI_ghash_free(gh, NULL, NULL); + + return path; +} + +static int count_edge_faces(BMesh *bm, BMEdge *e) +{ + int i = 0; + BMLoop *l = e->l; + + if (!l) { + return 0; + } + + do { + if (!BMO_elem_flag_test(bm, l->f, FACE_IGNORE)) { + i++; + } + + l = l->radial_next; + } while (l != e->l); + + return i; +} + +void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter; + BMOIter siter; + BMFace *f; + BMEdge *e, *edge; + BMVert **verts = NULL; + BLI_array_declare(verts); + EPath *path; + EPathNode *node; + EdgeData *edata; + VertData *vdata; + BMEdge **edges = NULL; + PathBase *pathbase = edge_pathbase_new(); + BLI_array_declare(edges); + int use_restrict = BMO_slot_int_get(op, "use_restrict"); + int i, j, group = 0; + unsigned int winding[2]; /* accumulte winding directions for each edge which has a face */ + + if (!bm->totvert || !bm->totedge) + return; + + edata = MEM_callocN(sizeof(EdgeData)*bm->totedge, "EdgeData"); + vdata = MEM_callocN(sizeof(VertData)*bm->totvert, "VertData"); + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + BMO_slot_buffer_flag_enable(bm, op, "excludefaces", FACE_IGNORE, BM_FACE); + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, f, ELE_ORIG); + } + + i = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BM_elem_index_set(e, i); /* set_inline */ + + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) { + edata[i].tag = 2; + } + + i++; + } + bm->elem_index_dirty &= ~BM_EDGE; + + init_rotsys(bm, edata, vdata); + + while (1) { + edge = NULL; + group = 0; + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + /* if restrict is on, only start on faces in the restrict map */ + if (use_restrict && !BMO_slot_map_contains(bm, op, "restrict", e)) + continue; + + if (edata[BM_elem_index_get(e)].tag < 2) { + edge = e; + + if (use_restrict) { + int i = 0, j = 0, gi = 0; + + group = BMO_slot_map_int_get(bm, op, "restrict", e); + + for (i = 0; i < 30; i++) { + if (group & (1 << i)) { + j++; + gi = i; + + if (j - 1 == edata[BM_elem_index_get(e)].tag) { + break; + } + } + } + + group = (1 << gi); + } + + break; + } + } + + if (!edge) + break; + + edata[BM_elem_index_get(edge)].tag += 1; + + path = edge_find_shortest_path(bm, op, edge, edata, vdata, pathbase, group); + if (!path) + continue; + + winding[0] = winding[1] = 0; + + BLI_array_empty(edges); + BLI_array_empty(verts); + i = 0; + for (node = path->nodes.first; node; node = node->next) { + if (!node->next) + continue; + + e = BM_edge_exists(node->v, node->next->v); + + /* this should never happe */ + if (!e) + break; + + /* check on the winding */ + if (e->l) { + BMVert *test_v1, *test_v2; + /* we want to use the reverse winding to the existing order */ + BM_edge_ordered_verts(edge, &test_v2, &test_v1); + + /* edges vote on which winding wins out */ + winding[(test_v1 == node->v)]++; + } + + edata[BM_elem_index_get(e)].ftag++; + BLI_array_growone(edges); + edges[i++] = e; + + BLI_array_append(verts, node->v); + } + + BLI_array_growone(edges); + edges[i++] = edge; + edata[BM_elem_index_get(edge)].ftag++; + + for (j = 0; j < i; j++) { + if (count_edge_faces(bm, edges[j]) >= 2) { + edge_free_path(pathbase, path); + break; + } + } + + if (j != i) { + continue; + } + + if (i) { + BMVert *v1, *v2; + + /* to define the winding order must select first edge, + * otherwise we could leave this as-is */ + edge = edges[0]; + + /* if these are even it doesnt really matter what to do, + * with consistent geometry one will be zero, the choice is clear */ + if (winding[0] > winding[1]) { + v1 = verts[0]; + v2 = verts[1]; + } + else { + v1 = verts[1]; + v2 = verts[0]; + } + + f = BM_face_create_ngon(bm, v1, v2, edges, i, TRUE); + if (f && !BMO_elem_flag_test(bm, f, ELE_ORIG)) { + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (use_restrict) + BMO_slot_map_int_insert(bm, op, "faceout_groupmap", f, path->group); + } + + edge_free_path(pathbase, path); + } + + BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE); + + BLI_array_free(edges); + BLI_array_free(verts); + edge_pathbase_free(pathbase); + MEM_freeN(edata); + MEM_freeN(vdata); +} + +static BMEdge *edge_next(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMEdge *e2; + int i; + + for (i = 0; i < 2; i++) { + BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) { + if ( (BMO_elem_flag_test(bm, e2, EDGE_MARK)) && + (!BMO_elem_flag_test(bm, e2, EDGE_VIS)) && + (e2 != e)) + { + return e2; + } + } + } + + return NULL; +} + +void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e; + BMEdge **edges1 = NULL, **edges2 = NULL, **edges; + BLI_array_declare(edges1); + BLI_array_declare(edges2); + BLI_array_declare(edges); + int ok = 1; + int i, count; + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + /* validate that each edge has at most one other tagged edge in the + * disk cycle around each of it's vertices */ + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + for (i = 0; i < 2; i++) { + count = BMO_vert_edge_flags_count(bm, i ? e->v2 : e->v1, EDGE_MARK); + if (count > 2) { + ok = 0; + break; + } + } + + if (!ok) { + break; + } + } + + /* we don't have valid edge layouts, retur */ + if (!ok) { + return; + } + + /* find connected loops within the input edge */ + count = 0; + while (1) { + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + if (!BMO_elem_flag_test(bm, e, EDGE_VIS)) { + if ( BMO_vert_edge_flags_count(bm, e->v1, EDGE_MARK) == 1 || + BMO_vert_edge_flags_count(bm, e->v2, EDGE_MARK) == 1) + { + break; + } + } + } + + if (!e) { + break; + } + + if (!count) { + edges = edges1; + } + else if (count == 1) { + edges = edges2; + } + else { + break; + } + + i = 0; + while (e) { + BMO_elem_flag_enable(bm, e, EDGE_VIS); + BLI_array_growone(edges); + edges[i] = e; + + e = edge_next(bm, e); + i++; + } + + if (!count) { + edges1 = edges; + BLI_array_set_length(edges1, BLI_array_count(edges)); + } + else { + edges2 = edges; + BLI_array_set_length(edges2, BLI_array_count(edges)); + } + + BLI_array_empty(edges); + count++; + } + + if (edges1 && BLI_array_count(edges1) > 2 && + BM_edge_share_vert(edges1[0], edges1[BLI_array_count(edges1) - 1])) + { + if (edges2 && BLI_array_count(edges2) > 2 && + BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1])) + { + BLI_array_free(edges1); + BLI_array_free(edges2); + return; + } + else { + edges1 = edges2; + edges2 = NULL; + } + } + + if (edges2 && BLI_array_count(edges2) > 2 && + BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1])) + { + edges2 = NULL; + } + + /* two unconnected loops, connect the */ + if (edges1 && edges2) { + BMVert *v1, *v2, *v3, *v4; + + if (BLI_array_count(edges1) == 1) { + v1 = edges1[0]->v1; + v2 = edges1[0]->v2; + } + else { + if (BM_vert_in_edge(edges1[1], edges1[0]->v1)) + v1 = edges1[0]->v2; + else v1 = edges1[0]->v1; + + i = BLI_array_count(edges1) - 1; + if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1)) + v2 = edges1[i]->v2; + else v2 = edges1[i]->v1; + } + + if (BLI_array_count(edges2) == 1) { + v3 = edges2[0]->v1; + v4 = edges2[0]->v2; + } + else { + if (BM_vert_in_edge(edges2[1], edges2[0]->v1)) + v3 = edges2[0]->v2; + else v3 = edges2[0]->v1; + + i = BLI_array_count(edges2) - 1; + if (BM_vert_in_edge(edges2[i - 1], edges2[i]->v1)) + v4 = edges2[i]->v2; + else v4 = edges2[i]->v1; + } + + /* avoid sqrt for comparison */ + if (len_squared_v3v3(v1->co, v3->co) + len_squared_v3v3(v2->co, v4->co) > + len_squared_v3v3(v1->co, v4->co) + len_squared_v3v3(v2->co, v3->co)) + { + BMVert *v; + v = v3; + v3 = v4; + v4 = v; + } + + e = BM_edge_create(bm, v1, v3, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + e = BM_edge_create(bm, v2, v4, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + } + else if (edges1) { + BMVert *v1, *v2; + + if (BLI_array_count(edges1) > 1) { + if (BM_vert_in_edge(edges1[1], edges1[0]->v1)) + v1 = edges1[0]->v2; + else v1 = edges1[0]->v1; + + i = BLI_array_count(edges1) - 1; + if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1)) + v2 = edges1[i]->v2; + else v2 = edges1[i]->v1; + + e = BM_edge_create(bm, v1, v2, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + } + } + + BMO_slot_from_flag(bm, op, "edgeout", ELE_NEW, BM_EDGE); + + BLI_array_free(edges1); + BLI_array_free(edges2); +} + +/* this is essentially new fke */ +void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op) +{ + BMOperator op2; + BMOIter oiter; + BMIter iter; + BMHeader *h; + BMVert *v, *verts[4]; + BMEdge *e; + BMFace *f; + int totv = 0, tote = 0, totf = 0, amount; + + /* count number of each element type we were passe */ + BMO_ITER(h, &oiter, bm, op, "geom", BM_VERT|BM_EDGE|BM_FACE) { + switch (h->htype) { + case BM_VERT: totv++; break; + case BM_EDGE: tote++; break; + case BM_FACE: totf++; break; + } + + BMO_elem_flag_enable(bm, (BMElemF *)h, ELE_NEW); + } + + /* --- Support for Special Case --- + * where there is a contiguous edge ring with one isolated vertex. + * + * This example shows 2 edges created from 3 verts + * with 1 free standing vertex. Dotted lines denote the 2 edges that are created. + * + * note that this works for any sided shape. + * + * +--------+ + * | . + * | . + * | . + * | . + * +........+ <-- starts out free standing. + * + */ + + /* Here we check for consistancy and create 2 edges */ + if (totf == 0 && totv >= 4 && totv == tote + 2) { + /* find a free standing vertex and 2 endpoint verts */ + BMVert *v_free = NULL, *v_a = NULL, *v_b = NULL; + int ok = TRUE; + + + BMO_ITER(v, &oiter, bm, op, "geom", BM_VERT) { + /* count how many flagged edges this vertex uses */ + int tot_edges = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + if (BMO_elem_flag_test(bm, e, ELE_NEW)) { + tot_edges++; + if (tot_edges > 2) { + break; + } + } + } + + if (tot_edges == 0) { + /* only accept 1 free vert */ + if (v_free == NULL) v_free = v; + else ok = FALSE; /* only ever want one of these */ + } + else if (tot_edges == 1) { + if (v_a == NULL) v_a = v; + else if (v_b == NULL) v_b = v; + else ok = FALSE; /* only ever want 2 of these */ + } + else if (tot_edges == 2) { + /* do nothing, regular case */ + } + else { + ok = FALSE; /* if a vertex has 3+ edge users then cancel - this is only simple cases */ + } + + if (ok == FALSE) { + break; + } + } + + if (ok == TRUE && v_free && v_a && v_b) { + e = BM_edge_create(bm, v_free, v_a, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + + e = BM_edge_create(bm, v_free, v_b, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + } + } + /* --- end special case support, continue as normal --- */ + + + /* possible bug?, selecting 2 triangles and pressing F will make a quad rather then joining them, + * perhaps this should be looked into? - campbell */ + + /* call edgenet create */ + /* call edgenet prepare op so additional face creation cases wor */ + BMO_op_initf(bm, &op2, "edgenet_prepare edges=%fe", ELE_NEW); + BMO_op_exec(bm, &op2); + BMO_slot_buffer_flag_enable(bm, &op2, "edgeout", ELE_NEW, BM_EDGE); + BMO_op_finish(bm, &op2); + + BMO_op_initf(bm, &op2, "edgenet_fill edges=%fe", ELE_NEW); + BMO_op_exec(bm, &op2); + + /* return if edge net create did somethin */ + if (BMO_slot_buf_count(bm, &op2, "faceout")) { + BMO_slot_copy(&op2, op, "faceout", "faceout"); + BMO_op_finish(bm, &op2); + return; + } + + BMO_op_finish(bm, &op2); + + /* now call dissolve face */ + BMO_op_initf(bm, &op2, "dissolvefaces faces=%ff", ELE_NEW); + BMO_op_exec(bm, &op2); + + /* if we dissolved anything, then return */ + if (BMO_slot_buf_count(bm, &op2, "regionout")) { + BMO_slot_copy(&op2, op, "regionout", "faceout"); + BMO_op_finish(bm, &op2); + return; + } + + BMO_op_finish(bm, &op2); + + /* now, count how many verts we hav */ + amount = 0; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, ELE_NEW)) { + verts[amount] = v; + amount++; + + if (amount > 4) break; + } + } + + if (amount == 2) { + /* create edg */ + e = BM_edge_create(bm, verts[0], verts[1], NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_OUT); + } + else if (amount == 3) { + /* create triangl */ + BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], NULL, NULL, TRUE); + } + else if (amount == 4) { + f = NULL; + + /* the order of vertices can be anything, 6 cases to check */ + if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], verts[3], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[3]->co, verts[1]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[3], verts[1], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[1]->co, verts[3]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[1], verts[3], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[3]->co, verts[2]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[3], verts[2], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[2]->co, verts[1]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[2], verts[1], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[1]->co, verts[2]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[1], verts[2], NULL, TRUE); + } + else { + printf("cannot find nice quad from concave set of vertices\n"); + } + + if (f) BMO_elem_flag_enable(bm, f, ELE_OUT); + } +} diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c new file mode 100644 index 00000000000..05aead466d1 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -0,0 +1,559 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +#define FACE_MARK 1 +#define FACE_ORIG 2 +#define FACE_NEW 4 +#define EDGE_MARK 1 + +#define VERT_MARK 1 + +static int UNUSED_FUNCTION(check_hole_in_region)(BMesh *bm, BMFace *f) +{ + BMWalker regwalker; + BMIter liter2; + BMLoop *l2, *l3; + BMFace *f2; + + /* checks if there are any unmarked boundary edges in the face regio */ + + BMW_init(®walker, bm, BMW_ISLAND, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, + BMW_NIL_LAY); + f2 = BMW_begin(®walker, f); + for ( ; f2; f2 = BMW_step(®walker)) { + l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2); + for ( ; l2; l2 = BM_iter_step(&liter2)) { + l3 = bmesh_radial_nextloop(l2); + if ( BMO_elem_flag_test(bm, l3->f, FACE_MARK) != + BMO_elem_flag_test(bm, l2->f, FACE_MARK)) + { + if (!BMO_elem_flag_test(bm, l2->e, EDGE_MARK)) { + return FALSE; + } + } + } + } + BMW_end(®walker); + + return TRUE; +} + +void dissolvefaces_exec(BMesh *bm, BMOperator *op) +{ + BMOIter oiter; + BMFace *f, *f2 /* , *nf = NULL */; + BLI_array_declare(faces); + BLI_array_declare(regions); + BMFace ***regions = NULL; + BMFace **faces = NULL; + BMWalker regwalker; + int i; + + int use_verts = BMO_slot_int_get(op, "use_verts"); + + if (use_verts) { + /* tag verts that start out with only 2 edges, + * don't remove these later */ + BMIter viter; + BMVert *v; + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_vert_edge_count(v) == 2) { + BMO_elem_flag_disable(bm, v, VERT_MARK); + } + else { + BMO_elem_flag_enable(bm, v, VERT_MARK); + } + } + } + + BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_MARK, BM_FACE); + + /* collect region */ + BMO_ITER(f, &oiter, bm, op, "faces", BM_FACE) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) continue; + + BLI_array_empty(faces); + faces = NULL; /* forces different allocatio */ + + /* yay, walk */ + BMW_init(®walker, bm, BMW_ISLAND, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, + BMW_NIL_LAY); + + f2 = BMW_begin(®walker, f); + for ( ; f2; f2 = BMW_step(®walker)) { + BLI_array_append(faces, f2); + } + BMW_end(®walker); + + for (i = 0; i < BLI_array_count(faces); i++) { + f2 = faces[i]; + BMO_elem_flag_disable(bm, f2, FACE_MARK); + BMO_elem_flag_enable(bm, f2, FACE_ORIG); + } + + if (BMO_error_occurred(bm)) { + BMO_error_clear(bm); + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL); + goto cleanup; + } + + BLI_array_append(faces, NULL); + BLI_array_append(regions, faces); + } + + for (i = 0; i < BLI_array_count(regions); i++) { + int tot = 0; + + faces = regions[i]; + if (!faces[0]) { + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, + "Could not find boundary of dissolve region"); + goto cleanup; + } + + while (faces[tot]) + tot++; + + f = BM_faces_join(bm, faces, tot); + if (!f) { + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, + "Could not create merged face"); + goto cleanup; + } + + /* if making the new face failed (e.g. overlapping test) + * unmark the original faces for deletion */ + BMO_elem_flag_disable(bm, f, FACE_ORIG); + BMO_elem_flag_enable(bm, f, FACE_NEW); + + } + + BMO_op_callf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES); + + + if (use_verts) { + BMIter viter; + BMVert *v; + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + if (BM_vert_edge_count(v) == 2) { + BM_vert_collapse_edges(bm, v->e, v); + } + } + } + } + + if (BMO_error_occurred(bm)) goto cleanup; + + BMO_slot_from_flag(bm, op, "regionout", FACE_NEW, BM_FACE); + +cleanup: + /* free/cleanu */ + for (i = 0; i < BLI_array_count(regions); i++) { + if (regions[i]) MEM_freeN(regions[i]); + } + + BLI_array_free(regions); +} + +/* almost identical to dissolve edge, except it cleans up vertice */ +void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op) +{ + /* BMOperator fop; */ + BMOIter oiter; + BMIter iter; + BMVert *v, **verts = NULL; + BLI_array_declare(verts); + BMEdge *e; + /* BMFace *f; */ + int i; + + BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) { + if (BM_edge_face_count(e) == 2) { + BMO_elem_flag_enable(bm, e->v1, VERT_MARK); + BMO_elem_flag_enable(bm, e->v2, VERT_MARK); + + BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); + } + } + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK) && BM_vert_edge_count(v) == 2) { + BLI_array_append(verts, v); + } + } + + /* clean up extreneous 2-valence vertice */ + for (i = 0; i < BLI_array_count(verts); i++) { + if (verts[i]->e) { + BM_vert_collapse_edges(bm, verts[i]->e, verts[i]); + } + } + + BLI_array_free(verts); + + //BMO_op_initf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK); + //BMO_op_exec(bm, &fop); + + //BMO_slot_copy(op, &fop, "regionout", "regionout"); + + //BMO_op_finish(bm, &fop); +} + + +void dissolveedges_exec(BMesh *bm, BMOperator *op) +{ + /* might want to make this an option or mode - campbell */ + + /* BMOperator fop; */ + BMOIter eiter; + BMEdge *e; + + BMIter viter; + BMVert *v; + + int use_verts = BMO_slot_int_get(op, "use_verts"); + + if (use_verts) { + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_vert_edge_count(v) == 2) { + BMO_elem_flag_disable(bm, v, VERT_MARK); + } + else { + BMO_elem_flag_enable(bm, v, VERT_MARK); + } + } + } + + BMO_ITER(e, &eiter, bm, op, "edges", BM_EDGE) { + const int edge_face_count = BM_edge_face_count(e); + if (edge_face_count == 2) { + + /* join faces */ + BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); + } + } + + if (use_verts) { + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + if (BM_vert_edge_count(v) == 2) { + BM_vert_collapse_edges(bm, v->e, v); + } + } + } + } +} + +static int test_extra_verts(BMesh *bm, BMVert *v) +{ + BMIter iter, liter, iter2, iter3; + BMFace *f, *f2; + BMLoop *l; + BMEdge *e; + int found; + + /* test faces around verts for verts that would be wronly killed + * by dissolve faces. */ + f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&iter)) { + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&liter)) { + if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) { + /* if an edge around a vert is a boundary edge, + * then dissolve faces won't destroy it. + * also if it forms a boundary with one + * of the face region */ + found = FALSE; + e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, l->v); + for ( ; e; e = BM_iter_step(&iter2)) { + if (BM_edge_face_count(e) == 1) { + found = TRUE; + } + f2 = BM_iter_new(&iter3, bm, BM_FACES_OF_EDGE, e); + for ( ; f2; f2 = BM_iter_step(&iter3)) { + if (!BMO_elem_flag_test(bm, f2, FACE_MARK)) { + found = TRUE; + break; + } + } + if (found == TRUE) { + break; + } + } + if (found == FALSE) { + return FALSE; + } + } + } + } + + return TRUE; +} +void dissolveverts_exec(BMesh *bm, BMOperator *op) +{ + BMOpSlot *vinput; + BMIter iter, fiter; + BMVert *v; + BMFace *f; + /* int i; */ + + vinput = BMO_slot_get(op, "verts"); + BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_MARK, BM_VERT); + + for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + /* check if it's a two-valence ver */ + if (BM_vert_edge_count(v) == 2) { + + /* collapse the ver */ + BM_vert_collapse_faces(bm, v->e, v, 1.0f, TRUE); + continue; + } + + f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&fiter)) { + BMO_elem_flag_enable(bm, f, FACE_ORIG); + BMO_elem_flag_enable(bm, f, FACE_MARK); + } + + /* check if our additions to the input to face dissolve + * will destroy nonmarked vertices. */ + if (!test_extra_verts(bm, v)) { + f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&fiter)) { + if (BMO_elem_flag_test(bm, f, FACE_ORIG)) { + BMO_elem_flag_disable(bm, f, FACE_MARK); + BMO_elem_flag_disable(bm, f, FACE_ORIG); + } + } + } + else { + f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&fiter)) { + BMO_elem_flag_disable(bm, f, FACE_ORIG); + } + } + } + } + + BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_MARK); + if (BMO_error_occurred(bm)) { + const char *msg; + + BMO_error_get(bm, &msg, NULL); + BMO_error_clear(bm); + BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg); + } + + /* clean up any remainin */ + for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + if (!BM_vert_dissolve(bm, v)) { + BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL); + return; + } + } + } + +} + +/* this code is for cleaning up two-edged faces, it shall become + * it's own function one day */ +#if 0 +void dummy_exec(BMesh *bm, BMOperator *op) +{ + { + /* clean up two-edged face */ + /* basic idea is to keep joining 2-edged faces until their + * gone. this however relies on joining two 2-edged faces + * together to work, which doesn't */ + found3 = 1; + while (found3) { + found3 = 0; + for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) { + if (!BM_face_validate(bm, f, stderr)) { + printf("error.\n"); + } + + if (f->len == 2) { + //this design relies on join faces working + //with two-edged faces properly. + //commenting this line disables the + //outermost loop. + //found3 = 1; + found2 = 0; + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + fe = l->e; + for ( ; l; l = BM_iter_step(&liter)) { + f2 = BM_iter_new(&fiter, bm, + BM_FACES_OF_EDGE, l->e); + for ( ; f2; f2 = BM_iter_step(&fiter)) { + if (f2 != f) { + BM_faces_join_pair(bm, f, f2, l->e); + found2 = 1; + break; + } + } + if (found2) break; + } + + if (!found2) { + bmesh_kf(bm, f); + bmesh_ke(bm, fe); + } + } /* else if (f->len == 3) { + BMEdge *ed[3]; + BMVert *vt[3]; + BMLoop *lp[3]; + int i = 0; + + //check for duplicate edges + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&liter)) { + ed[i] = l->e; + lp[i] = l; + vt[i++] = l->v; + } + if (vt[0] == vt[1] || vt[0] == vt[2]) { + i += 1; + } + */ + } + } + if (oldlen == len) break; + oldlen = len; + } +} + +#endif + +/**/ +typedef struct DissolveElemWeight_t { + BMHeader *ele; + float weight; +} DissolveElemWeight_t; + +static int dissolve_elem_cmp(const void *a1, const void *a2) +{ + const struct DissolveElemWeight_t *d1 = a1, *d2 = a2; + + if (d1->weight > d2->weight) return 1; + else if (d1->weight < d2->weight) return -1; + return 0; +} + +void dissolvelimit_exec(BMesh *bm, BMOperator *op) +{ + BMOpSlot *einput = BMO_slot_get(op, "edges"); + BMOpSlot *vinput = BMO_slot_get(op, "verts"); + const float angle_max = (float)M_PI / 2.0f; + const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit")); + DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) * + sizeof(DissolveElemWeight_t), __func__); + int i, tot_found; + + /* --- first edges --- */ + + /* go through and split edge */ + for (i = 0, tot_found = 0; i < einput->len; i++) { + BMEdge *e = ((BMEdge **)einput->data.p)[i]; + const float angle = BM_edge_face_angle(bm, e); + + if (angle < angle_limit) { + weight_elems[i].ele = (BMHeader *)e; + weight_elems[i].weight = angle; + tot_found++; + } + else { + weight_elems[i].ele = NULL; + weight_elems[i].weight = angle_max; + } + } + + if (tot_found != 0) { + qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp); + + for (i = 0; i < tot_found; i++) { + BMEdge *e = (BMEdge *)weight_elems[i].ele; + /* check twice because cumulative effect could disolve over angle limit */ + if (BM_edge_face_angle(bm, e) < angle_limit) { + BMFace *nf = BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); /* join faces */ + + /* there may be some errors, we dont mind, just move on */ + if (nf == NULL) { + BMO_error_clear(bm); + } + } + } + } + + /* --- second verts --- */ + for (i = 0, tot_found = 0; i < vinput->len; i++) { + BMVert *v = ((BMVert **)vinput->data.p)[i]; + const float angle = BM_vert_edge_angle(bm, v); + + if (angle < angle_limit) { + weight_elems[i].ele = (BMHeader *)v; + weight_elems[i].weight = angle; + tot_found++; + } + else { + weight_elems[i].ele = NULL; + weight_elems[i].weight = angle_max; + } + } + + if (tot_found != 0) { + qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp); + + for (i = 0; i < tot_found; i++) { + BMVert *v = (BMVert *)weight_elems[i].ele; + /* check twice because cumulative effect could disolve over angle limit */ + if (BM_vert_edge_angle(bm, v) < angle_limit) { + BM_vert_collapse_edges(bm, v->e, v); /* join edges */ + } + } + } + + MEM_freeN(weight_elems); +} diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c new file mode 100644 index 00000000000..2e1c91d920c --- /dev/null +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -0,0 +1,512 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "bmesh.h" + +/* local flag define */ +#define DUPE_INPUT 1 /* input from operato */ +#define DUPE_NEW 2 +#define DUPE_DONE 4 +#define DUPE_MAPPED 8 + +/* + * COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * + */ +static BMVert *copy_vertex(BMesh *source_mesh, BMVert *source_vertex, BMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /* Create a new verte */ + target_vertex = BM_vert_create(target_mesh, source_vertex->co, NULL); + + /* Insert new vertex into the vert has */ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /* Copy attribute */ + BM_elem_attrs_copy(source_mesh, target_mesh, source_vertex, target_vertex); + + /* Set internal op flag */ + BMO_elem_flag_enable(target_mesh, target_vertex, DUPE_NEW); + + return target_vertex; +} + +/* + * COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * + */ +static BMEdge *copy_edge(BMOperator *op, BMesh *source_mesh, + BMEdge *source_edge, BMesh *target_mesh, + GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + BMFace *face; + BMIter fiter; + int rlen; + + /* see if any of the neighboring faces are + * not being duplicated. in that case, + * add it to the new/old map. */ + rlen = 0; + for (face = BM_iter_new(&fiter, source_mesh, BM_FACES_OF_EDGE, source_edge); + face; + face = BM_iter_step(&fiter)) + { + if (BMO_elem_flag_test(source_mesh, face, DUPE_INPUT)) { + rlen++; + } + } + + /* Lookup v1 and v2 */ + target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1); + target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2); + + /* Create a new edg */ + target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE); + + /* add to new/old edge map if necassar */ + if (rlen < 2) { + /* not sure what non-manifold cases of greater then three + * radial should do. */ + BMO_slot_map_ptr_insert(source_mesh, op, "boundarymap", + source_edge, target_edge); + } + + /* Insert new edge into the edge hash */ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /* Copy attributes */ + BM_elem_attrs_copy(source_mesh, target_mesh, source_edge, target_edge); + + /* Set internal op flags */ + BMO_elem_flag_enable(target_mesh, target_edge, DUPE_NEW); + + return target_edge; +} + +/* + * COPY FACE + * + * Copy an existing face from one bmesh to another. + */ + +static BMFace *copy_face(BMOperator *op, BMesh *source_mesh, + BMFace *source_face, BMesh *target_mesh, + BMVert **vtar, BMEdge **edar, GHash *vhash, GHash *ehash) +{ + /* BMVert *target_vert1, *target_vert2; */ /* UNUSED */ + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + BMIter iter, iter2; + int i; + + /* lookup the first and second vert */ +#if 0 /* UNUSED */ + target_vert1 = BLI_ghash_lookup(vhash, BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face)); + target_vert2 = BLI_ghash_lookup(vhash, BM_iter_step(&iter)); +#else + BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face); + BM_iter_step(&iter); +#endif + + /* lookup edge */ + for (i = 0, source_loop = BM_iter_new(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face); + source_loop; + source_loop = BM_iter_step(&iter), i++) + { + vtar[i] = BLI_ghash_lookup(vhash, source_loop->v); + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + } + + /* create new fac */ + target_face = BM_face_create(target_mesh, vtar, edar, source_face->len, FALSE); + BMO_slot_map_ptr_insert(source_mesh, op, + "facemap", source_face, target_face); + BMO_slot_map_ptr_insert(source_mesh, op, + "facemap", target_face, source_face); + + BM_elem_attrs_copy(source_mesh, target_mesh, source_face, target_face); + + /* mark the face for outpu */ + BMO_elem_flag_enable(target_mesh, target_face, DUPE_NEW); + + /* copy per-loop custom dat */ + BM_ITER(source_loop, &iter, source_mesh, BM_LOOPS_OF_FACE, source_face) { + BM_ITER(target_loop, &iter2, target_mesh, BM_LOOPS_OF_FACE, target_face) { + if (BLI_ghash_lookup(vhash, source_loop->v) == target_loop->v) { + BM_elem_attrs_copy(source_mesh, target_mesh, source_loop, target_loop); + break; + } + } + } + + return target_face; +} + +/* + * COPY MESH + * + * Internal Copy function. + */ +static void copy_mesh(BMOperator *op, BMesh *source, BMesh *target) +{ + + BMVert *v = NULL, *v2; + BMEdge *e = NULL; + BMFace *f = NULL; + + BLI_array_declare(vtar); + BLI_array_declare(edar); + BMVert **vtar = NULL; + BMEdge **edar = NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + + GHash *vhash; + GHash *ehash; + + /* initialize pointer hashe */ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops v"); + ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops e"); + + for (v = BM_iter_new(&verts, source, BM_VERTS_OF_MESH, source); v; v = BM_iter_step(&verts)) { + if ( BMO_elem_flag_test(source, v, DUPE_INPUT) && + !BMO_elem_flag_test(source, v, DUPE_DONE)) + { + BMIter iter; + int iso = 1; + + v2 = copy_vertex(source, v, target, vhash); + + BM_ITER(f, &iter, source, BM_FACES_OF_VERT, v) { + if (BMO_elem_flag_test(source, f, DUPE_INPUT)) { + iso = 0; + break; + } + } + + if (iso) { + BM_ITER(e, &iter, source, BM_EDGES_OF_VERT, v) { + if (BMO_elem_flag_test(source, e, DUPE_INPUT)) { + iso = 0; + break; + } + } + } + + if (iso) { + BMO_slot_map_ptr_insert(source, op, "isovertmap", v, v2); + } + + BMO_elem_flag_enable(source, v, DUPE_DONE); + } + } + + /* now we dupe all the edge */ + for (e = BM_iter_new(&edges, source, BM_EDGES_OF_MESH, source); e; e = BM_iter_step(&edges)) { + if ( BMO_elem_flag_test(source, e, DUPE_INPUT) && + !BMO_elem_flag_test(source, e, DUPE_DONE)) + { + /* make sure that verts are copie */ + if (!BMO_elem_flag_test(source, e->v1, DUPE_DONE)) { + copy_vertex(source, e->v1, target, vhash); + BMO_elem_flag_enable(source, e->v1, DUPE_DONE); + } + if (!BMO_elem_flag_test(source, e->v2, DUPE_DONE)) { + copy_vertex(source, e->v2, target, vhash); + BMO_elem_flag_enable(source, e->v2, DUPE_DONE); + } + /* now copy the actual edg */ + copy_edge(op, source, e, target, vhash, ehash); + BMO_elem_flag_enable(source, e, DUPE_DONE); + } + } + + /* first we dupe all flagged faces and their elements from sourc */ + for (f = BM_iter_new(&faces, source, BM_FACES_OF_MESH, source); f; f = BM_iter_step(&faces)) { + if (BMO_elem_flag_test(source, f, DUPE_INPUT)) { + /* vertex pas */ + for (v = BM_iter_new(&verts, source, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) { + if (!BMO_elem_flag_test(source, v, DUPE_DONE)) { + copy_vertex(source, v, target, vhash); + BMO_elem_flag_enable(source, v, DUPE_DONE); + } + } + + /* edge pas */ + for (e = BM_iter_new(&edges, source, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) { + if (!BMO_elem_flag_test(source, e, DUPE_DONE)) { + copy_edge(op, source, e, target, vhash, ehash); + BMO_elem_flag_enable(source, e, DUPE_DONE); + } + } + + /* ensure arrays are the right size */ + BLI_array_empty(vtar); + BLI_array_empty(edar); + + BLI_array_growitems(vtar, f->len); + BLI_array_growitems(edar, f->len); + + copy_face(op, source, f, target, vtar, edar, vhash, ehash); + BMO_elem_flag_enable(source, f, DUPE_DONE); + } + } + + /* free pointer hashe */ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + BLI_array_free(vtar); /* free vert pointer array */ + BLI_array_free(edar); /* free edge pointer array */ +} + +/* + * Duplicate Operator + * + * Duplicates verts, edges and faces of a mesh. + * + * INPUT SLOTS: + * + * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be duplicated + * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be duplicated + * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be duplicated + * + * OUTPUT SLOTS: + * + * BMOP_DUPE_VORIGINAL: Buffer containing pointers to the original mesh vertices + * BMOP_DUPE_EORIGINAL: Buffer containing pointers to the original mesh edges + * BMOP_DUPE_FORIGINAL: Buffer containing pointers to the original mesh faces + * BMOP_DUPE_VNEW: Buffer containing pointers to the new mesh vertices + * BMOP_DUPE_ENEW: Buffer containing pointers to the new mesh edges + * BMOP_DUPE_FNEW: Buffer containing pointers to the new mesh faces + * + */ + +void dupeop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *dupeop = op; + BMesh *bm2 = BMO_slot_ptr_get(op, "dest"); + + if (!bm2) + bm2 = bm; + + /* flag inpu */ + BMO_slot_buffer_flag_enable(bm, dupeop, "geom", DUPE_INPUT, BM_ALL); + + /* use the internal copy functio */ + copy_mesh(dupeop, bm, bm2); + + /* Outpu */ + /* First copy the input buffers to output buffers - original dat */ + BMO_slot_copy(dupeop, dupeop, "geom", "origout"); + + /* Now alloc the new output buffer */ + BMO_slot_from_flag(bm, dupeop, "newout", DUPE_NEW, BM_ALL); +} + +#if 0 /* UNUSED */ +/* executes the duplicate operation, feeding elements of + * type flag etypeflag and header flag flag to it. note, + * to get more useful information (such as the mapping from + * original to new elements) you should run the dupe op manually */ +void BMO_dupe_from_flag(BMesh *bm, int etypeflag, const char hflag) +{ + BMOperator dupeop; + + BMO_op_init(bm, &dupeop, "dupe"); + BMO_slot_from_hflag(bm, &dupeop, "geom", hflag, etypeflag); + + BMO_op_exec(bm, &dupeop); + BMO_op_finish(bm, &dupeop); +} +#endif + +/* + * Split Operator + * + * Duplicates verts, edges and faces of a mesh but also deletes the originals. + * + * INPUT SLOTS: + * + * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be split + * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be split + * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be split + * + * OUTPUT SLOTS: + * + * BMOP_DUPE_VOUTPUT: Buffer containing pointers to the split mesh vertices + * BMOP_DUPE_EOUTPUT: Buffer containing pointers to the split mesh edges + * BMOP_DUPE_FOUTPUT: Buffer containing pointers to the split mesh faces + */ + +#define SPLIT_INPUT 1 +void splitop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *splitop = op; + BMOperator dupeop; + BMOperator delop; + BMVert *v; + BMEdge *e; + BMFace *f; + BMIter iter, iter2; + int found; + + /* initialize our sub-operator */ + BMO_op_init(bm, &dupeop, "dupe"); + BMO_op_init(bm, &delop, "del"); + + BMO_slot_copy(splitop, &dupeop, "geom", "geom"); + BMO_op_exec(bm, &dupeop); + + BMO_slot_buffer_flag_enable(bm, splitop, "geom", SPLIT_INPUT, BM_ALL); + + /* make sure to remove edges and verts we don't need */ + for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); e; e = BM_iter_step(&iter)) { + found = 0; + f = BM_iter_new(&iter2, bm, BM_FACES_OF_EDGE, e); + for ( ; f; f = BM_iter_step(&iter2)) { + if (!BMO_elem_flag_test(bm, f, SPLIT_INPUT)) { + found = 1; + break; + } + } + if (!found) BMO_elem_flag_enable(bm, e, SPLIT_INPUT); + } + + for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) { + found = 0; + e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, v); + for ( ; e; e = BM_iter_step(&iter2)) { + if (!BMO_elem_flag_test(bm, e, SPLIT_INPUT)) { + found = 1; + break; + } + } + if (!found) BMO_elem_flag_enable(bm, v, SPLIT_INPUT); + + } + + /* connect outputs of dupe to delete, exluding keep geometr */ + BMO_slot_int_set(&delop, "context", DEL_FACES); + BMO_slot_from_flag(bm, &delop, "geom", SPLIT_INPUT, BM_ALL); + + BMO_op_exec(bm, &delop); + + /* now we make our outputs by copying the dupe output */ + BMO_slot_copy(&dupeop, splitop, "newout", "geomout"); + BMO_slot_copy(&dupeop, splitop, "boundarymap", + "boundarymap"); + BMO_slot_copy(&dupeop, splitop, "isovertmap", + "isovertmap"); + + /* cleanu */ + BMO_op_finish(bm, &delop); + BMO_op_finish(bm, &dupeop); +} + + +void delop_exec(BMesh *bm, BMOperator *op) +{ +#define DEL_INPUT 1 + + BMOperator *delop = op; + + /* Mark Buffer */ + BMO_slot_buffer_flag_enable(bm, delop, "geom", DEL_INPUT, BM_ALL); + + BMO_remove_tagged_context(bm, DEL_INPUT, BMO_slot_int_get(op, "context")); + +#undef DEL_INPUT +} + +/* + * Spin Operator + * + * Extrude or duplicate geometry a number of times, + * rotating and possibly translating after each step + */ + +void spinop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator dupop, extop; + float cent[3], dvec[3]; + float axis[3] = {0.0f, 0.0f, 1.0f}; + float q[4]; + float rmat[3][3]; + float phi, si; + int steps, dupli, a, usedvec; + + BMO_slot_vec_get(op, "cent", cent); + BMO_slot_vec_get(op, "axis", axis); + normalize_v3(axis); + BMO_slot_vec_get(op, "dvec", dvec); + usedvec = !is_zero_v3(dvec); + steps = BMO_slot_int_get(op, "steps"); + phi = BMO_slot_float_get(op, "ang") * (float)M_PI / (360.0f * steps); + dupli = BMO_slot_int_get(op, "dupli"); + + si = (float)sin(phi); + q[0] = (float)cos(phi); + q[1] = axis[0]*si; + q[2] = axis[1]*si; + q[3] = axis[2]*si; + quat_to_mat3(rmat, q); + + BMO_slot_copy(op, op, "geom", "lastout"); + for (a = 0; a < steps; a++) { + if (dupli) { + BMO_op_initf(bm, &dupop, "dupe geom=%s", op, "lastout"); + BMO_op_exec(bm, &dupop); + BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", + cent, rmat, &dupop, "newout"); + BMO_slot_copy(&dupop, op, "newout", "lastout"); + BMO_op_finish(bm, &dupop); + } + else { + BMO_op_initf(bm, &extop, "extrudefaceregion edgefacein=%s", + op, "lastout"); + BMO_op_exec(bm, &extop); + BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", + cent, rmat, &extop, "geomout"); + BMO_slot_copy(&extop, op, "geomout", "lastout"); + BMO_op_finish(bm, &extop); + } + + if (usedvec) + BMO_op_callf(bm, "translate vec=%v verts=%s", dvec, op, "lastout"); + } +} diff --git a/source/blender/bmesh/operators/bmo_edgesplit.c b/source/blender/bmesh/operators/bmo_edgesplit.c new file mode 100644 index 00000000000..7d719e1ddc9 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_edgesplit.c @@ -0,0 +1,426 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +typedef struct EdgeTag { + BMVert *newv1, *newv2; + BMEdge *newe1, *newe2; + int tag; +} EdgeTag; + +/* (EDGE_DEL == FACE_DEL) - this must be the case */ +#define EDGE_DEL 1 +#define EDGE_SEAM 2 +#define EDGE_MARK 4 +#define EDGE_RET1 8 +#define EDGE_RET2 16 + +#define FACE_DEL 1 +#define FACE_NEW 2 + +static BMFace *remake_face(BMesh *bm, EdgeTag *etags, BMFace *f, BMVert **verts, BMEdge **edges_tmp) +{ + BMIter liter1, liter2; + EdgeTag *et; + BMFace *f2; + BMLoop *l, *l2; + BMEdge *e; + BMVert *lastv1, *lastv2 /* , *v1, *v2 */ /* UNUSED */; + int i; + + /* we do final edge last */ + lastv1 = verts[f->len - 1]; + lastv2 = verts[0]; + /* v1 = verts[0]; */ /* UNUSED */ + /* v2 = verts[1]; */ /* UNUSED */ + for (i = 0; i < f->len - 1; i++) { + e = BM_edge_create(bm, verts[i], verts[i + 1], NULL, TRUE); + if (!e) { + return NULL; + } + edges_tmp[i] = e; + } + + edges_tmp[i] = BM_edge_create(bm, lastv1, lastv2, NULL, TRUE); + + f2 = BM_face_create(bm, verts, edges_tmp, f->len, FALSE); + if (!f2) { + return NULL; + } + + BM_elem_attrs_copy(bm, bm, f, f2); + + l = BM_iter_new(&liter1, bm, BM_LOOPS_OF_FACE, f); + l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2); + for ( ; l && l2; l = BM_iter_step(&liter1), l2 = BM_iter_step(&liter2)) { + BM_elem_attrs_copy(bm, bm, l, l2); + if (l->e != l2->e) { + /* set up data for figuring out the two sides of + * the split */ + + /* set edges index as dirty after running all */ + BM_elem_index_set(l2->e, BM_elem_index_get(l->e)); /* set_dirty! */ + et = &etags[BM_elem_index_get(l->e)]; + + if (!et->newe1) { + et->newe1 = l2->e; + } + else if (!et->newe2) { + et->newe2 = l2->e; + } + else { + /* Only two new edges should be created from each original edge + * for edge split operation */ + + //BLI_assert(et->newe1 == l2->e || et->newe2 == l2->e); + et->newe2 = l2->e; + } + + if (BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) { + BMO_elem_flag_enable(bm, l2->e, EDGE_SEAM); + } + + BM_elem_attrs_copy(bm, bm, l->e, l2->e); + } + + BMO_elem_flag_enable(bm, l->e, EDGE_MARK); + BMO_elem_flag_enable(bm, l2->e, EDGE_MARK); + } + + return f2; +} + +static void tag_out_edges(BMesh *bm, EdgeTag *etags, BMOperator *UNUSED(op)) +{ + EdgeTag *et; + BMIter iter; + BMLoop *l, *startl; + BMEdge *e; + BMVert *v; + int i, ok; + + ok = 0; + while (ok++ < 100000) { + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_SEAM)) + continue; + + et = &etags[BM_elem_index_get(e)]; + if (!et->tag && e->l) { + break; + } + } + + if (!e) { + break; + } + + /* ok we found an edge, part of a region of splits we need + * to identify. now walk along it */ + for (i = 0; i < 2; i++) { + l = e->l; + + v = i ? l->next->v : l->v; + + while (1) { + et = &etags[BM_elem_index_get(l->e)]; + if (et->newe1 == l->e) { + if (et->newe1) { + BMO_elem_flag_enable(bm, et->newe1, EDGE_RET1); + BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM); + } + if (et->newe2) { + BMO_elem_flag_enable(bm, et->newe2, EDGE_RET2); + BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM); + } + } + else { + if (et->newe1) { + BMO_elem_flag_enable(bm, et->newe1, EDGE_RET2); + BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM); + } + if (et->newe2) { + BMO_elem_flag_enable(bm, et->newe2, EDGE_RET1); + BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM); + } + } + + /* If the original edge was non-manifold edges, then it is + * possible l->e is not et->newe1 or et->newe2. So always clear + * the flag on l->e as well, to prevent infinite looping. */ + BMO_elem_flag_disable(bm, l->e, EDGE_SEAM); + + startl = l; + do { + l = BM_face_other_loop(l->e, l->f, v); + if (l == startl || BM_edge_face_count(l->e) != 2) { + break; + } + l = l->radial_next; + } while (l != startl && !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)); + + if (l == startl || !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) { + break; + } + + v = (l->v == v) ? l->next->v : l->v; + } + } + } +} + +void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op) +{ + EdgeTag *etags, *et; + BMIter iter, liter; + BMOIter siter; + BMFace *f, *f2; + BMLoop *l, *nextl, *prevl, *l2, *l3; + BMEdge *e, *e2; + BMVert *v, *v2, **verts = NULL; + BLI_array_declare(verts); + BMEdge **edges_tmp = NULL; + BLI_array_declare(edges_tmp); + int i, j; + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_SEAM, BM_EDGE); + + /* single marked edges unconnected to any other marked edges + * are illegal, go through and unmark them */ + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + for (i = 0; i < 2; i++) { + BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) { + if (e != e2 && BMO_elem_flag_test(bm, e2, EDGE_SEAM)) { + break; + } + } + if (e2) { + break; + } + } + + if (!e2) { + BMO_elem_flag_disable(bm, e, EDGE_SEAM); + } + } + + etags = MEM_callocN(sizeof(EdgeTag)*bm->totedge, "EdgeTag"); + + BM_mesh_elem_index_ensure(bm, BM_EDGE); + +#ifdef ETV +# undef ETV +#endif +#ifdef SETETV +# undef SETETV +#endif + +#define ETV(et, v, l) (l->e->v1 == v ? et->newv1 : et->newv2) +#define SETETV(et, v, l, vs) l->e->v1 == v ? (et->newv1 = vs) : (et->newv2 = vs) + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + + if (BMO_elem_flag_test(bm, f, FACE_NEW)) { + continue; + } + + BLI_array_empty(verts); + BLI_array_growitems(verts, f->len); + memset(verts, 0, sizeof(BMVert *) * f->len); + + /* this is passed onto remake_face() so it doesnt need to allocate + * a new array on each call. */ + BLI_array_empty(edges_tmp); + BLI_array_growitems(edges_tmp, f->len); + + i = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (!BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) { + if (!verts[i]) { + + et = &etags[BM_elem_index_get(l->e)]; + if (ETV(et, l->v, l)) { + verts[i] = ETV(et, l->v, l); + } + else + { + verts[i] = l->v; + } + } + i++; + continue; + } + + nextl = l->next; + prevl = l->prev; + + for (j = 0; j < 2; j++) { + /* correct as long as i & j dont change during the loop */ + const int fv_index = j ? (i + 1) % f->len : i; /* face vert index */ + l2 = j ? nextl : prevl; + v = j ? l2->v : l->v; + + if (BMO_elem_flag_test(bm, l2->e, EDGE_SEAM)) { + if (verts[fv_index] == NULL) { + /* make unique vert here for this face only */ + v2 = BM_vert_create(bm, v->co, v); + verts[fv_index] = v2; + } + else { + v2 = verts[fv_index]; + } + } + else { + /* generate unique vert for non-seam edge(s) + * around the manifold vert fan if necassary */ + + /* first check that we have two seam edges + * somewhere within this fa */ + l3 = l2; + do { + if (BM_edge_face_count(l3->e) != 2) { + /* if we hit a boundary edge, tag + * l3 as null so we know to disconnect + * it */ + if (BM_edge_face_count(l3->e) == 1) { + l3 = NULL; + } + break; + } + + l3 = l3->radial_next; + l3 = BM_face_other_loop(l3->e, l3->f, v); + } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM)); + + if (l3 == NULL || (BMO_elem_flag_test(bm, l3->e, EDGE_SEAM) && l3->e != l->e)) { + et = &etags[BM_elem_index_get(l2->e)]; + if (ETV(et, v, l2) == NULL) { + v2 = BM_vert_create(bm, v->co, v); + + l3 = l2; + do { + SETETV(et, v, l3, v2); + if (BM_edge_face_count(l3->e) != 2) { + break; + } + + l3 = l3->radial_next; + l3 = BM_face_other_loop(l3->e, l3->f, v); + + et = &etags[BM_elem_index_get(l3->e)]; + } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM)); + } + else { + v2 = ETV(et, v, l2); + } + + verts[fv_index] = v2; + } + else { + verts[fv_index] = v; + } + } + } + + i++; + } + + /* debugging code, quick way to find the face/vert combination + * which is failing assuming quads start planer - campbell */ +#if 0 + if (f->len == 4) { + float no1[3]; + float no2[3]; + float angle_error; + printf(" ** found QUAD\n"); + normal_tri_v3(no1, verts[0]->co, verts[1]->co, verts[2]->co); + normal_tri_v3(no2, verts[0]->co, verts[2]->co, verts[3]->co); + if ((angle_error = angle_v3v3(no1, no2)) > 0.05) { + printf(" ERROR %.4f\n", angle_error); + print_v3("0", verts[0]->co); + print_v3("1", verts[1]->co); + print_v3("2", verts[2]->co); + print_v3("3", verts[3]->co); + + } + } + else { + printf(" ** fount %d len face\n", f->len); + } +#endif + + f2 = remake_face(bm, etags, f, verts, edges_tmp); + if (!f2) { + continue; + } + + BMO_elem_flag_enable(bm, f, FACE_DEL); + BMO_elem_flag_enable(bm, f2, FACE_NEW); + } + + /* remake_face() sets invalid indecies, + * likely these will be corrected on operator exit anyway */ + bm->elem_index_dirty &= ~BM_EDGE; + + /* cant call the operator because 'tag_out_edges' + * relies on original index values, from before editing geometry */ + +#if 0 + BMO_op_callf(bm, "del geom=%ff context=%i", FACE_DEL, DEL_ONLYFACES); +#else + BMO_remove_tagged_context(bm, FACE_DEL, DEL_ONLYFACES); +#endif + + /* test EDGE_MARK'd edges if we need to delete them, EDGE_MARK + * is set in remake_face */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + if (!e->l) { + BMO_elem_flag_enable(bm, e, EDGE_DEL); + } + } + } + +#if 0 + BMO_op_callf(bm, "del geom=%fe context=%i", EDGE_DEL, DEL_EDGES); +#else + BMO_remove_tagged_context(bm, EDGE_DEL, DEL_EDGES); +#endif + + tag_out_edges(bm, etags, op); + BMO_slot_from_flag(bm, op, "edgeout1", EDGE_RET1, BM_EDGE); + BMO_slot_from_flag(bm, op, "edgeout2", EDGE_RET2, BM_EDGE); + + BLI_array_free(verts); + BLI_array_free(edges_tmp); + if (etags) MEM_freeN(etags); +} + +#undef ETV +#undef SETETV diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c new file mode 100644 index 00000000000..b6a87e604ec --- /dev/null +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -0,0 +1,591 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +#define EXT_INPUT 1 +#define EXT_KEEP 2 +#define EXT_DEL 4 + +#define VERT_MARK 1 +#define EDGE_MARK 1 +#define FACE_MARK 1 +#define VERT_NONMAN 2 +#define EDGE_NONMAN 2 + +void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter liter, liter2; + BMFace *f, *f2, *f3; + BMLoop *l, *l2, *l3, *l4, *l_tmp; + BMEdge **edges = NULL, *e, *laste; + BMVert *v, *lastv, *firstv; + BLI_array_declare(edges); + int i; + + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + BLI_array_empty(edges); + i = 0; + firstv = lastv = NULL; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BLI_array_growone(edges); + + v = BM_vert_create(bm, l->v->co, l->v); + + if (lastv) { + e = BM_edge_create(bm, lastv, v, l->e, FALSE); + edges[i++] = e; + } + + lastv = v; + laste = l->e; + if (!firstv) firstv = v; + } + + BLI_array_growone(edges); + e = BM_edge_create(bm, v, firstv, laste, FALSE); + edges[i++] = e; + + BMO_elem_flag_enable(bm, f, EXT_DEL); + + f2 = BM_face_create_ngon(bm, firstv, BM_edge_other_vert(edges[0], firstv), edges, f->len, FALSE); + if (!f2) { + BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Extrude failed; could not create face"); + BLI_array_free(edges); + return; + } + + BMO_elem_flag_enable(bm, f2, EXT_KEEP); + BM_elem_attrs_copy(bm, bm, f, f2); + + l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2); + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_elem_attrs_copy(bm, bm, l, l2); + + l3 = l->next; + l4 = l2->next; + + f3 = BM_face_create_quad_tri(bm, l3->v, l4->v, l2->v, l->v, f, FALSE); + + l_tmp = BM_FACE_FIRST_LOOP(f3); + + BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next; + BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next; + BM_elem_attrs_copy(bm, bm, l, l_tmp); l_tmp = l_tmp->next; + BM_elem_attrs_copy(bm, bm, l, l_tmp); + + l2 = BM_iter_step(&liter2); + } + } + + BLI_array_free(edges); + + BMO_op_callf(bm, "del geom=%ff context=%d", EXT_DEL, DEL_ONLYFACES); + BMO_slot_from_flag(bm, op, "faceout", EXT_KEEP, BM_FACE); +} + +void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMOperator dupeop; + BMVert *v1, *v2, *v3, *v4; + BMEdge *e, *e2; + BMFace *f; + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + BMO_elem_flag_enable(bm, e, EXT_INPUT); + BMO_elem_flag_enable(bm, e->v1, EXT_INPUT); + BMO_elem_flag_enable(bm, e->v2, EXT_INPUT); + } + + BMO_op_initf(bm, &dupeop, "dupe geom=%fve", EXT_INPUT); + BMO_op_exec(bm, &dupeop); + + e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0); + for ( ; e; e = BMO_iter_step(&siter)) { + e2 = BMO_iter_map_value(&siter); + e2 = *(BMEdge **)e2; + + if (e->l && e->v1 != e->l->v) { + v1 = e->v1; + v2 = e->v2; + v3 = e2->v2; + v4 = e2->v1; + } + else { + v1 = e2->v1; + v2 = e2->v2; + v3 = e->v2; + v4 = e->v1; + } + /* not sure what to do about example face, pass NULL for now */ + f = BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE); + + if (BMO_elem_flag_test(bm, e, EXT_INPUT)) + e = e2; + + BMO_elem_flag_enable(bm, f, EXT_KEEP); + BMO_elem_flag_enable(bm, e, EXT_KEEP); + BMO_elem_flag_enable(bm, e->v1, EXT_KEEP); + BMO_elem_flag_enable(bm, e->v2, EXT_KEEP); + + } + + BMO_op_finish(bm, &dupeop); + + BMO_slot_from_flag(bm, op, "geomout", EXT_KEEP, BM_ALL); +} + +void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMVert *v, *dupev; + BMEdge *e; + + v = BMO_iter_new(&siter, bm, op, "verts", BM_VERT); + for ( ; v; v = BMO_iter_step(&siter)) { + dupev = BM_vert_create(bm, v->co, v); + + e = BM_edge_create(bm, v, dupev, NULL, FALSE); + + BMO_elem_flag_enable(bm, e, EXT_KEEP); + BMO_elem_flag_enable(bm, dupev, EXT_KEEP); + } + + BMO_slot_from_flag(bm, op, "vertout", EXT_KEEP, BM_VERT); + BMO_slot_from_flag(bm, op, "edgeout", EXT_KEEP, BM_EDGE); +} + +void extrude_edge_context_exec(BMesh *bm, BMOperator *op) +{ + BMOperator dupeop, delop; + BMOIter siter; + BMIter iter, fiter, viter; + BMEdge *e, *newedge; + BMLoop *l, *l2; + BMVert *verts[4], *v, *v2; + BMFace *f; + int rlen, found, fwd, delorig = 0; + + /* initialize our sub-operators */ + BMO_op_init(bm, &dupeop, "dupe"); + + BMO_slot_buffer_flag_enable(bm, op, "edgefacein", EXT_INPUT, BM_EDGE|BM_FACE); + + /* if one flagged face is bordered by an unflagged face, then we delete + * original geometry unless caller explicitly asked to keep it. */ + if (!BMO_slot_int_get(op, "alwayskeeporig")) { + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) continue; + + found = 0; + f = BM_iter_new(&fiter, bm, BM_FACES_OF_EDGE, e); + for (rlen = 0; f; f = BM_iter_step(&fiter), rlen++) { + if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) { + found = 1; + delorig = 1; + break; + } + } + + if (!found && (rlen > 1)) BMO_elem_flag_enable(bm, e, EXT_DEL); + } + } + + /* calculate verts to delet */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + found = 0; + + BM_ITER(e, &viter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) { + found = 1; + break; + } + } + + BM_ITER(f, &viter, bm, BM_FACES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) { + found = 1; + break; + } + } + + if (!found) { + BMO_elem_flag_enable(bm, v, EXT_DEL); + } + } + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, f, EXT_INPUT)) + BMO_elem_flag_enable(bm, f, EXT_DEL); + } + + if (delorig) { + BMO_op_initf(bm, &delop, "del geom=%fvef context=%d", + EXT_DEL, DEL_ONLYTAGGED); + } + + BMO_slot_copy(op, &dupeop, "edgefacein", "geom"); + BMO_op_exec(bm, &dupeop); + + if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT)) + bm->act_face = BMO_slot_map_ptr_get(bm, &dupeop, "facemap", bm->act_face); + + if (delorig) BMO_op_exec(bm, &delop); + + /* if not delorig, reverse loops of original face */ + if (!delorig) { + for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) { + if (BMO_elem_flag_test(bm, f, EXT_INPUT)) { + BM_face_normal_flip(bm, f); + } + } + } + + BMO_slot_copy(&dupeop, op, "newout", "geomout"); + e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0); + for ( ; e; e = BMO_iter_step(&siter)) { + if (BMO_slot_map_contains(bm, op, "exclude", e)) continue; + + newedge = BMO_iter_map_value(&siter); + newedge = *(BMEdge **)newedge; + if (!newedge) continue; + + /* orient loop to give same normal as a loop of newedge + * if it exists (will be an extruded face), + * else same normal as a loop of e, if it exists */ + if (!newedge->l) + fwd = !e->l || !(e->l->v == e->v1); + else + fwd = (newedge->l->v == newedge->v1); + + + if (fwd) { + verts[0] = e->v1; + verts[1] = e->v2; + verts[2] = newedge->v2; + verts[3] = newedge->v1; + } + else { + verts[3] = e->v1; + verts[2] = e->v2; + verts[1] = newedge->v2; + verts[0] = newedge->v1; + } + + /* not sure what to do about example face, pass NULL for now */ + f = BM_face_create_quad_tri_v(bm, verts, 4, NULL, FALSE); + + /* copy attribute */ + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&iter)) { + if (l->e != e && l->e != newedge) continue; + l2 = l->radial_next; + + if (l2 == l) { + l2 = newedge->l; + BM_elem_attrs_copy(bm, bm, l2->f, l->f); + + BM_elem_attrs_copy(bm, bm, l2, l); + l2 = l2->next; + l = l->next; + BM_elem_attrs_copy(bm, bm, l2, l); + } + else { + BM_elem_attrs_copy(bm, bm, l2->f, l->f); + + /* copy dat */ + if (l2->v == l->v) { + BM_elem_attrs_copy(bm, bm, l2, l); + l2 = l2->next; + l = l->next; + BM_elem_attrs_copy(bm, bm, l2, l); + } + else { + l2 = l2->next; + BM_elem_attrs_copy(bm, bm, l2, l); + l2 = l2->prev; + l = l->next; + BM_elem_attrs_copy(bm, bm, l2, l); + } + } + } + } + + /* link isolated vert */ + v = BMO_iter_new(&siter, bm, &dupeop, "isovertmap", 0); + for ( ; v; v = BMO_iter_step(&siter)) { + v2 = *((void **)BMO_iter_map_value(&siter)); + BM_edge_create(bm, v, v2, v->e, TRUE); + } + + /* cleanu */ + if (delorig) BMO_op_finish(bm, &delop); + BMO_op_finish(bm, &dupeop); +} + +/* + * Compute higher-quality vertex normals used by solidify. + * Only considers geometry in the marked solidify region. + * Note that this does not work so well for non-manifold + * regions. + */ +static void calc_solidify_normals(BMesh *bm) +{ + BMIter viter, eiter, fiter; + BMVert *v; + BMEdge *e; + BMFace *f, *f1, *f2; + float edge_normal[3]; + int i; + + /* can't use BM_edge_face_count because we need to count only marked faces */ + int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__); + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + } + + BM_mesh_elem_index_ensure(bm, BM_EDGE); + + BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) { + continue; + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_FACE, f) { + + /* And mark all edges and vertices on the + * marked faces */ + BMO_elem_flag_enable(bm, e, EDGE_MARK); + BMO_elem_flag_enable(bm, e->v1, VERT_MARK); + BMO_elem_flag_enable(bm, e->v2, VERT_MARK); + edge_face_count[BM_elem_index_get(e)]++; + } + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + i = edge_face_count[BM_elem_index_get(e)]++; + + if (i == 0 || i > 2) { + /* Edge & vertices are non-manifold even when considering + * only marked faces */ + BMO_elem_flag_enable(bm, e, EDGE_NONMAN); + BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN); + BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN); + } + } + MEM_freeN(edge_face_count); + edge_face_count = NULL; /* dont re-use */ + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_vert_is_manifold(bm, v)) { + BMO_elem_flag_enable(bm, v, VERT_NONMAN); + continue; + } + + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + zero_v3(v->no); + } + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) { + + /* If the edge is not part of a the solidify region + * its normal should not be considered */ + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + /* If the edge joins more than two marked faces high + * quality normal computation won't work */ + if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) { + continue; + } + + f1 = f2 = NULL; + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + if (BMO_elem_flag_test(bm, f, FACE_MARK)) { + if (f1 == NULL) { + f1 = f; + } + else { + BLI_assert(f2 == NULL); + f2 = f; + } + } + } + + BLI_assert(f1 != NULL); + + if (f2 != NULL) { + const float angle = angle_normalized_v3v3(f1->no, f2->no); + + if (angle > 0.0f) { + /* two faces using this edge, calculate the edge normal + * using the angle between the faces as a weighting */ + add_v3_v3v3(edge_normal, f1->no, f2->no); + normalize_v3(edge_normal); + mul_v3_fl(edge_normal, angle); + } + else { + /* can't do anything useful here! + * Set the face index for a vert incase it gets a zero normal */ + BM_elem_flag_disable(e->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e->v2, BM_ELEM_TAG); + continue; + } + } + else { + /* only one face attached to that edge */ + /* an edge without another attached- the weight on this is + * undefined, M_PI / 2 is 90d in radians and that seems good enough */ + copy_v3_v3(edge_normal, f1->no); + mul_v3_fl(edge_normal, M_PI / 2); + } + + add_v3_v3(e->v1->no, edge_normal); + add_v3_v3(e->v2->no, edge_normal); + } + + /* normalize accumulated vertex normal */ + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, v, VERT_MARK)) { + continue; + } + + if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) { + /* use standard normals for vertices connected to non-manifold edges */ + BM_vert_normal_update(bm, v); + } + else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) { + /* exceptional case, totally flat. use the normal + * of any marked face around the vertex */ + BM_ITER(f, &fiter, bm, BM_FACES_OF_VERT, v) { + if (BMO_elem_flag_test(bm, f, FACE_MARK)) { + break; + } + } + copy_v3_v3(v->no, f->no); + } + } +} + +static void solidify_add_thickness(BMesh *bm, float dist) +{ + BMFace *f; + BMVert *v; + BMLoop *l; + BMIter iter, loopIter; + float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */ + float *vert_accum = vert_angles + bm->totvert; + float angle; + int i, index; + float maxdist = dist * sqrtf(3.0f); + + /* array for passing verts to angle_poly_v3 */ + float **verts = NULL; + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + /* array for receiving angles from angle_poly_v3 */ + float *angles = NULL; + BLI_array_staticdeclare(angles, BM_NGON_STACK_SIZE); + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) { + continue; + } + + BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) { + BLI_array_append(verts, l->v->co); + BLI_array_growone(angles); + } + + angle_poly_v3(angles, (const float **)verts, f->len); + + i = 0; + BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) { + v = l->v; + index = BM_elem_index_get(v); + angle = angles[i]; + vert_accum[index] += angle; + vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * angle; + i++; + } + + BLI_array_empty(verts); + BLI_array_empty(angles); + } + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + index = BM_elem_index_get(v); + if (vert_accum[index]) { /* zero if unselected */ + float vdist = MIN2(maxdist, dist * vert_angles[index] / vert_accum[index]); + madd_v3_v3fl(v->co, v->no, vdist); + } + } + + MEM_freeN(vert_angles); +} + +void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op) +{ + BMOperator extrudeop; + BMOperator reverseop; + float thickness; + + thickness = BMO_slot_float_get(op, "thickness"); + + /* Flip original faces (so the shell is extruded inward) */ + BMO_op_init(bm, &reverseop, "reversefaces"); + BMO_slot_copy(op, &reverseop, "geom", "faces"); + BMO_op_exec(bm, &reverseop); + BMO_op_finish(bm, &reverseop); + + /* Extrude the region */ + BMO_op_initf(bm, &extrudeop, "extrudefaceregion alwayskeeporig=%i", TRUE); + BMO_slot_copy(op, &extrudeop, "geom", "edgefacein"); + BMO_op_exec(bm, &extrudeop); + + /* Push the verts of the extruded faces inward to create thickness */ + BMO_slot_buffer_flag_enable(bm, &extrudeop, "geomout", FACE_MARK, BM_FACE); + calc_solidify_normals(bm); + solidify_add_thickness(bm, thickness); + + BMO_slot_copy(&extrudeop, op, "geomout", "geomout"); + + BMO_op_finish(bm, &extrudeop); +} diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c new file mode 100644 index 00000000000..921c579447b --- /dev/null +++ b/source/blender/bmesh/operators/bmo_join_triangles.c @@ -0,0 +1,373 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +/* + * JOIN_TRIANGLES.C + * + * utility bmesh operators, e.g. transform, + * translate, rotate, scale, etc. + * + */ + +/* Bitflags for edges */ +#define T2QDELETE 1 +#define T2QCOMPLEX 2 +#define T2QJOIN 4 + +/* assumes edges are validated before reaching this poin */ +static float measure_facepair(BMesh *UNUSED(bm), BMVert *v1, BMVert *v2, + BMVert *v3, BMVert *v4, float limit) +{ + /* gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would mak */ + /* Note: this is more complicated than it needs to be and should be cleaned up.. */ + float n1[3], n2[3], measure = 0.0f, angle1, angle2, diff; + float edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3]; + float minarea, maxarea, areaA, areaB; + + /* First Test: Normal differenc */ + normal_tri_v3(n1, v1->co, v2->co, v3->co); + normal_tri_v3(n2, v1->co, v3->co, v4->co); + + if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle1 = 0.0f; + else angle1 = angle_v3v3(n1, n2); + + normal_tri_v3(n1, v2->co, v3->co, v4->co); + normal_tri_v3(n2, v4->co, v1->co, v2->co); + + if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle2 = 0.0f; + else angle2 = angle_v3v3(n1, n2); + + measure += (angle1 + angle2) * 0.5f; + if (measure > limit) { + return measure; + } + + /* Second test: Colinearit */ + sub_v3_v3v3(edgeVec1, v1->co, v2->co); + sub_v3_v3v3(edgeVec2, v2->co, v3->co); + sub_v3_v3v3(edgeVec3, v3->co, v4->co); + sub_v3_v3v3(edgeVec4, v4->co, v1->co); + + /* a completely skinny face is 'pi' after halving */ + diff = 0.25f * (fabsf(angle_v3v3(edgeVec1, edgeVec2) - (float)M_PI_2) + + fabsf(angle_v3v3(edgeVec2, edgeVec3) - (float)M_PI_2) + + fabsf(angle_v3v3(edgeVec3, edgeVec4) - (float)M_PI_2) + + fabsf(angle_v3v3(edgeVec4, edgeVec1) - (float)M_PI_2)); + + if (!diff) { + return 0.0; + } + + measure += diff; + if (measure > limit) { + return measure; + } + + /* Third test: Concavit */ + areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co); + areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co); + + if (areaA <= areaB) minarea = areaA; + else minarea = areaB; + + if (areaA >= areaB) maxarea = areaA; + else maxarea = areaB; + + if (!maxarea) measure += 1; + else measure += (1 - (minarea / maxarea)); + + return measure; +} + +#define T2QUV_LIMIT 0.005f +#define T2QCOL_LIMIT 3 + +static int compareFaceAttribs(BMesh *bm, BMEdge *e, int douvs, int dovcols) +{ + MTexPoly *tp1, *tp2; + MLoopCol *lcol1, *lcol2, *lcol3, *lcol4; + MLoopUV *luv1, *luv2, *luv3, *luv4; + BMLoop *l1, *l2, *l3, *l4; + int mergeok_uvs = !douvs, mergeok_vcols = !dovcols; + + l1 = e->l; + l3 = e->l->radial_next; + + /* match up loops on each side of an edge corrusponding to each ver */ + if (l1->v == l3->v) { + l2 = l1->next; + l4 = l2->next; + } + else { + l2 = l1->next; + + l4 = l3; + l3 = l4->next; + } + + lcol1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + lcol2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + lcol3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + lcol4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + + luv1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + luv2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + luv3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + luv4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + + tp1 = CustomData_bmesh_get(&bm->pdata, l1->f->head.data, CD_MTEXPOLY); + tp2 = CustomData_bmesh_get(&bm->pdata, l2->f->head.data, CD_MTEXPOLY); + + if (!lcol1) + mergeok_vcols = 1; + + if (!luv1) + mergeok_uvs = 1; + + /* compare faceedges for each face attribute. Additional per face attributes can be added late */ + + /* do VCOL */ + if (lcol1 && dovcols) { + char *cols[4] = {(char *)lcol1, (char *)lcol2, (char *)lcol3, (char *)lcol4}; + int i; + + for (i = 0; i < 3; i++) { + if (cols[0][i] + T2QCOL_LIMIT < cols[2][i] - T2QCOL_LIMIT) + break; + if (cols[1][i] + T2QCOL_LIMIT < cols[3][i] - T2QCOL_LIMIT) + break; + } + + if (i == 3) + mergeok_vcols = 1; + } + + /* do UV */ + if (luv1 && douvs) { + if (tp1->tpage != tp2->tpage); /* do nothin */ + else { + int i; + + for (i = 0; i < 2; i++) { + if (luv1->uv[0] + T2QUV_LIMIT > luv3->uv[0] && luv1->uv[0] - T2QUV_LIMIT < luv3->uv[0] && + luv1->uv[1] + T2QUV_LIMIT > luv3->uv[1] && luv1->uv[1] - T2QUV_LIMIT < luv3->uv[1]) + { + if (luv2->uv[0] + T2QUV_LIMIT > luv4->uv[0] && luv2->uv[0] - T2QUV_LIMIT < luv4->uv[0] && + luv2->uv[1] + T2QUV_LIMIT > luv4->uv[1] && luv2->uv[1] - T2QUV_LIMIT < luv4->uv[1]) + { + mergeok_uvs = 1; + } + } + } + } + } + + if (douvs == mergeok_uvs && dovcols == mergeok_vcols) { + return TRUE; + } + + return FALSE; +} + +typedef struct JoinEdge { + float weight; + BMEdge *e; +} JoinEdge; + +#define EDGE_MARK 1 +#define EDGE_CHOSEN 2 + +#define FACE_MARK 1 +#define FACE_INPUT 2 + +static int fplcmp(const void *v1, const void *v2) +{ + const JoinEdge *e1 = (JoinEdge *)v1, *e2 = (JoinEdge *)v2; + + if (e1->weight > e2->weight) return 1; + else if (e1->weight < e2->weight) return -1; + + return 0; +} + +void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter, liter; + BMOIter siter; + BMFace *f1, *f2; + BMLoop *l; + BMEdge *e; + BLI_array_declare(jedges); + JoinEdge *jedges = NULL; + int dosharp = BMO_slot_int_get(op, "compare_sharp"), douvs = BMO_slot_int_get(op, "compare_uvs"); + int dovcols = BMO_slot_int_get(op, "compare_vcols"), domat = BMO_slot_int_get(op, "compare_materials"); + float limit = BMO_slot_float_get(op, "limit"); + int i, totedge; + + /* flag all edges of all input face */ + BMO_ITER(f1, &siter, bm, op, "faces", BM_FACE) { + BMO_elem_flag_enable(bm, f1, FACE_INPUT); + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f1) { + BMO_elem_flag_enable(bm, l->e, EDGE_MARK); + } + } + + /* unflag edges that are invalid; e.g. aren't surrounded by triangle */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) + continue; + + if (BM_edge_face_count(e) != 2) { + BMO_elem_flag_disable(bm, e, EDGE_MARK); + continue; + } + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + if (f1->len != 3 || f2->len != 3) { + BMO_elem_flag_disable(bm, e, EDGE_MARK); + continue; + } + + if (!BMO_elem_flag_test(bm, f1, FACE_INPUT) || !BMO_elem_flag_test(bm, f2, FACE_INPUT)) { + BMO_elem_flag_disable(bm, e, EDGE_MARK); + continue; + } + } + + i = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMVert *v1, *v2, *v3, *v4; + BMFace *f1, *f2; + float measure; + + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) + continue; + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + v1 = e->l->v; + v2 = e->l->prev->v; + v3 = e->l->next->v; + v4 = e->l->radial_next->prev->v; + + if (dosharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) + continue; + + if ((douvs || dovcols) && compareFaceAttribs(bm, e, douvs, dovcols)) + continue; + + if (domat && f1->mat_nr != f2->mat_nr) + continue; + + measure = measure_facepair(bm, v1, v2, v3, v4, limit); + if (measure < limit) { + BLI_array_growone(jedges); + + jedges[i].e = e; + jedges[i].weight = measure; + + i++; + } + } + + if (!jedges) + return; + + qsort(jedges, BLI_array_count(jedges), sizeof(JoinEdge), fplcmp); + + totedge = BLI_array_count(jedges); + for (i = 0; i < totedge; i++) { + BMFace *f1, *f2; + + e = jedges[i].e; + f1 = e->l->f; + f2 = e->l->radial_next->f; + + if (BMO_elem_flag_test(bm, f1, FACE_MARK) || BMO_elem_flag_test(bm, f2, FACE_MARK)) + continue; + + BMO_elem_flag_enable(bm, f1, FACE_MARK); + BMO_elem_flag_enable(bm, f2, FACE_MARK); + BMO_elem_flag_enable(bm, e, EDGE_CHOSEN); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN)) + continue; + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + BM_faces_join_pair(bm, f1, f2, e); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + /* ok, this edge wasn't merged, check if it's + * in a 2-tri-pair island, and if so merg */ + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + if (f1->len != 3 || f2->len != 3) + continue; + + for (i = 0; i < 2; i++) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, i ? f2 : f1) { + if (l->e != e && BMO_elem_flag_test(bm, l->e, EDGE_MARK)) { + break; + } + } + + /* if l isn't NULL, we broke out of the loo */ + if (l) { + break; + } + } + + /* if i isn't 2, we broke out of that loo */ + if (i != 2) { + continue; + } + + BM_faces_join_pair(bm, f1, f2, e); + } + } + + BLI_array_free(jedges); +} diff --git a/source/blender/bmesh/operators/bmo_mesh_conv.c b/source/blender/bmesh/operators/bmo_mesh_conv.c new file mode 100644 index 00000000000..4f6db9056e5 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_mesh_conv.c @@ -0,0 +1,906 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_key_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_mesh.h" +#include "BLI_listbase.h" +#include "BKE_global.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_customdata.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +/* + * MESH CONV.C + * + * This file contains functions + * for converting a Mesh + * into a Bmesh, and back again. + * + */ + +void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op) +{ + Object *ob = BMO_slot_ptr_get(op, "object"); + Mesh *me = BMO_slot_ptr_get(op, "mesh"); + MVert *mvert; + BLI_array_declare(verts); + MEdge *medge; + MLoop *ml; + MPoly *mpoly; + KeyBlock *actkey, *block; + BMVert *v, **vt = NULL, **verts = NULL; + BMEdge *e, **fedges = NULL, **et = NULL; + BMFace *f; + BMLoop *l; + BLI_array_declare(fedges); + float (*keyco)[3] = NULL; + int *keyi; + int set_key = BMO_slot_int_get(op, "set_shapekey"); + int totuv, i, j; + + if (!me || !me->totvert) { + return; /* sanity check */ + } + + vt = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable"); + + CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /* make sure uv layer names are consisten */ + totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + for (i = 0; i < totuv; i++) { + int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i); + CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name); + } + + if (!CustomData_has_layer(&bm->edata, CD_CREASE)) + CustomData_add_layer(&bm->edata, CD_CREASE, CD_ASSIGN, NULL, 0); + + if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) + CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_ASSIGN, NULL, 0); + + if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) + CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_ASSIGN, NULL, 0); + + + if (me->key && ob->shapenr > me->key->totkey) { + ob->shapenr = me->key->totkey - 1; + } + + actkey = ob_get_keyblock(ob); + if (actkey && actkey->totelem == me->totvert) { + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + + /* check if we need to generate unique ids for the shapekeys. + * this also exists in the file reading code, but is here for + * a sanity chec */ + if (!me->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); + + me->key->uidgen = 1; + for (block = me->key->block.first; block; block = block->next) { + block->uid = me->key->uidgen++; + } + } + + keyco = actkey->data; + bm->shapenr = ob->shapenr; + for (i = 0, block = me->key->block.first; block; block = block->next, i++) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + + j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + } + else if (actkey) { + printf("shapekey<->mesh mismatch!\n"); + } + + CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]); + CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]); + CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]); + CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]); + + for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { + v = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL); + BM_elem_index_set(v, i); /* set_ok */ + vt[i] = v; + + /* transfer flag */ + v->head.hflag = BM_vert_flag_from_mflag(mvert->flag); + + /* this is necassary for selection counts to work properl */ + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) BM_vert_select_set(bm, v, TRUE); + + normal_short_to_float_v3(v->no, mvert->no); + + BM_elem_float_data_set(&bm->vdata, v, CD_BWEIGHT, (float)mvert->bweight / 255.0f); + + /* Copy Custom Dat */ + CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data); + + /* set shapekey dat */ + if (me->key) { + /* set shape key original inde */ + keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX); + if (keyi) { + *keyi = i; + } + + for (block = me->key->block.first, j = 0; block; block = block->next, j++) { + float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j); + + if (co) { + copy_v3_v3(co, ((float *)block->data) + 3 * i); + } + } + } + } + + bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ + + if (!me->totedge) { + MEM_freeN(vt); + return; + } + + et = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable"); + + medge = me->medge; + for (i = 0; i < me->totedge; i++, medge++) { + e = BM_edge_create(bm, vt[medge->v1], vt[medge->v2], NULL, FALSE); + BM_elem_index_set(e, i); /* set_ok */ + et[i] = e; + + /* transfer flags */ + e->head.hflag = BM_edge_flag_from_mflag(medge->flag); + + /* this is necassary for selection counts to work properly */ + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) BM_elem_select_set(bm, e, TRUE); + + /* Copy Custom Dat */ + CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data); + + BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (float)medge->crease / 255.0f); + BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (float)medge->bweight / 255.0f); + } + + bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */ + + mpoly = me->mpoly; + for (i = 0; i < me->totpoly; i++, mpoly++) { + BMIter iter; + + BLI_array_empty(fedges); + BLI_array_empty(verts); + + BLI_array_growitems(fedges, mpoly->totloop); + BLI_array_growitems(verts, mpoly->totloop); + + for (j = 0; j < mpoly->totloop; j++) { + ml = &me->mloop[mpoly->loopstart + j]; + v = vt[ml->v]; + e = et[ml->e]; + + fedges[j] = e; + verts[j] = v; + } + + /* not sure what this block is supposed to do, + * but its unused. so commenting - campbell */ +#if 0 + { + BMVert *v1, *v2; + v1 = vt[me->mloop[mpoly->loopstart].v]; + v2 = vt[me->mloop[mpoly->loopstart + 1].v]; + + if (v1 == fedges[0]->v1) { + v2 = fedges[0]->v2; + } + else { + v1 = fedges[0]->v2; + v2 = fedges[0]->v1; + } + } +#endif + + f = BM_face_create(bm, verts, fedges, mpoly->totloop, FALSE); + + if (!f) { + printf("%s: Warning! Bad face in mesh" + " \"%s\" at index %d!, skipping\n", + __func__, me->id.name + 2, i); + continue; + } + + /* dont use 'i' since we may have skipped the face */ + BM_elem_index_set(f, bm->totface - 1); /* set_ok */ + + /* transfer flag */ + f->head.hflag = BM_face_flag_from_mflag(mpoly->flag); + + /* this is necassary for selection counts to work properl */ + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) BM_elem_select_set(bm, f, TRUE); + + f->mat_nr = mpoly->mat_nr; + if (i == me->act_face) bm->act_face = f; + + j = 0; + BM_ITER_INDEX(l, &iter, bm, BM_LOOPS_OF_FACE, f, j) { + /* Save index of correspsonding MLoop */ + BM_elem_index_set(l, mpoly->loopstart + j); /* set_loop */ + } + + /* Copy Custom Dat */ + CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data); + } + + bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */ + + { + BMIter fiter; + BMIter liter; + + /* Copy over loop CustomData. Doing this in a separate loop isn't necessary + * but is an optimization, to avoid copying a bunch of interpolated customdata + * for each BMLoop (from previous BMLoops using the same edge), always followed + * by freeing the interpolated data and overwriting it with data from the Mesh. */ + BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + int li = BM_elem_index_get(l); + CustomData_to_bmesh_block(&me->ldata, &bm->ldata, li, &l->head.data); + BM_elem_index_set(l, 0); /* set_loop */ + } + } + } + + if (me->mselect && me->totselect != 0) { + BMIter iter; + BMVert *vertex; + BMEdge *edge; + BMFace *face; + BMVert **vertex_array = MEM_callocN(sizeof(BMVert *) * bm->totvert, + "Selection Conversion Vertex Pointer Array"); + BMEdge **edge_array = MEM_callocN(sizeof(BMEdge *) * bm->totedge, + "Selection Conversion Edge Pointer Array"); + BMFace **face_array = MEM_callocN(sizeof(BMFace *) * bm->totface, + "Selection Conversion Face Pointer Array"); + + for (i = 0, vertex = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + vertex; + i++, vertex = BM_iter_step(&iter)) + { + vertex_array[i] = vertex; + } + + for (i = 0, edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); + edge; + i++, edge = BM_iter_step(&iter)) + { + edge_array[i] = edge; + } + + for (i = 0, face = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); + face; + i++, face = BM_iter_step(&iter)) + { + face_array[i] = face; + } + + if (me->mselect) { + for (i = 0; i < me->totselect; i++) { + if (me->mselect[i].type == ME_VSEL) { + BM_select_history_store(bm, vertex_array[me->mselect[i].index]); + } + else if (me->mselect[i].type == ME_ESEL) { + BM_select_history_store(bm, edge_array[me->mselect[i].index]); + } + else if (me->mselect[i].type == ME_FSEL) { + BM_select_history_store(bm, face_array[me->mselect[i].index]); + } + } + } + else { + me->totselect = 0; + } + + MEM_freeN(vertex_array); + MEM_freeN(edge_array); + MEM_freeN(face_array); + } + else { + me->totselect = 0; + if (me->mselect) { + MEM_freeN(me->mselect); + me->mselect = NULL; + } + } + + BLI_array_free(fedges); + BLI_array_free(verts); + + MEM_freeN(vt); + MEM_freeN(et); +} + +void object_load_bmesh_exec(BMesh *bm, BMOperator *op) +{ + Object *ob = BMO_slot_ptr_get(op, "object"); + /* Scene *scene = BMO_slot_ptr_get(op, "scene"); */ + Mesh *me = ob->data; + + BMO_op_callf(bm, "bmesh_to_mesh mesh=%p object=%p notesselation=%i", me, ob, TRUE); +} + + +static BMVert **bmesh_to_mesh_vertex_map(BMesh *bm, int ototvert) +{ + BMVert **vertMap = NULL; + BMVert *eve; + int index; + int i = 0; + BMIter iter; + + /* caller needs to ensure this */ + BLI_assert(ototvert > 0); + + vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap"); + if (CustomData_has_layer(&bm->vdata, CD_SHAPE_KEYINDEX)) { + int *keyi; + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (keyi) { + if (((index = *keyi) != ORIGINDEX_NONE) && (index < ototvert)) { + vertMap[index] = eve; + } + } + else { + if (i < ototvert) { + vertMap[i] = eve; + } + } + i++; + } + } + else { + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (i < ototvert) { + vertMap[i] = eve; + } + else { + break; + } + i++; + } + } + + return vertMap; +} + +BM_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* this is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses */ + + + if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.998f)) + { + med->flag &= ~ME_EDGEDRAW; + } +} + + +void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) +{ + Mesh *me = BMO_slot_ptr_get(op, "mesh"); + /* Object *ob = BMO_slot_ptr_get(op, "object"); */ + MLoop *mloop; + MPoly *mpoly; + MVert *mvert, *oldverts; + MEdge *med, *medge; + BMVert *v, *eve; + BMEdge *e; + BMLoop *l; + BMFace *f; + BMIter iter, liter; + int i, j, *keyi, ototvert, totloop; + int dotess = !BMO_slot_int_get(op, "notesselation"); + + ototvert = me->totvert; + + /* new Vertex block */ + if (bm->totvert == 0) mvert = NULL; + else mvert = MEM_callocN(bm->totvert * sizeof(MVert), "loadeditbMesh vert"); + + /* new Edge block */ + if (bm->totedge == 0) medge = NULL; + else medge = MEM_callocN(bm->totedge * sizeof(MEdge), "loadeditbMesh edge"); + + /* build ngon dat */ + /* new Ngon Face block */ + if (bm->totface == 0) mpoly = NULL; + else mpoly = MEM_callocN(bm->totface * sizeof(MPoly), "loadeditbMesh poly"); + + /* find number of loops to allocat */ + totloop = 0; + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + totloop += f->len; + } + + if (totloop == 0) mloop = NULL; + else mloop = MEM_callocN(totloop * sizeof(MLoop), "loadeditbMesh loop"); + + /* lets save the old verts just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldverts = me->mvert; + + /* don't free this yet */ + CustomData_set_layer(&me->vdata, CD_MVERT, NULL); + + /* free custom data */ + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + CustomData_free(&me->ldata, me->totloop); + CustomData_free(&me->pdata, me->totpoly); + + /* add new custom data */ + me->totvert = bm->totvert; + me->totedge = bm->totedge; + me->totloop = totloop; + me->totpoly = bm->totface; + /* will be overwritten with a valid value if 'dotess' is set, otherwise we + * end up with 'me->totface' and me->mface == NULL which can crash [#28625] + */ + me->totface = 0; + + CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert); + CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge); + CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop); + CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly); + + CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); + CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); + CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); + CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); + + /* this is called again, 'dotess' arg is used there */ + mesh_update_customdata_pointers(me, 0); + + i = 0; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + float *bweight = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BWEIGHT); + + mvert->bweight = bweight ? (char)((*bweight) * 255) : 0; + + copy_v3_v3(mvert->co, v->co); + normal_float_to_short_v3(mvert->no, v->no); + + mvert->flag = BM_vert_flag_to_mflag(v); + + BM_elem_index_set(v, i); /* set_inline */ + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); + + i++; + mvert++; + + BM_CHECK_ELEMENT(bm, v); + } + bm->elem_index_dirty &= ~BM_VERT; + + med = medge; + i = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + float *crease = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE); + float *bweight = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BWEIGHT); + + med->v1 = BM_elem_index_get(e->v1); + med->v2 = BM_elem_index_get(e->v2); + med->crease = crease ? (char)((*crease) * 255) : 0; + med->bweight = bweight ? (char)((*bweight) * 255) : 0; + + med->flag = BM_edge_flag_to_mflag(e); + + BM_elem_index_set(e, i); /* set_inline */ + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i); + + bmesh_quick_edgedraw_flag(med, e); + + i++; + med++; + BM_CHECK_ELEMENT(bm, e); + } + bm->elem_index_dirty &= ~BM_EDGE; + + i = 0; + j = 0; + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + mpoly->loopstart = j; + mpoly->totloop = f->len; + mpoly->mat_nr = f->mat_nr; + mpoly->flag = BM_face_flag_to_mflag(f); + + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&liter), j++, mloop++) { + mloop->e = BM_elem_index_get(l->e); + mloop->v = BM_elem_index_get(l->v); + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, j); + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->v); + } + + if (f == bm->act_face) me->act_face = i; + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i); + + i++; + mpoly++; + BM_CHECK_ELEMENT(bm, f); + } + + /* patch hook indices and vertex parents */ + if (ototvert > 0) { + Object *ob; + ModifierData *md; + BMVert **vertMap = NULL; + int i, j; + + for (ob = G.main->object.first; ob; ob = ob->id.next) { + if (ob->parent == bm->ob && ELEM(ob->partype, PARVERT1, PARVERT3)) { + + if (vertMap == NULL) { + vertMap = bmesh_to_mesh_vertex_map(bm, ototvert); + } + + if (ob->par1 < ototvert) { + eve = vertMap[ob->par1]; + if (eve) ob->par1 = BM_elem_index_get(eve); + } + if (ob->par2 < ototvert) { + eve = vertMap[ob->par2]; + if (eve) ob->par2 = BM_elem_index_get(eve); + } + if (ob->par3 < ototvert) { + eve = vertMap[ob->par3]; + if (eve) ob->par3 = BM_elem_index_get(eve); + } + + } + if (ob->data == me) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData *) md; + + if (vertMap == NULL) { + vertMap = bmesh_to_mesh_vertex_map(bm, ototvert); + } + + for (i = j = 0; i < hmd->totindex; i++) { + if (hmd->indexar[i] < ototvert) { + eve = vertMap[hmd->indexar[i]]; + + if (eve) { + hmd->indexar[j++] = BM_elem_index_get(eve); + } + } + else j++; + } + + hmd->totindex = j; + } + } + } + } + + if (vertMap) MEM_freeN(vertMap); + } + + if (dotess) { + BKE_mesh_tessface_calc(me); + } + + mesh_update_customdata_pointers(me, dotess); + + { + BMEditSelection *selected; + me->totselect = BLI_countlist(&(bm->selected)); + + if (me->mselect) MEM_freeN(me->mselect); + + me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->data); + } + } + + /* see comment below, this logic is in twice */ + + if (me->key) { + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float (*ofs)[3] = NULL; + + /* go through and find any shapekey customdata layers + * that might not have corrusponding KeyBlocks, and add them if + * necassary */ + j = 0; + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) + continue; + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) + break; + } + + if (!currkey) { + currkey = MEM_callocN(sizeof(KeyBlock), "KeyBlock mesh_conv.c"); + currkey->type = KEY_LINEAR; + currkey->slidermin = 0.0f; + currkey->slidermax = 1.0f; + + BLI_addtail(&me->key->block, currkey); + me->key->totkey++; + } + + j++; + } + + + /* editing the base key should update others */ + if (me->key->type == KEY_RELATIVE && oldverts) { + int act_is_basis = 0; + /* find if this key is a basis for any others */ + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (bm->shapenr - 1 == currkey->relative) { + act_is_basis = 1; + break; + } + } + + if (act_is_basis) { /* active key is a base */ + float (*fp)[3] = actkey->data; + int *keyi; + i = 0; + ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (keyi && *keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]); + } + i++; + mvert++; + } + } + } + + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + j = 0; + + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) + continue; + + if (currkey->uid == bm->vdata.layers[i].uid) { + int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative)); + float *fp, *co; + float (*ofs_pt)[3] = ofs; + + if (currkey->data) + MEM_freeN(currkey->data); + currkey->data = fp = MEM_mallocN(sizeof(float) * 3 * bm->totvert, "shape key data"); + currkey->totelem = bm->totvert; + + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + co = (currkey == actkey) ? + eve->co : + CustomData_bmesh_get_n(&bm->vdata, eve->head.data, CD_SHAPEKEY, j); + + copy_v3_v3(fp, co); + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + } + + fp += 3; + } + break; + } + + j++; + } + + /* if we didn't find a shapekey, tag the block to be reconstructed + * via the old method below */ + if (j == CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)) { + currkey->flag |= KEYBLOCK_MISSING; + } + } + + if (ofs) MEM_freeN(ofs); + } + + /* XXX, code below is from trunk and a duplicate functionality + * to the block above. + * We should use one or the other, having both means we have to maintain + * both and keep them working the same way which is a hassle - campbell */ + + /* old method of reconstructing keys via vertice's original key indices, + * currently used if the new method above fails (which is theoretically + * possible in certain cases of undo) */ + if (me->key) { + float *fp, *newkey, *oldkey; + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float (*ofs)[3] = NULL; + + /* editing the base key should update others */ + if (me->key->type == KEY_RELATIVE && oldverts) { + int act_is_basis = 0; + /* find if this key is a basis for any others */ + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (bm->shapenr - 1 == currkey->relative) { + act_is_basis = 1; + break; + } + } + + if (act_is_basis) { /* active key is a base */ + float (*fp)[3] = actkey->data; + int *keyi; + i = 0; + ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (keyi && *keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]); + } + i++; + mvert++; + } + } + } + + /* Lets reorder the key data so that things line up roughly + * with the way things were before editmode */ + currkey = me->key->block.first; + while (currkey) { + int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative)); + + if (!(currkey->flag & KEYBLOCK_MISSING)) { + currkey = currkey->next; + continue; + } + + printf("warning: had to hackishly reconstruct shape key \"%s\"," + " it may not be correct anymore.\n", currkey->name); + + currkey->flag &= ~KEYBLOCK_MISSING; + + fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); + oldkey = currkey->data; + + eve = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + + i = 0; + mvert = me->mvert; + while (eve) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (!keyi) { + break; + } + if (*keyi >= 0 && *keyi < currkey->totelem) { // valid old vertex + if (currkey == actkey) { + if (actkey == me->key->refkey) { + copy_v3_v3(fp, mvert->co); + } + else { + copy_v3_v3(fp, mvert->co); + if (oldverts) { + copy_v3_v3(mvert->co, oldverts[*keyi].co); + } + } + } + else { + if (oldkey) { + copy_v3_v3(fp, oldkey + 3 * *keyi); + } + } + } + else { + copy_v3_v3(fp, mvert->co); + } + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, ofs[i]); + } + + fp+= 3; + ++i; + ++mvert; + eve = BM_iter_step(&iter); + } + currkey->totelem = bm->totvert; + if (currkey->data) MEM_freeN(currkey->data); + currkey->data = newkey; + + currkey = currkey->next; + } + + if (ofs) MEM_freeN(ofs); + } + + if (oldverts) MEM_freeN(oldverts); +} diff --git a/source/blender/bmesh/operators/bmo_mirror.c b/source/blender/bmesh/operators/bmo_mirror.c new file mode 100644 index 00000000000..82e77fc9a96 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_mirror.c @@ -0,0 +1,126 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_operators_private.h" /* own include */ + +#define ELE_NEW 1 + +void bmesh_mirror_exec(BMesh *bm, BMOperator *op) +{ + BMOperator dupeop, weldop; + BMOIter siter; + BMIter iter; + BMVert *v, *v2, **vmap = NULL; + BLI_array_declare(vmap); + BMEdge /* *e, */ **emap = NULL; + BLI_array_declare(emap); + float mtx[4][4]; + float imtx[4][4]; + float scale[3] = {1.0f, 1.0f, 1.0f}; + float dist = BMO_slot_float_get(op, "mergedist"); + int i, ototvert, ototedge, axis = BMO_slot_int_get(op, "axis"); + int mirroru = BMO_slot_int_get(op, "mirror_u"); + int mirrorv = BMO_slot_int_get(op, "mirror_v"); + + ototvert = bm->totvert; + ototedge = bm->totedge; + + BMO_slot_mat4_get(op, "mat", mtx); + invert_m4_m4(imtx, mtx); + + BMO_op_initf(bm, &dupeop, "dupe geom=%s", op, "geom"); + BMO_op_exec(bm, &dupeop); + + BMO_slot_buffer_flag_enable(bm, &dupeop, "newout", ELE_NEW, BM_ALL); + + /* create old -> new mappin */ + i = 0; + v2 = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + BMO_ITER(v, &siter, bm, &dupeop, "newout", BM_VERT) { + BLI_array_growone(vmap); + vmap[i] = v; + + /* BMESH_TODO, double check this is being used, calling following operators will overwrite anyway - campbell */ + BM_elem_index_set(v2, i); /* set_dirty! */ + v2 = BM_iter_step(&iter); + + i++; + } + bm->elem_index_dirty |= BM_VERT; + + /* feed old data to transform bmo */ + scale[axis] = -1.0f; + BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, mtx); + BMO_op_callf(bm, "scale verts=%fv vec=%v", ELE_NEW, scale); + BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, imtx); + + BMO_op_init(bm, &weldop, "weldverts"); + + v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + for (i = 0; i < ototvert; i++) { + if (ABS(v->co[axis]) <= dist) { + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", vmap[i], v); + } + v = BM_iter_step(&iter); + } + + if (mirroru || mirrorv) { + BMFace *f; + BMLoop *l; + MLoopUV *luv; + int totlayer; + BMIter liter; + + BMO_ITER(f, &siter, bm, &dupeop, "newout", BM_FACE) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + totlayer = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV); + for (i = 0; i < totlayer; i++) { + luv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i); + if (mirroru) + luv->uv[0] = 1.0f - luv->uv[0]; + if (mirrorv) + luv->uv[1] = 1.0f - luv->uv[1]; + } + } + } + } + + BMO_op_exec(bm, &weldop); + + BMO_op_finish(bm, &weldop); + BMO_op_finish(bm, &dupeop); + + BMO_slot_from_flag(bm, op, "newout", ELE_NEW, BM_ALL); + + BLI_array_free(vmap); + BLI_array_free(emap); +} diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c new file mode 100644 index 00000000000..f7a1a8f4d06 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -0,0 +1,734 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "ED_mesh.h" + +#include "bmesh.h" +#include "bmesh_private.h" + + + +/* ************************ primitives ******************* */ + +static float icovert[12][3] = { + {0.0f,0.0f,-200.0f}, + {144.72f, -105.144f,-89.443f}, + {-55.277f, -170.128,-89.443f}, + {-178.885f,0.0f,-89.443f}, + {-55.277f,170.128f,-89.443f}, + {144.72f,105.144f,-89.443f}, + {55.277f,-170.128f,89.443f}, + {-144.72f,-105.144f,89.443f}, + {-144.72f,105.144f,89.443f}, + {55.277f,170.128f,89.443f}, + {178.885f,0.0f,89.443f}, + {0.0f,0.0f,200.0f} +}; + +static short icoface[20][3] = { + {0,1,2}, + {1,0,5}, + {0,2,3}, + {0,3,4}, + {0,4,5}, + {1,5,10}, + {2,1,6}, + {3,2,7}, + {4,3,8}, + {5,4,9}, + {1,10,6}, + {2,6,7}, + {3,7,8}, + {4,8,9}, + {5,9,10}, + {6,10,11}, + {7,6,11}, + {8,7,11}, + {9,8,11}, + {10,9,11} +}; + +// HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker +// this hack is only used so that scons & mingw + split-sources hack works +// ------------------------------- start copied code +/* these are not the monkeys you are looking for */ +static int monkeyo = 4; +static int monkeynv = 271; +static int monkeynf = 250; +static signed char monkeyv[271][3] = { + {-71,21,98},{-63,12,88},{-57,7,74},{-82,-3,79},{-82,4,92}, + {-82,17,100},{-92,21,102},{-101,12,95},{-107,7,83}, + {-117,31,84},{-109,31,95},{-96,31,102},{-92,42,102}, + {-101,50,95},{-107,56,83},{-82,66,79},{-82,58,92}, + {-82,46,100},{-71,42,98},{-63,50,88},{-57,56,74}, + {-47,31,72},{-55,31,86},{-67,31,97},{-66,31,99}, + {-70,43,100},{-82,48,103},{-93,43,105},{-98,31,105}, + {-93,20,105},{-82,31,106},{-82,15,103},{-70,20,100}, + {-127,55,95},{-127,45,105},{-127,-87,94},{-127,-41,100}, + {-127,-24,102},{-127,-99,92},{-127,52,77},{-127,73,73}, + {-127,115,-70},{-127,72,-109},{-127,9,-106},{-127,-49,-45}, + {-101,-24,72},{-87,-56,73},{-82,-89,73},{-80,-114,68}, + {-85,-121,67},{-104,-124,71},{-127,-126,74},{-71,-18,68}, + {-46,-5,69},{-21,19,57},{-17,55,76},{-36,62,80}, + {-64,77,88},{-86,97,94},{-107,92,97},{-119,63,96}, + {-106,53,99},{-111,39,98},{-101,12,95},{-79,2,90}, + {-64,8,86},{-47,24,83},{-45,38,83},{-50,48,85}, + {-72,56,92},{-95,60,97},{-127,-98,94},{-113,-92,94}, + {-112,-107,91},{-119,-113,89},{-127,-114,88},{-127,-25,96}, + {-127,-18,95},{-114,-19,95},{-111,-29,96},{-116,-37,95}, + {-76,-6,86},{-48,7,80},{-34,26,77},{-32,48,84}, + {-39,53,93},{-71,70,102},{-87,82,107},{-101,79,109}, + {-114,55,108},{-111,-13,104},{-100,-57,91},{-95,-90,88}, + {-93,-105,85},{-97,-117,81},{-106,-119,81},{-127,-121,82}, + {-127,6,93},{-127,27,98},{-85,61,95},{-106,18,96}, + {-110,27,97},{-112,-88,94},{-117,-57,96},{-127,-57,96}, + {-127,-42,95},{-115,-35,100},{-110,-29,102},{-113,-17,100}, + {-122,-16,100},{-127,-26,106},{-121,-19,104},{-115,-20,104}, + {-113,-29,106},{-117,-32,103},{-127,-37,103},{-94,-40,71}, + {-106,-31,91},{-104,-40,91},{-97,-32,71},{-127,-112,88}, + {-121,-111,88},{-115,-105,91},{-115,-95,93},{-127,-100,84}, + {-115,-96,85},{-115,-104,82},{-121,-109,81},{-127,-110,81}, + {-105,28,100},{-103,20,99},{-84,55,97},{-92,54,99}, + {-73,51,99},{-55,45,89},{-52,37,88},{-53,25,87}, + {-66,13,92},{-79,8,95},{-98,14,100},{-104,38,100}, + {-100,48,100},{-97,46,97},{-102,38,97},{-96,16,97}, + {-79,11,93},{-68,15,90},{-57,27,86},{-56,36,86}, + {-59,43,87},{-74,50,96},{-91,51,98},{-84,52,96}, + {-101,22,96},{-102,29,96},{-113,59,78},{-102,85,79}, + {-84,88,76},{-65,71,71},{-40,58,63},{-25,52,59}, + {-28,21,48},{-50,0,53},{-71,-12,60},{-127,115,37}, + {-127,126,-10},{-127,-25,-86},{-127,-59,24},{-127,-125,59}, + {-127,-103,44},{-127,-73,41},{-127,-62,36},{-18,30,7}, + {-17,41,-6},{-28,34,-56},{-68,56,-90},{-33,-6,9}, + {-51,-16,-21},{-45,-1,-55},{-84,7,-85},{-97,-45,52}, + {-104,-53,33},{-90,-91,49},{-95,-64,50},{-85,-117,51}, + {-109,-97,47},{-111,-69,46},{-106,-121,56},{-99,-36,55}, + {-100,-29,60},{-101,-22,64},{-100,-50,21},{-89,-40,-34}, + {-83,-19,-69},{-69,111,-49},{-69,119,-9},{-69,109,30}, + {-68,67,55},{-34,52,43},{-46,58,36},{-45,90,7}, + {-25,72,16},{-25,79,-15},{-45,96,-25},{-45,87,-57}, + {-25,69,-46},{-48,42,-75},{-65,3,-70},{-22,42,-26}, + {-75,-22,19},{-72,-25,-27},{-13,52,-30},{-28,-18,-16}, + {6,-13,-42},{37,7,-55},{46,41,-54},{31,65,-54}, + {4,61,-40},{3,53,-37},{25,56,-50},{35,37,-52}, + {28,10,-52},{5,-5,-39},{-21,-9,-17},{-9,46,-28}, + {-6,39,-37},{-14,-3,-27},{6,0,-47},{25,12,-57}, + {31,32,-57},{23,46,-56},{4,44,-46},{-19,37,-27}, + {-20,22,-35},{-30,12,-35},{-22,11,-35},{-19,2,-35}, + {-23,-2,-35},{-34,0,-9},{-35,-3,-22},{-35,5,-24}, + {-25,26,-27},{-13,31,-34},{-13,30,-41},{-23,-2,-41}, + {-18,2,-41},{-21,10,-41},{-29,12,-41},{-19,22,-41}, + {6,42,-53},{25,44,-62},{34,31,-63},{28,11,-62}, + {7,0,-54},{-14,-2,-34},{-5,37,-44},{-13,14,-42}, + {-7,8,-43},{1,16,-47},{-4,22,-45},{3,30,-48}, + {8,24,-49},{15,27,-50},{12,35,-50},{4,56,-62}, + {33,60,-70},{48,38,-64},{41,7,-68},{6,-11,-63}, + {-26,-16,-42},{-17,49,-49}, +}; + +static signed char monkeyf[250][4] = { + {27,4,5,26}, {25,4,5,24}, {3,6,5,4}, {1,6,5,2}, {5,6,7,4}, + {3,6,7,2}, {5,8,7,6}, {3,8,7,4}, {7,8,9,6}, + {5,8,9,4}, {7,10,9,8}, {5,10,9,6}, {9,10,11,8}, + {7,10,11,6}, {9,12,11,10}, {7,12,11,8}, {11,6,13,12}, + {5,4,13,12}, {3,-2,13,12}, {-3,-4,13,12}, {-5,-10,13,12}, + {-11,-12,14,12}, {-13,-18,14,13}, {-19,4,5,13}, {10,12,4,4}, + {10,11,9,9}, {8,7,9,9}, {7,5,6,6}, {6,3,4,4}, + {5,1,2,2}, {4,-1,0,0}, {3,-3,-2,-2}, {22,67,68,23}, + {20,65,66,21}, {18,63,64,19}, {16,61,62,17}, {14,59,60,15}, + {12,19,48,57}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, + {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, + {18,19,48,47}, {18,-9,-8,47}, {18,27,45,46}, {26,55,43,44}, + {24,41,42,54}, {22,39,40,23}, {20,37,38,21}, {18,35,36,19}, + {16,33,34,17}, {14,31,32,15}, {12,39,30,13}, {11,48,45,38}, + {8,36,-19,9}, {8,-20,44,47}, {42,45,46,43}, {18,19,40,39}, + {16,17,38,37}, {14,15,36,35}, {32,44,43,33}, {12,33,32,42}, + {19,44,43,42}, {40,41,42,-27}, {8,9,39,-28}, {15,43,42,16}, + {13,43,42,14}, {11,43,42,12}, {9,-30,42,10}, {37,12,38,-32}, + {-33,37,45,46}, {-33,40,41,39}, {38,40,41,37}, {36,40,41,35}, + {34,40,41,33}, {36,39,38,37}, {35,40,39,38}, {1,2,14,21}, + {1,2,40,13}, {1,2,40,39}, {1,24,12,39}, {-34,36,38,11}, + {35,38,36,37}, {-37,8,35,37}, {-11,-12,-45,40}, {-11,-12,39,38}, + {-11,-12,37,36}, {-11,-12,35,34}, {33,34,40,41}, {33,34,38,39}, + {33,34,36,37}, {33,-52,34,35}, {33,37,36,34}, {33,35,34,34}, + {8,7,37,36}, {-32,7,35,46}, {-34,-33,45,46}, {4,-33,43,34}, + {-34,-33,41,42}, {-34,-33,39,40}, {-34,-33,37,38}, {-34,-33,35,36}, + {-34,-33,33,34}, {-34,-33,31,32}, {-34,-4,28,30}, {-5,-34,28,27}, + {-35,-44,36,27}, {26,35,36,45}, {24,25,44,45}, {25,23,44,42}, + {25,24,41,40}, {25,24,39,38}, {25,24,37,36}, {25,24,35,34}, + {25,24,33,32}, {25,24,31,30}, {15,24,29,38}, {25,24,27,26}, + {23,12,37,26}, {11,12,35,36}, {-86,-59,36,-80}, {-60,-61,36,35}, + {-62,-63,36,35}, {-64,-65,36,35}, {-66,-67,36,35}, {-68,-69,36,35}, + {-70,-71,36,35}, {-72,-73,36,35}, {-74,-75,36,35}, {42,43,53,58}, + {40,41,57,56}, {38,39,55,57}, {-81,-80,37,56}, {-83,-82,55,52}, + {-85,-84,51,49}, {-87,-86,48,49}, {47,50,51,48}, {46,48,51,49}, + {43,46,49,44}, {-92,-91,45,42}, {-23,49,50,-20}, {-94,40,48,-24}, + {-96,-22,48,49}, {-97,48,21,-90}, {-100,36,50,23}, {22,49,48,-100}, + {-101,47,46,22}, {21,45,35,25}, {33,34,44,41}, {13,14,28,24}, + {-107,26,30,-106}, {14,46,45,15}, {14,44,43,-110}, {-111,42,23,-110}, + {6,7,45,46}, {45,44,47,46}, {45,46,47,48}, {47,46,49,48}, + {17,49,47,48}, {17,36,46,48}, {35,36,44,45}, {35,36,40,43}, + {35,36,38,39}, {-4,-3,37,35}, {-123,34,33,1}, {-9,-8,-7,-6}, + {-10,-7,32,-125}, {-127,-11,-126,-126}, {-7,-6,5,31}, {4,5,33,30}, + {4,39,33,32}, {4,35,32,38}, {20,21,39,38}, {4,37,38,5}, + {-11,-10,36,3}, {-11,15,14,35}, {13,16,34,34}, {-13,14,13,13}, + {-3,1,30,29}, {-3,28,29,1}, {-2,31,28,-1}, {12,13,27,30}, + {-2,26,12,12}, {35,29,42,36}, {34,35,36,33}, {32,35,36,31}, + {30,35,36,29}, {28,35,36,27}, {26,35,36,25}, {34,39,38,35}, + {32,39,38,33}, {30,39,38,31}, {28,39,38,29}, {26,39,38,27}, + {25,31,32,38}, {-18,-17,45,44}, {-18,17,28,44}, {-24,-20,42,-23}, + {11,35,27,14}, {25,28,39,41}, {37,41,40,38}, {34,40,36,35}, + {32,40,39,33}, {30,39,31,40}, {21,29,39,22}, {-31,37,28,4}, + {-32,33,35,36}, {32,33,34,34}, {18,35,36,48}, {34,25,40,35}, + {24,25,38,39}, {24,25,36,37}, {24,25,34,35}, {24,25,32,33}, + {24,13,41,31}, {17,11,41,35}, {15,16,34,35}, {13,14,34,35}, + {11,12,34,35}, {9,10,34,35}, {7,8,34,35}, {26,25,37,36}, + {35,36,37,38}, {37,36,39,38}, {37,38,39,40}, {25,31,36,39}, + {18,34,35,30}, {17,22,30,33}, {19,29,21,20}, {16,26,29,17}, + {24,29,28,25}, {22,31,28,23}, {20,31,30,21}, {18,31,30,19}, + {16,30,17,17}, {-21,-22,35,34}, {-21,-22,33,32}, {-21,-22,31,30}, + {-21,-22,29,28}, {-21,-22,27,26}, {-28,-22,25,31}, {24,28,29,30}, + {23,24,26,27}, {23,24,25,25}, {-69,-35,-32,27}, {-70,26,25,-66}, + {-68,-67,24,-33}, +}; + +#define VERT_MARK 1 + +#define EDGE_ORIG 1 +#define EDGE_MARK 2 + +#define FACE_MARK 1 +#define FACE_NEW 2 + +void bmesh_create_grid_exec(BMesh *bm, BMOperator *op) +{ + BMOperator bmop, prevop; + BMVert *eve, *preveve; + BMEdge *e; + float vec[3], mat[4][4], phi, phid, dia = BMO_slot_float_get(op, "size"); + int a, tot = BMO_slot_int_get(op, "xsegments"), seg = BMO_slot_int_get(op, "ysegments"); + + if (tot < 2) tot = 2; + if (seg < 2) seg = 2; + + BMO_slot_mat4_get(op, "mat", mat); + + /* one segment first: the X axis */ + phi = 1.0f; + phid = 2.0f / ((float)tot - 1); + for (a = 0; a < tot; a++) { + vec[0] = dia * phi; + vec[1] = -dia; + vec[2] = 0.0f; + mul_m4_v3(mat, vec); + + eve = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, eve, VERT_MARK); + + if (a) { + e = BM_edge_create(bm, preveve, eve, NULL, TRUE); + BMO_elem_flag_enable(bm, e, EDGE_ORIG); + } + + preveve = eve; + phi -= phid; + } + + /* extrude and translate */ + vec[0] = vec[2] = 0.0f; + vec[1] = dia * phid; + mul_mat3_m4_v3(mat, vec); + + for (a = 0; a < seg - 1; a++) { + if (a) { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout"); + BMO_op_exec(bm, &bmop); + BMO_op_finish(bm, &prevop); + + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + } + else { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG); + BMO_op_exec(bm, &bmop); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + } + + BMO_op_callf(bm, "translate vec=%v verts=%s", vec, &bmop, "geomout"); + prevop = bmop; + } + + if (a) + BMO_op_finish(bm, &bmop); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op) +{ + BMOperator bmop, prevop; + BMVert *eve, *preveve; + BMEdge *e; + BMIter iter; + float vec[3], mat[4][4], cmat[3][3], phi, q[4]; + float phid, dia = BMO_slot_float_get(op, "diameter"); + int a, seg = BMO_slot_int_get(op, "segments"), tot = BMO_slot_int_get(op, "revolutions"); + + BMO_slot_mat4_get(op, "mat", mat); + + phid = 2.0f * (float)M_PI / tot; + phi = 0.25f * (float)M_PI; + + /* one segment first */ + phi = 0; + phid /= 2; + for (a = 0; a <= tot; a++) { + /* Going in this direction, then edge extruding, makes normals face outward */ + vec[0] = -dia * sinf(phi); + vec[1] = 0.0; + vec[2] = dia * cosf(phi); + eve = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, eve, VERT_MARK); + + if (a != 0) { + e = BM_edge_create(bm, preveve, eve, NULL, FALSE); + BMO_elem_flag_enable(bm, e, EDGE_ORIG); + } + + phi+= phid; + preveve = eve; + } + + /* extrude and rotate; negative phi to make normals face outward */ + phi = -M_PI / seg; + q[0] = cosf(phi); + q[3] = sinf(phi); + q[1] = q[2] = 0.0f; + quat_to_mat3(cmat, q); + + for (a = 0; a < seg; a++) { + if (a) { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout"); + BMO_op_exec(bm, &bmop); + BMO_op_finish(bm, &prevop); + } + else { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG); + BMO_op_exec(bm, &bmop); + } + + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", vec, cmat, &bmop, "geomout"); + + prevop = bmop; + } + + if (a) + BMO_op_finish(bm, &bmop); + + { + float len, len2, vec2[3]; + + len= 2*dia*sinf(phid / 2.0f); + + /* length of one segment in shortest parallen */ + vec[0]= dia*sinf(phid); + vec[1]= 0.0; + vec[2]= dia*cosf(phid); + + mul_v3_m3v3(vec2, cmat, vec); + len2= len_v3v3(vec, vec2); + + /* use shortest segment length divided by 3 as merge threshold */ + BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, MIN2(len, len2) / 3.0f); + } + + /* and now do imat */ + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, eve, VERT_MARK)) { + mul_m4_v3(mat, eve->co); + } + } + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op) +{ + BMVert *eva[12]; + BMVert *v; + BMIter liter; + BMIter viter; + BMLoop *l; + float vec[3], mat[4][4] /* , phi, phid */; + float dia = BMO_slot_float_get(op, "diameter"); + int a, subdiv = BMO_slot_int_get(op, "subdivisions"); + + BMO_slot_mat4_get(op, "mat", mat); + + /* phid = 2.0f * (float)M_PI / subdiv; */ /* UNUSED */ + /* phi = 0.25f * (float)M_PI; */ /* UNUSED */ + + dia /= 200.0f; + for (a = 0; a < 12; a++) { + vec[0] = dia * icovert[a][0]; + vec[1] = dia * icovert[a][1]; + vec[2] = dia * icovert[a][2]; + eva[a] = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, eva[a], VERT_MARK); + } + + for (a = 0; a < 20; a++) { + BMFace *eftemp; + BMVert *v1, *v2, *v3; + + v1 = eva[icoface[a][0]]; + v2 = eva[icoface[a][1]]; + v3 = eva[icoface[a][2]]; + + eftemp = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, NULL, FALSE); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, eftemp) { + BMO_elem_flag_enable(bm, l->e, EDGE_MARK); + } + + BMO_elem_flag_enable(bm, eftemp, FACE_MARK); + } + + dia *= 200.0f; + + for (a = 1; a < subdiv; a++) { + BMOperator bmop; + + BMO_op_initf(bm, &bmop, + "esubd edges=%fe smooth=%f numcuts=%i gridfill=%i beauty=%i", + EDGE_MARK, dia, 1, 1, B_SPHERE); + BMO_op_exec(bm, &bmop); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", EDGE_MARK, BM_EDGE); + BMO_op_finish(bm, &bmop); + } + + /* must transform after becayse of sphere subdivision */ + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + mul_m4_v3(mat, v->co); + } + } + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op) +{ + BMVert *eve; + BMVert **tv = MEM_mallocN(sizeof(*tv)*monkeynv * 2, "tv"); + float mat[4][4]; + int i; + + BMO_slot_mat4_get(op, "mat", mat); + + for (i = 0; i < monkeynv; i++) { + float v[3]; + + v[0] = (monkeyv[i][0] + 127) / 128.0, v[1] = monkeyv[i][1] / 128.0, v[2] = monkeyv[i][2] / 128.0; + + tv[i] = BM_vert_create(bm, v, NULL); + BMO_elem_flag_enable(bm, tv[i], VERT_MARK); + + tv[monkeynv + i] = (fabsf(v[0] = -v[0]) < 0.001f) ? + tv[i] : + (eve = BM_vert_create(bm, v, NULL), mul_m4_v3(mat, eve->co), eve); + + BMO_elem_flag_enable(bm, tv[monkeynv + i], VERT_MARK); + + mul_m4_v3(mat, tv[i]->co); + } + + for (i = 0; i < monkeynf; i++) { + BM_face_create_quad_tri(bm, + tv[monkeyf[i][0] + i - monkeyo], + tv[monkeyf[i][1] + i - monkeyo], + tv[monkeyf[i][2] + i - monkeyo], + (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeyf[i][3] + i - monkeyo] : NULL, + NULL, FALSE); + + BM_face_create_quad_tri(bm, + tv[monkeynv + monkeyf[i][2] + i - monkeyo], + tv[monkeynv + monkeyf[i][1] + i - monkeyo], + tv[monkeynv + monkeyf[i][0] + i - monkeyo], + (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeynv + monkeyf[i][3] + i - monkeyo]: NULL, + NULL, FALSE); + } + + MEM_freeN(tv); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + + +void bmesh_create_circle_exec(BMesh *bm, BMOperator *op) +{ + BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL; + float vec[3], mat[4][4], phi, phid; + float dia = BMO_slot_float_get(op, "diameter"); + int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments"); + int cap_tris = BMO_slot_int_get(op, "cap_tris"); + int a; + + if (!segs) + return; + + BMO_slot_mat4_get(op, "mat", mat); + + phid = 2.0f * (float)M_PI / segs; + phi = .25f * (float)M_PI; + + if (cap_ends) { + vec[0] = vec[1] = 0.0f; + vec[2] = 0.0; + mul_m4_v3(mat, vec); + + cent1 = BM_vert_create(bm, vec, NULL); + } + + for (a = 0; a < segs; a++, phi += phid) { + /* Going this way ends up with normal(s) upward */ + vec[0] = -dia * sinf(phi); + vec[1] = dia * cosf(phi); + vec[2] = 0.0f; + mul_m4_v3(mat, vec); + v1 = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, v1, VERT_MARK); + + if (lastv1) + BM_edge_create(bm, v1, lastv1, NULL, FALSE); + + if (a && cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (!firstv1) + firstv1 = v1; + + lastv1 = v1; + } + + if (!a) + return; + + BM_edge_create(bm, lastv1, firstv1, NULL, FALSE); + + if (cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (!cap_tris) { + BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW); + } + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_cone_exec(BMesh *bm, BMOperator *op) +{ + BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2; + float vec[3], mat[4][4], phi, phid; + float dia1 = BMO_slot_float_get(op, "diameter1"); + float dia2 = BMO_slot_float_get(op, "diameter2"); + float depth = BMO_slot_float_get(op, "depth"); + int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments"); + int cap_tris = BMO_slot_int_get(op, "cap_tris"); + int a; + + if (!segs) + return; + + BMO_slot_mat4_get(op, "mat", mat); + + phid = 2.0f * (float)M_PI / segs; + phi = 0.25f * (float)M_PI; + + depth *= 0.5f; + if (cap_ends) { + vec[0] = vec[1] = 0.0f; + vec[2] = -depth; + mul_m4_v3(mat, vec); + + cent1 = BM_vert_create(bm, vec, NULL); + + vec[0] = vec[1] = 0.0f; + vec[2] = depth; + mul_m4_v3(mat, vec); + + cent2 = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, cent1, VERT_MARK); + BMO_elem_flag_enable(bm, cent2, VERT_MARK); + } + + for (a = 0; a < segs; a++, phi += phid) { + vec[0] = dia1 * sinf(phi); + vec[1] = dia1 * cosf(phi); + vec[2] = -depth; + mul_m4_v3(mat, vec); + v1 = BM_vert_create(bm, vec, NULL); + + vec[0] = dia2 * sinf(phi); + vec[1] = dia2 * cosf(phi); + vec[2] = depth; + mul_m4_v3(mat, vec); + v2 = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, v1, VERT_MARK); + BMO_elem_flag_enable(bm, v2, VERT_MARK); + + if (a) { + if (cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, FALSE); + } + else { + firstv1 = v1; + firstv2 = v2; + } + + lastv1 = v1; + lastv2 = v2; + } + + if (!a) + return; + + if (cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (!cap_tris) { + BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW); + } + + BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, FALSE); + + BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, 0.000001); + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_cube_exec(BMesh *bm, BMOperator *op) +{ + BMVert *v1, *v2, *v3, *v4, *v5, *v6, *v7, *v8; + float vec[3], mat[4][4], off = BMO_slot_float_get(op, "size") / 2.0f; + + BMO_slot_mat4_get(op, "mat", mat); + + if (!off) off = 0.5f; + + vec[0] = -off; + vec[1] = -off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v1 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v1, VERT_MARK); + + vec[0] = -off; + vec[1] = off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v2 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v2, VERT_MARK); + + vec[0] = off; + vec[1] = off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v3 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v3, VERT_MARK); + + vec[0] = off; + vec[1] = -off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v4 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v4, VERT_MARK); + + vec[0] = -off; + vec[1] = -off; + vec[2] = off; + mul_m4_v3(mat, vec); + v5 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v5, VERT_MARK); + + vec[0] = -off; + vec[1] = off; + vec[2] = off; + mul_m4_v3(mat, vec); + v6 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v6, VERT_MARK); + + vec[0] = off; + vec[1] = off; + vec[2] = off; + mul_m4_v3(mat, vec); + v7 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v7, VERT_MARK); + + vec[0] = off; + vec[1] = -off; + vec[2] = off; + mul_m4_v3(mat, vec); + v8 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v8, VERT_MARK); + + /* the four sides */ + BM_face_create_quad_tri(bm, v5, v6, v2, v1, NULL, FALSE); + BM_face_create_quad_tri(bm, v6, v7, v3, v2, NULL, FALSE); + BM_face_create_quad_tri(bm, v7, v8, v4, v3, NULL, FALSE); + BM_face_create_quad_tri(bm, v8, v5, v1, v4, NULL, FALSE); + + /* top/bottom */ + BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE); + BM_face_create_quad_tri(bm, v8, v7, v6, v5, NULL, FALSE); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c new file mode 100644 index 00000000000..2eb1cf7db3e --- /dev/null +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -0,0 +1,590 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op) +{ + BMIter liter; + BMLoop *l; + BMVert *v2, *doub; + int split = FALSE; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", l->v); + /* ok: if v2 is NULL (e.g. not in the map) then it's + * a target vert, otherwise it's a doubl */ + if ((v2 && BM_vert_in_face(f, v2)) && + (v2 != l->prev->v) && + (v2 != l->next->v)) + { + doub = l->v; + split = TRUE; + break; + } + } + + if (split && doub != v2) { + BMLoop *nl; + BMFace *f2 = BM_face_split(bm, f, doub, v2, &nl, NULL); + + remdoubles_splitface(f, bm, op); + remdoubles_splitface(f2, bm, op); + } +} + +#define ELE_DEL 1 +#define EDGE_COL 2 +#define FACE_MARK 2 + +#if 0 +int remdoubles_face_overlaps(BMesh *bm, BMVert **varr, + int len, BMFace *exclude, + BMFace **overlapface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + if (overlapface) *overlapface = NULL; + + for (i = 0; i < len; i++) { + f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]); + while (f) { + amount = BM_verts_in_face(bm, f, varr, len); + if (amount >= len) { + if (overlapface) *overlapface = f; + return TRUE; + } + f = BM_iter_step(&vertfaces); + } + } + return FALSE; +} +#endif + +void bmesh_weldverts_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter, liter; + BMVert *v, *v2; + BMEdge *e, *e2, **edges = NULL; + BLI_array_declare(edges); + BMLoop *l, *l2, **loops = NULL; + BLI_array_declare(loops); + BMFace *f, *f2; + int a, b; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if ((v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v))) { + BMO_elem_flag_enable(bm, v, ELE_DEL); + + /* merge the vertex flags, else we get randomly selected/unselected verts */ + BM_elem_flag_merge(v, v2); + } + } + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + remdoubles_splitface(f, bm, op); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e->v1, ELE_DEL) || BMO_elem_flag_test(bm, e->v2, ELE_DEL)) { + v = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v1); + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v2); + + if (!v) v = e->v1; + if (!v2) v2 = e->v2; + + if (v == v2) + BMO_elem_flag_enable(bm, e, EDGE_COL); + else if (!BM_edge_exists(v, v2)) + BM_edge_create(bm, v, v2, e, TRUE); + + BMO_elem_flag_enable(bm, e, ELE_DEL); + } + } + + /* BMESH_TODO, stop abusing face index here */ + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_elem_index_set(f, 0); /* set_dirty! */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (BMO_elem_flag_test(bm, l->v, ELE_DEL)) { + BMO_elem_flag_enable(bm, f, FACE_MARK|ELE_DEL); + } + if (BMO_elem_flag_test(bm, l->e, EDGE_COL)) { + BM_elem_index_set(f, BM_elem_index_get(f) + 1); /* set_dirty! */ + } + } + } + bm->elem_index_dirty |= BM_FACE; + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) + continue; + + if (f->len - BM_elem_index_get(f) < 3) { + BMO_elem_flag_enable(bm, f, ELE_DEL); + continue; + } + + BLI_array_empty(edges); + BLI_array_empty(loops); + a = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + v = l->v; + v2 = l->next->v; + if (BMO_elem_flag_test(bm, v, ELE_DEL)) { + v = BMO_slot_map_ptr_get(bm, op, "targetmap", v); + } + if (BMO_elem_flag_test(bm, v2, ELE_DEL)) { + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2); + } + + e2 = v != v2 ? BM_edge_exists(v, v2) : NULL; + if (e2) { + for (b = 0; b < a; b++) { + if (edges[b] == e2) { + break; + } + } + if (b != a) { + continue; + } + + BLI_array_growone(edges); + BLI_array_growone(loops); + + edges[a] = e2; + loops[a] = l; + + a++; + } + } + + if (BLI_array_count(loops) < 3) + continue; + v = loops[0]->v; + v2 = loops[1]->v; + + if (BMO_elem_flag_test(bm, v, ELE_DEL)) { + v = BMO_slot_map_ptr_get(bm, op, "targetmap", v); + } + if (BMO_elem_flag_test(bm, v2, ELE_DEL)) { + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2); + } + + f2 = BM_face_create_ngon(bm, v, v2, edges, a, TRUE); + if (f2 && (f2 != f)) { + BM_elem_attrs_copy(bm, bm, f, f2); + + a = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f2) { + l2 = loops[a]; + BM_elem_attrs_copy(bm, bm, l2, l); + + a++; + } + } + } + + BMO_op_callf(bm, "del geom=%fvef context=%i", ELE_DEL, DEL_ONLYTAGGED); + + BLI_array_free(edges); + BLI_array_free(loops); +} + +static int vergaverco(const void *e1, const void *e2) +{ + const BMVert *v1 = *(void **)e1, *v2 = *(void **)e2; + float x1 = v1->co[0] + v1->co[1] + v1->co[2]; + float x2 = v2->co[0] + v2->co[1] + v2->co[2]; + + if (x1 > x2) return 1; + else if (x1 < x2) return -1; + else return 0; +} + +#define VERT_TESTED 1 +#define VERT_DOUBLE 2 +#define VERT_TARGET 4 +#define VERT_KEEP 8 +#define VERT_MARK 16 +#define VERT_IN 32 + +#define EDGE_MARK 1 + +void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMVert *v, *snapv; + BMLoop *l, *firstl = NULL; + float fac; + int i, tot; + + snapv = BMO_iter_new(&siter, bm, op, "snapv", BM_VERT); + tot = BM_vert_face_count(snapv); + + if (!tot) + return; + + fac = 1.0f / tot; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, snapv) { + if (!firstl) { + firstl = l; + } + + for (i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) { + int type = bm->ldata.layers[i].type; + void *e1, *e2; + + e1 = CustomData_bmesh_get_layer_n(&bm->ldata, firstl->head.data, i); + e2 = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + + CustomData_data_multiply(type, e2, fac); + + if (l != firstl) + CustomData_data_add(type, e1, e2); + } + } + } + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) { + if (l == firstl) { + continue; + } + + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, firstl->head.data, &l->head.data); + } + } +} + +void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMVert *v; + BMLoop *l /* , *firstl = NULL */; + CDBlockBytes min, max; + void *block; + int i, type; + + for (i = 0; i < bm->ldata.totlayer; i++) { + if (!CustomData_layer_has_math(&bm->ldata, i)) + continue; + + type = bm->ldata.layers[i].type; + CustomData_data_initminmax(type, &min, &max); + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) { + block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + CustomData_data_dominmax(type, block, &min, &max); + } + } + + CustomData_data_multiply(type, &min, 0.5f); + CustomData_data_multiply(type, &max, 0.5f); + CustomData_data_add(type, &min, &max); + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) { + block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + CustomData_data_copy_value(type, &min, block); + } + } + } +} + +void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op) +{ + BMOperator weldop; + BMOIter siter; + BMVert *v, *snapv = NULL; + float vec[3]; + + BMO_slot_vec_get(op, "mergeco", vec); + + //BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges"); + BMO_op_init(bm, &weldop, "weldverts"); + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + if (!snapv) { + snapv = v; + copy_v3_v3(snapv->co, vec); + } + else { + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", v, snapv); + } + } + + BMO_op_exec(bm, &weldop); + BMO_op_finish(bm, &weldop); +} + +void bmesh_collapse_exec(BMesh *bm, BMOperator *op) +{ + BMOperator weldop; + BMWalker walker; + BMIter iter; + BMEdge *e, **edges = NULL; + BLI_array_declare(edges); + float min[3], max[3]; + int i, tot; + + BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges"); + BMO_op_init(bm, &weldop, "weldverts"); + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + BMW_init(&walker, bm, BMW_SHELL, + BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_NIL_LAY); + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) + continue; + + e = BMW_begin(&walker, e->v1); + BLI_array_empty(edges); + + INIT_MINMAX(min, max); + for (tot = 0; e; tot++, e = BMW_step(&walker)) { + BLI_array_growone(edges); + edges[tot] = e; + + DO_MINMAX(e->v1->co, min, max); + DO_MINMAX(e->v2->co, min, max); + } + + add_v3_v3v3(min, min, max); + mul_v3_fl(min, 0.5f); + + /* snap edges to a point. for initial testing purposes anyway */ + for (i = 0; i < tot; i++) { + copy_v3_v3(edges[i]->v1->co, min); + copy_v3_v3(edges[i]->v2->co, min); + + if (edges[i]->v1 != edges[0]->v1) + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v1, edges[0]->v1); + if (edges[i]->v2 != edges[0]->v1) + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v2, edges[0]->v1); + } + } + + BMO_op_exec(bm, &weldop); + BMO_op_finish(bm, &weldop); + + BMW_end(&walker); + BLI_array_free(edges); +} + +/* uv collapse functio */ +static void bmesh_collapsecon_do_layer(BMesh *bm, BMOperator *op, int layer) +{ + BMIter iter, liter; + BMFace *f; + BMLoop *l, *l2; + BMWalker walker; + void **blocks = NULL; + BLI_array_declare(blocks); + CDBlockBytes min, max; + int i, tot, type = bm->ldata.layers[layer].type; + + /* clear all short flags */ + BMO_mesh_flag_disable_all(bm, op, BM_ALL, (1 << 16) - 1); + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND, + BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP, + layer); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (BMO_elem_flag_test(bm, l->e, EDGE_MARK)) { + /* wal */ + BLI_array_empty(blocks); + tot = 0; + l2 = BMW_begin(&walker, l); + + CustomData_data_initminmax(type, &min, &max); + for (tot = 0; l2; tot++, l2 = BMW_step(&walker)) { + BLI_array_growone(blocks); + blocks[tot] = CustomData_bmesh_get_layer_n(&bm->ldata, l2->head.data, layer); + CustomData_data_dominmax(type, blocks[tot], &min, &max); + } + + if (tot) { + CustomData_data_multiply(type, &min, 0.5f); + CustomData_data_multiply(type, &max, 0.5f); + CustomData_data_add(type, &min, &max); + + /* snap CD (uv, vcol) points to their centroi */ + for (i = 0; i < tot; i++) { + CustomData_data_copy_value(type, &min, blocks[i]); + } + } + } + } + } + + BMW_end(&walker); + BLI_array_free(blocks); +} + +void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op) +{ + int i; + + for (i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) + bmesh_collapsecon_do_layer(bm, op, i); + } +} + +void bmesh_finddoubles_common(BMesh *bm, BMOperator *op, BMOperator *optarget, const char *targetmapname) +{ + BMOIter oiter; + BMVert *v, *v2; + BMVert **verts = NULL; + BLI_array_declare(verts); + float dist, dist3; + int i, j, len, keepvert = 0; + + dist = BMO_slot_float_get(op, "dist"); + dist3 = dist * 3.0f; + + i = 0; + BMO_ITER(v, &oiter, bm, op, "verts", BM_VERT) { + BLI_array_growone(verts); + verts[i++] = v; + } + + /* Test whether keepverts arg exists and is non-empty */ + if (BMO_slot_exists(op, "keepverts")) { + keepvert = BMO_iter_new(&oiter, bm, op, "keepverts", BM_VERT) != NULL; + } + + /* sort by vertex coordinates added togethe */ + qsort(verts, BLI_array_count(verts), sizeof(void *), vergaverco); + + /* Flag keepverts */ + if (keepvert) { + BMO_slot_buffer_flag_enable(bm, op, "keepverts", VERT_KEEP, BM_VERT); + } + + len = BLI_array_count(verts); + for (i = 0; i < len; i++) { + v = verts[i]; + if (BMO_elem_flag_test(bm, v, VERT_DOUBLE)) continue; + + for (j = i + 1; j < len; j++) { + v2 = verts[j]; + + /* Compare sort values of the verts using 3x tolerance (allowing for the tolerance + * on each of the three axes). This avoids the more expensive length comparison + * for most vertex pairs. */ + if ((v2->co[0]+v2->co[1]+v2->co[2])-(v->co[0]+v->co[1]+v->co[2]) > dist3) + break; + + if (keepvert) { + if (BMO_elem_flag_test(bm, v2, VERT_KEEP) == BMO_elem_flag_test(bm, v, VERT_KEEP)) + continue; + } + + if (compare_len_v3v3(v->co, v2->co, dist)) { + + /* If one vert is marked as keep, make sure it will be the target */ + if (BMO_elem_flag_test(bm, v2, VERT_KEEP)) { + SWAP(BMVert *, v, v2); + } + + BMO_elem_flag_enable(bm, v2, VERT_DOUBLE); + BMO_elem_flag_enable(bm, v, VERT_TARGET); + + BMO_slot_map_ptr_insert(bm, optarget, targetmapname, v2, v); + } + } + } + + BLI_array_free(verts); +} + +void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op) +{ + BMOperator weldop; + + BMO_op_init(bm, &weldop, "weldverts"); + bmesh_finddoubles_common(bm, op, &weldop, "targetmap"); + BMO_op_exec(bm, &weldop); + BMO_op_finish(bm, &weldop); +} + + +void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op) +{ + bmesh_finddoubles_common(bm, op, op, "targetmapout"); +} + +void bmesh_automerge_exec(BMesh *bm, BMOperator *op) +{ + BMOperator findop, weldop; + BMIter viter; + BMVert *v; + + /* The "verts" input sent to this op is the set of verts that + * can be merged away into any other verts. Mark all other verts + * as VERT_KEEP. */ + BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_IN, BM_VERT); + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, v, VERT_IN)) { + BMO_elem_flag_enable(bm, v, VERT_KEEP); + } + } + + /* Search for doubles among all vertices, but only merge non-VERT_KEEP + * vertices into VERT_KEEP vertices. */ + BMO_op_initf(bm, &findop, "finddoubles verts=%av keepverts=%fv", VERT_KEEP); + BMO_slot_copy(op, &findop, "dist", "dist"); + BMO_op_exec(bm, &findop); + + /* weld the vertices */ + BMO_op_init(bm, &weldop, "weldverts"); + BMO_slot_copy(&findop, &weldop, "targetmapout", "targetmap"); + BMO_op_exec(bm, &weldop); + + BMO_op_finish(bm, &findop); + BMO_op_finish(bm, &weldop); +} diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c new file mode 100644 index 00000000000..310762e0e37 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_subdivide.c @@ -0,0 +1,1104 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_array.h" +#include "BLI_noise.h" + +#include "BKE_customdata.h" + +#include "DNA_object_types.h" + +#include "ED_mesh.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +#include "bmo_subdivide.h" /* own include */ + +/* flags for all elements share a common bitfield space */ +#define SUBD_SPLIT 1 + +#define EDGE_PERCENT 2 + +/* I don't think new faces are flagged, currently, but + * better safe than sorry. */ +#define FACE_CUSTOMFILL 4 +#define ELE_INNER 8 +#define ELE_SPLIT 16 + +/* + * NOTE: beauty has been renamed to flag! + */ + +/* generic subdivision rules: + * + * - two selected edges in a face should make a link + * between them. + * + * - one edge should do, what? make pretty topology, or just + * split the edge only? + */ + +/* connects face with smallest len, which I think should always be correct for + * edge subdivision */ +static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_nf) +{ + BMIter iter, iter2; + BMVert *v; + BMLoop *nl; + BMFace *face, *curf = NULL; + + /* this isn't the best thing in the world. it doesn't handle cases where there's + * multiple faces yet. that might require a convexity test to figure out which + * face is "best," and who knows what for non-manifold conditions. */ + for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) { + for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) { + if (v == v2) { + if (!curf || face->len < curf->len) curf = face; + } + } + } + + if (curf) { + face = BM_face_split(bm, curf, v1, v2, &nl, NULL); + + if (r_nf) *r_nf = face; + return nl ? nl->e : NULL; + } + + return NULL; +} +/* calculates offset for co, based on fractal, sphere or smooth settings */ +static void alter_co(BMesh *bm, BMVert *v, BMEdge *UNUSED(origed), const subdparams *params, float perc, + BMVert *vsta, BMVert *vend) +{ + float tvec[3], prev_co[3], fac; + float *co = NULL; + int i, totlayer = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY); + + BM_vert_normal_update_all(bm, v); + + co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, params->origkey); + copy_v3_v3(prev_co, co); + + if (params->beauty & B_SMOOTH) { + /* we calculate an offset vector vec1[], to be added to *co */ + float len, nor[3], nor1[3], nor2[3], smooth = params->smooth; + + sub_v3_v3v3(nor, vsta->co, vend->co); + len = 0.5f * normalize_v3(nor); + + copy_v3_v3(nor1, vsta->no); + copy_v3_v3(nor2, vend->no); + + /* cosine angle */ + fac= dot_v3v3(nor, nor1); + mul_v3_v3fl(tvec, nor1, fac); + + /* cosine angle */ + fac = -dot_v3v3(nor, nor2); + madd_v3_v3fl(tvec, nor2, fac); + + /* falloff for multi subdivide */ + smooth *= sqrtf(fabsf(1.0f - 2.0f * fabsf(0.5f-perc))); + + mul_v3_fl(tvec, smooth * len); + + add_v3_v3(co, tvec); + } + else if (params->beauty & B_SPHERE) { /* subdivide sphere */ + normalize_v3(co); + mul_v3_fl(co, params->smooth); + } + + if (params->beauty & B_FRACTAL) { + float len = len_v3v3(vsta->co, vend->co); + float vec2[3] = {0.0f, 0.0f, 0.0f}, co2[3]; + + fac = params->fractal * len; + + add_v3_v3(vec2, vsta->no); + add_v3_v3(vec2, vend->no); + mul_v3_fl(vec2, 0.5f); + + add_v3_v3v3(co2, v->co, params->off); + tvec[0] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f); + tvec[1] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f); + tvec[2] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f); + + mul_v3_v3(vec2, tvec); + + /* add displacemen */ + add_v3_v3v3(co, co, vec2); + } + + /* apply the new difference to the rest of the shape keys, + * note that this doent take rotations into account, we _could_ support + * this by getting the normals and coords for each shape key and + * re-calculate the smooth value for each but this is quite involved. + * for now its ok to simply apply the difference IMHO - campbell */ + sub_v3_v3v3(tvec, prev_co, co); + + for (i = 0; i < totlayer; i++) { + if (params->origkey != i) { + co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, i); + sub_v3_v3(co, tvec); + } + } + +} + +/* assumes in the edge is the correct interpolated vertices already */ +/* percent defines the interpolation, rad and flag are for special options */ +/* results in new vertex with correct coordinate, vertex normal and weight group info */ +static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge, + const subdparams *params, float percent, + float percent2, + BMEdge **out, BMVert *vsta, BMVert *vend) +{ + BMVert *ev; + + ev = BM_edge_split(bm, edge->v1, edge, out, percent); + + BMO_elem_flag_enable(bm, ev, ELE_INNER); + + /* offset for smooth or sphere or fractal */ + alter_co(bm, ev, oedge, params, percent2, vsta, vend); + +#if 0 //BMESH_TODO + /* clip if needed by mirror modifier */ + if (edge->v1->f2) { + if (edge->v1->f2 & edge->v2->f2 & 1) { + co[0] = 0.0f; + } + if (edge->v1->f2 & edge->v2->f2 & 2) { + co[1] = 0.0f; + } + if (edge->v1->f2 & edge->v2->f2 & 4) { + co[2] = 0.0f; + } + } +#endif + + return ev; +} + +static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, BMEdge *oedge, + int curpoint, int totpoint, const subdparams *params, + BMEdge **newe, BMVert *vsta, BMVert *vend) +{ + BMVert *ev; + float percent, percent2 = 0.0f; + + if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1) + percent = BMO_slot_map_float_get(bm, params->op, "edgepercents", edge); + else { + percent = 1.0f / (float)(totpoint + 1-curpoint); + percent2 = (float)(curpoint + 1) / (float)(totpoint + 1); + + } + + ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent, + percent2, newe, vsta, vend); + return ev; +} + +static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const subdparams *params, + BMVert *vsta, BMVert *vend) +{ + BMEdge *eed = edge, *newe, temp = *edge; + BMVert *v, ov1 = *edge->v1, ov2 = *edge->v2, *v1 = edge->v1, *v2 = edge->v2; + int i, numcuts = params->numcuts; + + temp.v1 = &ov1; + temp.v2 = &ov2; + + for (i = 0; i < numcuts; i++) { + v = subdivideedgenum(bm, eed, &temp, i, params->numcuts, params, &newe, vsta, vend); + + BMO_elem_flag_enable(bm, v, SUBD_SPLIT); + BMO_elem_flag_enable(bm, eed, SUBD_SPLIT); + BMO_elem_flag_enable(bm, newe, SUBD_SPLIT); + + BMO_elem_flag_enable(bm, v, ELE_SPLIT); + BMO_elem_flag_enable(bm, eed, ELE_SPLIT); + BMO_elem_flag_enable(bm, newe, SUBD_SPLIT); + + BM_CHECK_ELEMENT(bm, v); + if (v->e) BM_CHECK_ELEMENT(bm, v->e); + if (v->e && v->e->l) BM_CHECK_ELEMENT(bm, v->e->l->f); + } + + alter_co(bm, v1, &temp, params, 0, &ov1, &ov2); + alter_co(bm, v2, &temp, params, 1.0, &ov1, &ov2); +} + +/* note: the patterns are rotated as necassary to + * match the input geometry. they're based on the + * pre-split state of the face */ + +/* + * v3---------v2 + * | | + * | | + * | | + * | | + * v4---v0---v1 + */ +static void quad_1edge_split(BMesh *bm, BMFace *UNUSED(face), + BMVert **verts, const subdparams *params) +{ + BMFace *nf; + int i, add, numcuts = params->numcuts; + + /* if it's odd, the middle face is a quad, otherwise it's a triangl */ + if ((numcuts % 2) == 0) { + add = 2; + for (i = 0; i < numcuts; i++) { + if (i == numcuts / 2) { + add -= 1; + } + connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf); + } + } + else { + add = 2; + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf); + if (i == numcuts/2) { + add -= 1; + connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf); + } + } + + } +} + +static SubDPattern quad_1edge = { + {1, 0, 0, 0}, + quad_1edge_split, + 4, +}; + + +/* + * v6--------v5 + * | | + * | |v4s + * | |v3s + * | s s | + * v7-v0--v1-v2 + */ +static void quad_2edge_split_path(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + int i, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf); + } + connect_smallest_face(bm, verts[numcuts * 2 + 3], verts[numcuts * 2 + 1], &nf); +} + +static SubDPattern quad_2edge_path = { + {1, 1, 0, 0}, + quad_2edge_split_path, + 4, +}; + +/* + * v6--------v5 + * | | + * | |v4s + * | |v3s + * | s s | + * v7-v0--v1-v2 + */ +static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + BMVert *v, *lastv; + BMEdge *e, *ne, olde; + int i, numcuts = params->numcuts; + + lastv = verts[numcuts]; + + for (i = numcuts - 1; i >= 0; i--) { + e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf); + + olde = *e; + v = bm_subdivide_edge_addvert(bm, e, &olde, params, 0.5f, 0.5f, &ne, e->v1, e->v2); + + if (i != numcuts - 1) { + connect_smallest_face(bm, lastv, v, &nf); + } + + lastv = v; + } + + connect_smallest_face(bm, lastv, verts[numcuts * 2 + 2], &nf); +} + +static SubDPattern quad_2edge_innervert = { + {1, 1, 0, 0}, + quad_2edge_split_innervert, + 4, +}; + +/* + * v6--------v5 + * | | + * | |v4s + * | |v3s + * | s s | + * v7-v0--v1-v2 + * + */ +static void quad_2edge_split_fan(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + /* BMVert *v; */ /* UNUSED */ + /* BMVert *lastv = verts[2]; */ /* UNUSED */ + /* BMEdge *e, *ne; */ /* UNUSED */ + int i, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts * 2 + 2], &nf); + connect_smallest_face(bm, verts[numcuts + (numcuts - i)], verts[numcuts * 2 + 2], &nf); + } +} + +static SubDPattern quad_2edge_fan = { + {1, 1, 0, 0}, + quad_2edge_split_fan, + 4, +}; + +/* + * s s + * v8--v7--v6-v5 + * | | + * | v4 s + * | | + * | v3 s + * | s s | + * v9-v0--v1-v2 + */ +static void quad_3edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + int i, add = 0, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + if (i == numcuts / 2) { + if (numcuts % 2 != 0) { + connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf); + } + add = numcuts * 2 + 2; + } + connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf); + } + + for (i = 0; i < numcuts / 2 + 1; i++) { + connect_smallest_face(bm, verts[i], verts[(numcuts - i) + numcuts * 2 + 1], &nf); + } +} + +static SubDPattern quad_3edge = { + {1, 1, 1, 0}, + quad_3edge_split, + 4, +}; + +/* + * v8--v7-v6--v5 + * | s | + * |v9 s s|v4 + * first line | | last line + * |v10s s s|v3 + * v11-v0--v1-v2 + * + * it goes from bottom up + */ +static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + BMVert *v, *v1, *v2; + BMEdge *e, *ne, temp; + BMVert **lines; + int numcuts = params->numcuts; + int i, j, a, b, s = numcuts + 2 /* , totv = numcuts * 4 + 4 */; + + lines = MEM_callocN(sizeof(BMVert *)*(numcuts + 2)*(numcuts + 2), "q_4edge_split"); + /* build a 2-dimensional array of verts, + * containing every vert (and all new ones) + * in the face */ + + /* first line */ + for (i = 0; i < numcuts + 2; i++) { + lines[i] = verts[numcuts * 3 + 2 + (numcuts - i + 1)]; + } + + /* last line */ + for (i = 0; i < numcuts + 2; i++) { + lines[(s - 1) * s + i] = verts[numcuts + i]; + } + + /* first and last members of middle lines */ + for (i = 0; i < numcuts; i++) { + a = i; + b = numcuts + 1 + numcuts + 1 + (numcuts - i - 1); + + e = connect_smallest_face(bm, verts[a], verts[b], &nf); + if (!e) + continue; + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + + + v1 = lines[(i + 1)*s] = verts[a]; + v2 = lines[(i + 1)*s + s - 1] = verts[b]; + + temp = *e; + for (a = 0; a < numcuts; a++) { + v = subdivideedgenum(bm, e, &temp, a, numcuts, params, &ne, + v1, v2); + if (!v) + bmesh_error(); + + BMO_elem_flag_enable(bm, ne, ELE_INNER); + lines[(i + 1) * s + a + 1] = v; + } + } + + for (i = 1; i < numcuts + 2; i++) { + for (j = 1; j < numcuts + 1; j++) { + a = i * s + j; + b = (i - 1) * s + j; + e = connect_smallest_face(bm, lines[a], lines[b], &nf); + if (!e) + continue; + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + } + } + + MEM_freeN(lines); +} + +/* + * v3 + * / \ + * / \ + * / \ + * / \ + * / \ + * v4--v0--v1--v2 + * s s + */ +static void tri_1edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + int i, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts + 1], &nf); + } +} + +static SubDPattern tri_1edge = { + {1, 0, 0}, + tri_1edge_split, + 3, +}; + +/* v5 + * / \ + * s v6/---\ v4 s + * / \ / \ + * sv7/---v---\ v3 s + * / \/ \/ \ + * v8--v0--v1--v2 + * s s + */ +static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + BMEdge *e, *ne, temp; + BMVert ***lines, *v, ov1, ov2; + void *stackarr[1]; + int i, j, a, b, numcuts = params->numcuts; + + /* number of verts in each lin */ + lines = MEM_callocN(sizeof(void *)*(numcuts + 2), "triangle vert table"); + + lines[0] = (BMVert **) stackarr; + lines[0][0] = verts[numcuts * 2 + 1]; + + lines[numcuts + 1] = MEM_callocN(sizeof(void *) * (numcuts + 2), "triangle vert table 2"); + for (i = 0; i < numcuts; i++) { + lines[numcuts + 1][i + 1] = verts[i]; + } + lines[numcuts + 1][0] = verts[numcuts * 3 + 2]; + lines[numcuts + 1][numcuts + 1] = verts[numcuts]; + + for (i = 0; i < numcuts; i++) { + lines[i + 1] = MEM_callocN(sizeof(void *)*(2 + i), "triangle vert table row"); + a = numcuts * 2 + 2 + i; + b = numcuts + numcuts - i; + e = connect_smallest_face(bm, verts[a], verts[b], &nf); + if (!e) goto cleanup; + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + + lines[i + 1][0] = verts[a]; + lines[i + 1][i + 1] = verts[b]; + + temp = *e; + ov1 = *verts[a]; + ov2 = *verts[b]; + temp.v1 = &ov1; + temp.v2 = &ov2; + for (j = 0; j < i; j++) { + v = subdivideedgenum(bm, e, &temp, j, i, params, &ne, + verts[a], verts[b]); + lines[i + 1][j + 1] = v; + + BMO_elem_flag_enable(bm, ne, ELE_INNER); + } + } + + /* + * v5 + * / \ + * s v6/---\ v4 s + * / \ / \ + * sv7/---v---\ v3 s + * / \/ \/ \ + * v8--v0--v1--v2 + * s s + */ + for (i = 1; i < numcuts + 1; i++) { + for (j = 0; j < i; j++) { + e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &nf); + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + + e = connect_smallest_face(bm, lines[i][j + 1], lines[i + 1][j + 1], &nf); + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + } + } + +cleanup: + for (i = 1; i < numcuts + 2; i++) { + if (lines[i]) MEM_freeN(lines[i]); + } + + MEM_freeN(lines); +} + +static SubDPattern tri_3edge = { + {1, 1, 1}, + tri_3edge_subdivide, + 3, +}; + + +static SubDPattern quad_4edge = { + {1, 1, 1, 1}, + quad_4edge_subdivide, + 4, +}; + +static SubDPattern *patterns[] = { + NULL, //quad single edge pattern is inserted here + NULL, //quad corner vert pattern is inserted here + NULL, //tri single edge pattern is inserted here + NULL, + &quad_3edge, + NULL, +}; + +#define PLEN (sizeof(patterns) / sizeof(void *)) + +typedef struct subd_facedata { + BMVert *start; SubDPattern *pat; + int totedgesel; //only used if pat was NULL, e.g. no pattern was found + BMFace *face; +} subd_facedata; + +void esubdivide_exec(BMesh *bmesh, BMOperator *op) +{ + BMOpSlot *einput; + SubDPattern *pat; + subdparams params; + subd_facedata *facedata = NULL; + BMIter viter, fiter, liter; + BMVert *v, **verts = NULL; + BMEdge *edge, **edges = NULL; + BMLoop *nl, *l, **splits = NULL, **loops = NULL; + BMFace *face; + BLI_array_declare(splits); + BLI_array_declare(loops); + BLI_array_declare(facedata); + BLI_array_declare(edges); + BLI_array_declare(verts); + float smooth, fractal; + int beauty, cornertype, singleedge, gridfill; + int skey, seed, i, j, matched, a, b, numcuts, totesel; + + BMO_slot_buffer_flag_enable(bmesh, op, "edges", SUBD_SPLIT, BM_EDGE); + + numcuts = BMO_slot_int_get(op, "numcuts"); + seed = BMO_slot_int_get(op, "seed"); + smooth = BMO_slot_float_get(op, "smooth"); + fractal = BMO_slot_float_get(op, "fractal"); + beauty = BMO_slot_int_get(op, "beauty"); + cornertype = BMO_slot_int_get(op, "quadcornertype"); + singleedge = BMO_slot_int_get(op, "singleedge"); + gridfill = BMO_slot_int_get(op, "gridfill"); + + BLI_srandom(seed); + + patterns[1] = NULL; + //straight cut is patterns[1] == NULL + switch (cornertype) { + case SUBD_PATH: + patterns[1] = &quad_2edge_path; + break; + case SUBD_INNERVERT: + patterns[1] = &quad_2edge_innervert; + break; + case SUBD_FAN: + patterns[1] = &quad_2edge_fan; + break; + } + + if (singleedge) { + patterns[0] = &quad_1edge; + patterns[2] = &tri_1edge; + } + else { + patterns[0] = NULL; + patterns[2] = NULL; + } + + if (gridfill) { + patterns[3] = &quad_4edge; + patterns[5] = &tri_3edge; + } + else { + patterns[3] = NULL; + patterns[5] = NULL; + } + + /* add a temporary shapekey layer to store displacements on current geometr */ + BM_data_layer_add(bmesh, &bmesh->vdata, CD_SHAPEKEY); + skey = CustomData_number_of_layers(&bmesh->vdata, CD_SHAPEKEY) - 1; + + BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) { + float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey); + copy_v3_v3(co, v->co); + } + + /* first go through and tag edge */ + BMO_slot_from_flag(bmesh, op, "edges", + SUBD_SPLIT, BM_EDGE); + + params.numcuts = numcuts; + params.op = op; + params.smooth = smooth; + params.seed = seed; + params.fractal = fractal; + params.beauty = beauty; + params.origkey = skey; + params.off[0] = (float)BLI_drand() * 200.0f; + params.off[1] = (float)BLI_drand() * 200.0f; + params.off[2] = (float)BLI_drand() * 200.0f; + + BMO_slot_map_to_flag(bmesh, op, "custompatterns", + FACE_CUSTOMFILL); + + BMO_slot_map_to_flag(bmesh, op, "edgepercents", + EDGE_PERCENT); + + for (face = BM_iter_new(&fiter, bmesh, BM_FACES_OF_MESH, NULL); + face; + face = BM_iter_step(&fiter)) + { + BMEdge *e1 = NULL, *e2 = NULL; + float vec1[3], vec2[3]; + + /* figure out which pattern to us */ + + BLI_array_empty(edges); + BLI_array_empty(verts); + matched = 0; + + i = 0; + totesel = 0; + for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) { + BLI_array_growone(edges); + BLI_array_growone(verts); + edges[i] = nl->e; + verts[i] = nl->v; + + if (BMO_elem_flag_test(bmesh, edges[i], SUBD_SPLIT)) { + if (!e1) e1 = edges[i]; + else e2 = edges[i]; + + totesel++; + } + + i++; + } + + /* make sure the two edges have a valid angle to each othe */ + if (totesel == 2 && BM_edge_share_vert(e1, e2)) { + float angle; + + sub_v3_v3v3(vec1, e1->v2->co, e1->v1->co); + sub_v3_v3v3(vec2, e2->v2->co, e2->v1->co); + normalize_v3(vec1); + normalize_v3(vec2); + + angle = dot_v3v3(vec1, vec2); + angle = fabsf(angle); + if (fabsf(angle - 1.0f) < 0.01f) { + totesel = 0; + } + } + + if (BMO_elem_flag_test(bmesh, face, FACE_CUSTOMFILL)) { + pat = BMO_slot_map_data_get(bmesh, op, + "custompatterns", face); + for (i = 0; i < pat->len; i++) { + matched = 1; + for (j = 0; j < pat->len; j++) { + a = (j + i) % pat->len; + if ((!!BMO_elem_flag_test(bmesh, edges[a], SUBD_SPLIT)) != (!!pat->seledges[j])) { + matched = 0; + break; + } + } + if (matched) { + BLI_array_growone(facedata); + b = BLI_array_count(facedata) - 1; + facedata[b].pat = pat; + facedata[b].start = verts[i]; + facedata[b].face = face; + facedata[b].totedgesel = totesel; + BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT); + break; + } + } + + /* obvously don't test for other patterns matchin */ + continue; + } + + for (i = 0; i < PLEN; i++) { + pat = patterns[i]; + if (!pat) continue; + + if (pat->len == face->len) { + for (a = 0; a < pat->len; a++) { + matched = 1; + for (b = 0; b < pat->len; b++) { + j = (b + a) % pat->len; + if ((!!BMO_elem_flag_test(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) { + matched = 0; + break; + } + } + if (matched) { + break; + } + } + if (matched) { + BLI_array_growone(facedata); + j = BLI_array_count(facedata) - 1; + + BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT); + + facedata[j].pat = pat; + facedata[j].start = verts[a]; + facedata[j].face = face; + facedata[j].totedgesel = totesel; + break; + } + } + + } + + if (!matched && totesel) { + BLI_array_growone(facedata); + j = BLI_array_count(facedata) - 1; + + BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT); + facedata[j].totedgesel = totesel; + facedata[j].face = face; + } + } + + einput = BMO_slot_get(op, "edges"); + + /* go through and split edge */ + for (i = 0; i < einput->len; i++) { + edge = ((BMEdge **)einput->data.p)[i]; + bm_subdivide_multicut(bmesh, edge, ¶ms, edge->v1, edge->v2); + } + + i = 0; + for (i = 0; i < BLI_array_count(facedata); i++) { + face = facedata[i].face; + + /* figure out which pattern to us */ + BLI_array_empty(verts); + + pat = facedata[i].pat; + + if (!pat && facedata[i].totedgesel == 2) { + int vlen; + + /* ok, no pattern. we still may be able to do something */ + BLI_array_empty(loops); + BLI_array_empty(splits); + + /* for case of two edges, connecting them shouldn't be too har */ + BM_ITER(l, &liter, bmesh, BM_LOOPS_OF_FACE, face) { + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = l; + } + + vlen = BLI_array_count(loops); + + /* find the boundary of one of the split edge */ + for (a = 1; a < vlen; a++) { + if (!BMO_elem_flag_test(bmesh, loops[a - 1]->v, ELE_INNER) && + BMO_elem_flag_test(bmesh, loops[a]->v, ELE_INNER)) + { + break; + } + } + + if (BMO_elem_flag_test(bmesh, loops[(a + numcuts + 1) % vlen]->v, ELE_INNER)) { + b = (a + numcuts + 1) % vlen; + } + else { + /* find the boundary of the other edge. */ + for (j = 0; j < vlen; j++) { + b = (j + a + numcuts + 1) % vlen; + if (!BMO_elem_flag_test(bmesh, loops[b == 0 ? vlen - 1 : b - 1]->v, ELE_INNER) && + BMO_elem_flag_test(bmesh, loops[b]->v, ELE_INNER)) + { + break; + } + } + } + + b += numcuts - 1; + + for (j = 0; j < numcuts; j++) { + BLI_array_growone(splits); + splits[BLI_array_count(splits) - 1] = loops[a]; + + BLI_array_growone(splits); + splits[BLI_array_count(splits) - 1] = loops[b]; + + b = (b - 1) % vlen; + a = (a + 1) % vlen; + } + + //BM_face_legal_splits(bmesh, face, splits, BLI_array_count(splits)/2); + + for (j = 0; j < BLI_array_count(splits) / 2; j++) { + if (splits[j * 2]) { + /* BMFace *nf = */ /* UNUSED */ + BM_face_split(bmesh, face, splits[j * 2]->v, splits[j * 2 + 1]->v, &nl, NULL); + } + } + + continue; + } + else if (!pat) { + continue; + } + + j = a = 0; + for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); + nl; + nl = BM_iter_step(&liter)) + { + if (nl->v == facedata[i].start) { + a = j + 1; + break; + } + j++; + } + + for (j = 0; j < face->len; j++) { + BLI_array_growone(verts); + } + + j = 0; + for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) { + b = (j - a + face->len) % face->len; + verts[b] = nl->v; + j += 1; + } + + BM_CHECK_ELEMENT(bmesh, face); + pat->connectexec(bmesh, face, verts, ¶ms); + } + + /* copy original-geometry displacements to current coordinate */ + BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) { + float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey); + copy_v3_v3(v->co, co); + } + + BM_data_layer_free_n(bmesh, &bmesh->vdata, CD_SHAPEKEY, skey); + + if (facedata) BLI_array_free(facedata); + if (edges) BLI_array_free(edges); + if (verts) BLI_array_free(verts); + BLI_array_free(splits); + BLI_array_free(loops); + + BMO_slot_from_flag(bmesh, op, "outinner", + ELE_INNER, BM_ALL); + BMO_slot_from_flag(bmesh, op, "outsplit", + ELE_SPLIT, BM_ALL); + + BMO_slot_from_flag(bmesh, op, "geomout", + ELE_INNER|ELE_SPLIT|SUBD_SPLIT, BM_ALL); +} + +/* editmesh-emulating functio */ +void BM_mesh_esubdivideflag(Object *UNUSED(obedit), BMesh *bm, int flag, float smooth, + float fractal, int beauty, int numcuts, + int seltype, int cornertype, int singleedge, + int gridfill, int seed) +{ + BMOperator op; + + BMO_op_initf(bm, &op, "esubd edges=%he smooth=%f fractal=%f " + "beauty=%d numcuts=%d quadcornertype=%d singleedge=%d " + "gridfill=%d seed=%d", + flag, smooth, fractal, beauty, numcuts, + cornertype, singleedge, gridfill, seed); + + BMO_op_exec(bm, &op); + + if (seltype == SUBDIV_SELECT_INNER) { + BMOIter iter; + BMHeader *ele; + // int i; + + ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT); + for ( ; ele; ele = BMO_iter_step(&iter)) { + BM_elem_select_set(bm, ele, TRUE); + } + } + else if (seltype == SUBDIV_SELECT_LOOPCUT) { + BMOIter iter; + BMHeader *ele; + // int i; + + /* deselect input */ + BM_mesh_elem_flag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT); + + ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT); + for ( ; ele; ele = BMO_iter_step(&iter)) { + BM_elem_select_set(bm, ele, TRUE); + + if (ele->htype == BM_VERT) { + BMEdge *e; + BMIter eiter; + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, ele) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT)) + { + BM_elem_select_set(bm, e, TRUE); + bm->totedgesel += 1; + } + else if (BM_elem_flag_test(e, BM_ELEM_SELECT) && + (!BM_elem_flag_test(e->v1, BM_ELEM_SELECT) || + !BM_elem_flag_test(e->v2, BM_ELEM_SELECT))) + { + BM_elem_select_set(bm, e, FALSE); + bm->totedgesel -= 1; + } + } + } + } + } + + BMO_op_finish(bm, &op); +} + +void esplit_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e; + subdparams params; + int skey; + + params.numcuts = BMO_slot_get(op, "numcuts")->data.i; + params.op = op; + + BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY); + skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1; + + params.origkey = skey; + + /* go through and split edge */ + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + bm_subdivide_multicut(bm, e, ¶ms, e->v1, e->v2); + } + + BMO_slot_from_flag(bm, op, "outsplit", ELE_SPLIT, BM_ALL); + + BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey); +} diff --git a/source/blender/bmesh/operators/bmo_subdivide.h b/source/blender/bmesh/operators/bmo_subdivide.h new file mode 100644 index 00000000000..dd5198bdc30 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_subdivide.h @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMO_SUBDIVIDE_H__ +#define __BMO_SUBDIVIDE_H__ + +/** \file blender/bmesh/operators/bmo_subdivide.h + * \ingroup bmesh + */ + +typedef struct subdparams { + int numcuts; + float smooth; + float fractal; + int beauty; + int seed; + int origkey; /* shapekey holding displaced vertex coordinates for current geometry */ + BMOperator *op; + float off[3]; +} subdparams; + +typedef void (*subd_pattern_fill_fp)(BMesh *bm, BMFace *face, BMVert **verts, + const subdparams *params); + +/* + * note: this is a pattern-based edge subdivider. + * it tries to match a pattern to edge selections on faces, + * then executes functions to cut them. + */ +typedef struct SubDPattern { + int seledges[20]; /* selected edges mask, for splitting */ + + /* verts starts at the first new vert cut, not the first vert in the face */ + subd_pattern_fill_fp connectexec; + int len; /* total number of verts, before any subdivision */ +} SubDPattern; + +/* generic subdivision rules: + * + * - two selected edges in a face should make a link + * between them. + * + * - one edge should do, what? make pretty topology, or just + * split the edge only? + */ + +#endif /* __BMO_SUBDIVIDE_H__ */ diff --git a/source/blender/bmesh/operators/bmo_triangulate.c b/source/blender/bmesh/operators/bmo_triangulate.c new file mode 100644 index 00000000000..90efe0b6e44 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_triangulate.c @@ -0,0 +1,219 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_scanfill.h" +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_editVert.h" +#include "BLI_smallhash.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +#define EDGE_NEW 1 +#define FACE_NEW 1 + +#define ELE_NEW 1 +#define FACE_MARK 2 +#define EDGE_MARK 4 + +void triangulate_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *face, **newfaces = NULL; + BLI_array_declare(newfaces); + float (*projectverts)[3] = NULL; + BLI_array_declare(projectverts); + int i, lastlen = 0 /* , count = 0 */; + + face = BMO_iter_new(&siter, bm, op, "faces", BM_FACE); + for ( ; face; face = BMO_iter_step(&siter)) { + if (lastlen < face->len) { + BLI_array_empty(projectverts); + BLI_array_empty(newfaces); + for (lastlen = 0; lastlen < face->len; lastlen++) { + BLI_array_growone(projectverts); + BLI_array_growone(projectverts); + BLI_array_growone(projectverts); + BLI_array_growone(newfaces); + } + } + + BM_face_triangulate(bm, face, projectverts, EDGE_NEW, FACE_NEW, newfaces); + + BMO_slot_map_ptr_insert(bm, op, "facemap", face, face); + for (i = 0; newfaces[i]; i++) { + BMO_slot_map_ptr_insert(bm, op, "facemap", + newfaces[i], face); + + } + } + + BMO_slot_from_flag(bm, op, "edgeout", EDGE_NEW, BM_EDGE); + BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE); + + BLI_array_free(projectverts); + BLI_array_free(newfaces); +} + +void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMFace *f; + BMEdge *e; + int stop = 0; + + BMO_slot_buffer_flag_enable(bm, op, "constrain_edges", EDGE_MARK, BM_EDGE); + + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + if (f->len == 3) + BMO_elem_flag_enable(bm, f, FACE_MARK); + } + + while (!stop) { + stop = 1; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMVert *v1, *v2, *v3, *v4; + + if (BM_edge_face_count(e) != 2 || BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + if (!BMO_elem_flag_test(bm, e->l->f, FACE_MARK) || + !BMO_elem_flag_test(bm, e->l->radial_next->f, FACE_MARK)) + { + continue; + } + + v1 = e->l->prev->v; + v2 = e->l->v; + v3 = e->l->radial_next->prev->v; + v4 = e->l->next->v; + + if (is_quad_convex_v3(v1->co, v2->co, v3->co, v4->co)) { + float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2; + /* testing rule: + * the area divided by the total edge lengths + */ + len1 = len_v3v3(v1->co, v2->co); + len2 = len_v3v3(v2->co, v3->co); + len3 = len_v3v3(v3->co, v4->co); + len4 = len_v3v3(v4->co, v1->co); + len5 = len_v3v3(v1->co, v3->co); + len6 = len_v3v3(v2->co, v4->co); + + opp1 = area_tri_v3(v1->co, v2->co, v3->co); + opp2 = area_tri_v3(v1->co, v3->co, v4->co); + + fac1 = opp1 / (len1 + len2 + len5) + opp2 / (len3 + len4 + len5); + + opp1 = area_tri_v3(v2->co, v3->co, v4->co); + opp2 = area_tri_v3(v2->co, v4->co, v1->co); + + fac2 = opp1 / (len2 + len3 + len6) + opp2 / (len4 + len1 + len6); + + if (fac1 > fac2) { + e = BM_edge_rotate(bm, e, 0); + if (e) { + BMO_elem_flag_enable(bm, e, ELE_NEW); + + BMO_elem_flag_enable(bm, e->l->f, FACE_MARK|ELE_NEW); + BMO_elem_flag_enable(bm, e->l->radial_next->f, FACE_MARK|ELE_NEW); + stop = 0; + } + } + } + } + } + + BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE); +} + +void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e; + BMOperator bmop; + EditEdge *eed; + EditVert *eve, *v1, *v2; + EditFace *efa; + SmallHash hash; + + BLI_smallhash_init(&hash); + + BLI_begin_edgefill(); + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + + if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v1)) { + eve = BLI_addfillvert(e->v1->co); + eve->tmp.p = e->v1; + BLI_smallhash_insert(&hash, (uintptr_t)e->v1, eve); + } + + if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v2)) { + eve = BLI_addfillvert(e->v2->co); + eve->tmp.p = e->v2; + BLI_smallhash_insert(&hash, (uintptr_t)e->v2, eve); + } + + v1 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v1); + v2 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v2); + eed = BLI_addfilledge(v1, v2); + eed->tmp.p = e; + } + + BLI_edgefill(0); + + for (efa = fillfacebase.first; efa; efa = efa->next) { + BMFace *f = BM_face_create_quad_tri(bm, + efa->v1->tmp.p, efa->v2->tmp.p, efa->v3->tmp.p, NULL, + NULL, TRUE); + BMLoop *l; + BMIter liter; + + BMO_elem_flag_enable(bm, f, ELE_NEW); + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (!BMO_elem_flag_test(bm, l->e, EDGE_MARK)) { + BMO_elem_flag_enable(bm, l->e, ELE_NEW); + } + } + } + + BLI_end_edgefill(); + BLI_smallhash_release(&hash); + + /* clean up fill */ + BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", ELE_NEW, EDGE_MARK); + BMO_op_exec(bm, &bmop); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", ELE_NEW, BM_FACE|BM_EDGE); + BMO_op_finish(bm, &bmop); + + BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE); +} diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c new file mode 100644 index 00000000000..e0187476a6a --- /dev/null +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -0,0 +1,1297 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_heap.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +/* + * UTILS.C + * + * utility bmesh operators, e.g. transform, + * translate, rotate, scale, etc. + * + */ + +void bmesh_makevert_exec(BMesh *bm, BMOperator *op) +{ + float vec[3]; + + BMO_slot_vec_get(op, "co", vec); + + BMO_elem_flag_enable(bm, BM_vert_create(bm, vec, NULL), 1); + BMO_slot_from_flag(bm, op, "newvertout", 1, BM_VERT); +} + +void bmesh_transform_exec(BMesh *bm, BMOperator *op) +{ + BMOIter iter; + BMVert *v; + float mat[4][4]; + + BMO_slot_mat4_get(op, "mat", mat); + + BMO_ITER(v, &iter, bm, op, "verts", BM_VERT) { + mul_m4_v3(mat, v->co); + } +} + +void bmesh_translate_exec(BMesh *bm, BMOperator *op) +{ + float mat[4][4], vec[3]; + + BMO_slot_vec_get(op, "vec", vec); + + unit_m4(mat); + copy_v3_v3(mat[3], vec); + + BMO_op_callf(bm, "transform mat=%m4 verts=%s", mat, op, "verts"); +} + +void bmesh_scale_exec(BMesh *bm, BMOperator *op) +{ + float mat[3][3], vec[3]; + + BMO_slot_vec_get(op, "vec", vec); + + unit_m3(mat); + mat[0][0] = vec[0]; + mat[1][1] = vec[1]; + mat[2][2] = vec[2]; + + BMO_op_callf(bm, "transform mat=%m3 verts=%s", mat, op, "verts"); +} + +void bmesh_rotate_exec(BMesh *bm, BMOperator *op) +{ + float vec[3]; + + BMO_slot_vec_get(op, "cent", vec); + + /* there has to be a proper matrix way to do this, but + * this is how editmesh did it and I'm too tired to think + * through the math right now. */ + mul_v3_fl(vec, -1.0f); + BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec); + + BMO_op_callf(bm, "transform mat=%s verts=%s", op, "mat", op, "verts"); + + mul_v3_fl(vec, -1.0f); + BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec); +} + +void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *f; + + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + BM_face_normal_flip(bm, f); + } +} + +void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e, *e2; + int ccw = BMO_slot_int_get(op, "ccw"); + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + if (!(e2 = BM_edge_rotate(bm, e, ccw))) { + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge"); + return; + } + + BMO_elem_flag_enable(bm, e2, 1); + } + + BMO_slot_from_flag(bm, op, "edgeout", 1, BM_EDGE); +} + +#define SEL_FLAG 1 +#define SEL_ORIG 2 + +static void bmesh_regionextend_extend(BMesh *bm, BMOperator *op, int usefaces) +{ + BMVert *v; + BMEdge *e; + BMIter eiter; + BMOIter siter; + + if (!usefaces) { + BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) + break; + } + + if (e) { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); + } + } + } + } + else { + BMIter liter, fiter; + BMFace *f, *f2; + BMLoop *l; + + BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) { + if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) + BMO_elem_flag_enable(bm, f2, SEL_FLAG); + } + } + } + } +} + +static void bmesh_regionextend_constrict(BMesh *bm, BMOperator *op, int usefaces) +{ + BMVert *v; + BMEdge *e; + BMIter eiter; + BMOIter siter; + + if (!usefaces) { + BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) + break; + } + + if (e) { + BMO_elem_flag_enable(bm, v, SEL_FLAG); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + } + } + } + } + else { + BMIter liter, fiter; + BMFace *f, *f2; + BMLoop *l; + + BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) { + if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) { + BMO_elem_flag_enable(bm, f, SEL_FLAG); + break; + } + } + } + } + } +} + +void bmesh_regionextend_exec(BMesh *bm, BMOperator *op) +{ + int usefaces = BMO_slot_int_get(op, "usefaces"); + int constrict = BMO_slot_int_get(op, "constrict"); + + BMO_slot_buffer_flag_enable(bm, op, "geom", SEL_ORIG, BM_ALL); + + if (constrict) + bmesh_regionextend_constrict(bm, op, usefaces); + else + bmesh_regionextend_extend(bm, op, usefaces); + + BMO_slot_from_flag(bm, op, "geomout", SEL_FLAG, BM_ALL); +} + +/********* righthand faces implementation ****** */ + +#define FACE_VIS 1 +#define FACE_FLAG 2 +#define FACE_MARK 4 +#define FACE_FLIP 8 + +/* NOTE: these are the original righthandfaces comment in editmesh_mods.c, + * copied here for reference. */ + +/* based at a select-connected to witness loose objects */ + +/* count per edge the amount of faces + * find the ultimate left, front, upper face (not manhattan dist!!) + * also evaluate both triangle cases in quad, since these can be non-flat + * + * put normal to the outside, and set the first direction flags in edges + * + * then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces + * this is in fact the 'select connected' + * + * in case (selected) faces were not done: start over with 'find the ultimate ...' */ + +/* NOTE: this function uses recursion, which is a little unusual for a bmop + * function, but acceptable I think. */ + +/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ + +void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op) +{ + BMIter liter, liter2; + BMOIter siter; + BMFace *f, *startf, **fstack = NULL; + BLI_array_declare(fstack); + BMLoop *l, *l2; + float maxx, cent[3]; + int i, maxi, flagflip = BMO_slot_int_get(op, "doflip"); + + startf = NULL; + maxx = -1.0e10; + + BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_FLAG, BM_FACE); + + /* find a starting face */ + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + + /* clear dirty flag */ + BM_elem_flag_disable(f, BM_ELEM_TAG); + + if (BMO_elem_flag_test(bm, f, FACE_VIS)) + continue; + + if (!startf) startf = f; + + BM_face_center_bounds_calc(bm, f, cent); + + cent[0] = cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2]; + if (cent[0] > maxx) { + maxx = cent[0]; + startf = f; + } + } + + if (!startf) return; + + BM_face_center_bounds_calc(bm, startf, cent); + + /* make sure the starting face has the correct winding */ + if (dot_v3v3(cent, startf->no) < 0.0f) { + BM_face_normal_flip(bm, startf); + BMO_elem_flag_toggle(bm, startf, FACE_FLIP); + + if (flagflip) + BM_elem_flag_toggle(startf, BM_ELEM_TAG); + } + + /* now that we've found our starting face, make all connected faces + * have the same winding. this is done recursively, using a manual + * stack (if we use simple function recursion, we'd end up overloading + * the stack on large meshes). */ + + BLI_array_growone(fstack); + fstack[0] = startf; + BMO_elem_flag_enable(bm, startf, FACE_VIS); + + i = 0; + maxi = 1; + while (i >= 0) { + f = fstack[i]; + i--; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_LOOP, l) { + if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l) + continue; + + if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) { + BMO_elem_flag_enable(bm, l2->f, FACE_VIS); + i++; + + if (l2->v == l->v) { + BM_face_normal_flip(bm, l2->f); + + BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP); + if (flagflip) + BM_elem_flag_toggle(l2->f, BM_ELEM_TAG); + } + else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + if (flagflip) { + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + BM_elem_flag_disable(l2->f, BM_ELEM_TAG); + } + } + + if (i == maxi) { + BLI_array_growone(fstack); + maxi++; + } + + fstack[i] = l2->f; + } + } + } + } + + BLI_array_free(fstack); + + /* check if we have faces yet to do. if so, recurse */ + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + if (!BMO_elem_flag_test(bm, f, FACE_VIS)) { + bmesh_righthandfaces_exec(bm, op); + break; + } + } +} + +void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMVert *v; + BMEdge *e; + BLI_array_declare(cos); + float (*cos)[3] = NULL; + float *co, *co2, clipdist = BMO_slot_float_get(op, "clipdist"); + int i, j, clipx, clipy, clipz; + + clipx = BMO_slot_int_get(op, "mirror_clip_x"); + clipy = BMO_slot_int_get(op, "mirror_clip_y"); + clipz = BMO_slot_int_get(op, "mirror_clip_z"); + + i = 0; + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BLI_array_growone(cos); + co = cos[i]; + + j = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + co2 = BM_edge_other_vert(e, v)->co; + add_v3_v3v3(co, co, co2); + j += 1; + } + + if (!j) { + copy_v3_v3(co, v->co); + i++; + continue; + } + + mul_v3_fl(co, 1.0f / (float)j); + mid_v3_v3v3(co, co, v->co); + + if (clipx && fabsf(v->co[0]) <= clipdist) + co[0] = 0.0f; + if (clipy && fabsf(v->co[1]) <= clipdist) + co[1] = 0.0f; + if (clipz && fabsf(v->co[2]) <= clipdist) + co[2] = 0.0f; + + i++; + } + + i = 0; + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + copy_v3_v3(v->co, cos[i]); + i++; + } + + BLI_array_free(cos); +} + +/* + * compute the perimeter of an ngon + * + * NOTE: This should probably go to bmesh_polygon.c + */ +static float ngon_perimeter(BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + int num_verts = 0; + float v[3], sv[3]; + float perimeter = 0.0f; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (num_verts == 0) { + copy_v3_v3(v, l->v->co); + copy_v3_v3(sv, l->v->co); + } + else { + perimeter += len_v3v3(v, l->v->co); + copy_v3_v3(v, l->v->co); + } + num_verts++; + } + + perimeter += len_v3v3(v, sv); + + return perimeter; +} + +/* + * compute the fake surface of an ngon + * This is done by decomposing the ngon into triangles who share the centroid of the ngon + * while this method is far from being exact, it should garantee an invariance. + * + * NOTE: This should probably go to bmesh_polygon.c + */ +static float ngon_fake_area(BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + int num_verts = 0; + float v[3], sv[3], c[3]; + float area = 0.0f; + + BM_face_center_mean_calc(bm, f, c); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (num_verts == 0) { + copy_v3_v3(v, l->v->co); + copy_v3_v3(sv, l->v->co); + num_verts++; + } + else { + area += area_tri_v3(v, c, l->v->co); + copy_v3_v3(v, l->v->co); + num_verts++; + } + } + + area += area_tri_v3(v, c, sv); + + return area; +} + +/* + * extra face data (computed data) + */ +typedef struct tmp_face_ext { + BMFace *f; /* the face */ + float c[3]; /* center */ + union { + float area; /* area */ + float perim; /* perimeter */ + float d; /* 4th component of plane (the first three being the normal) */ + struct Image *t; /* image pointer */ + }; +} tmp_face_ext; + +/* + * Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h + * We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces + */ +void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op) +{ + BMIter fm_iter; + BMFace *fs, *fm; + BMOIter fs_iter; + int num_sels = 0, num_total = 0, i = 0, idx = 0; + float angle = 0.0f; + tmp_face_ext *f_ext = NULL; + int *indices = NULL; + float t_no[3]; /* temporary normal */ + int type = BMO_slot_int_get(op, "type"); + float thresh = BMO_slot_float_get(op, "thresh"); + + num_total = BM_mesh_elem_count(bm, BM_FACE); + + /* + ** The first thing to do is to iterate through all the the selected items and mark them since + ** they will be in the selection anyway. + ** This will increase performance, (especially when the number of originaly selected faces is high) + ** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces, + ** and n is the total number of faces + */ + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (!BMO_elem_flag_test(bm, fs, FACE_MARK)) { /* is this really needed ? */ + BMO_elem_flag_enable(bm, fs, FACE_MARK); + num_sels++; + } + } + + /* allocate memory for the selected faces indices and for all temporary faces */ + indices = (int *)MEM_callocN(sizeof(int) * num_sels, "face indices util.c"); + f_ext = (tmp_face_ext *)MEM_callocN(sizeof(tmp_face_ext) * num_total, "f_ext util.c"); + + /* loop through all the faces and fill the faces/indices structure */ + BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) { + f_ext[i].f = fm; + if (BMO_elem_flag_test(bm, fm, FACE_MARK)) { + indices[idx] = i; + idx++; + } + i++; + } + + /* + ** Save us some computation burden: In case of perimeter/area/coplanar selection we compute + ** only once. + */ + if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) { + for (i = 0; i < num_total; i++) { + switch (type) { + case SIMFACE_PERIMETER: + /* set the perimeter */ + f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f); + break; + + case SIMFACE_COPLANAR: + /* compute the center of the polygon */ + BM_face_center_mean_calc(bm, f_ext[i].f, f_ext[i].c); + + /* normalize the polygon normal */ + copy_v3_v3(t_no, f_ext[i].f->no); + normalize_v3(t_no); + + /* compute the plane distance */ + f_ext[i].d = dot_v3v3(t_no, f_ext[i].c); + break; + + case SIMFACE_AREA: + f_ext[i].area = ngon_fake_area(bm, f_ext[i].f); + break; + + case SIMFACE_IMAGE: + f_ext[i].t = NULL; + if (CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY)) { + MTexPoly *mtpoly = CustomData_bmesh_get(&bm->pdata, f_ext[i].f->head.data, CD_MTEXPOLY); + f_ext[i].t = mtpoly->tpage; + } + break; + } + } + } + + /* now select the rest (if any) */ + for (i = 0; i < num_total; i++) { + fm = f_ext[i].f; + if (!BMO_elem_flag_test(bm, fm, FACE_MARK) && !BM_elem_flag_test(fm, BM_ELEM_HIDDEN)) { + int cont = 1; + for (idx = 0; idx < num_sels && cont == 1; idx++) { + fs = f_ext[indices[idx]].f; + switch (type) { + case SIMFACE_MATERIAL: + if (fm->mat_nr == fs->mat_nr) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_IMAGE: + if (f_ext[i].t == f_ext[indices[idx]].t) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_NORMAL: + angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* if the angle between the normals -> 0 */ + if (angle / 180.0f <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_COPLANAR: + angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* angle -> 0 */ + if (angle / 180.0f <= thresh) { /* and dot product difference -> 0 */ + if (fabsf(f_ext[i].d - f_ext[indices[idx]].d) <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + } + break; + + case SIMFACE_AREA: + if (fabsf(f_ext[i].area - f_ext[indices[idx]].area) <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_PERIMETER: + if (fabsf(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + } + } + } + } + + MEM_freeN(f_ext); + MEM_freeN(indices); + + /* transfer all marked faces to the output slot */ + BMO_slot_from_flag(bm, op, "faceout", FACE_MARK, BM_FACE); +} + +/****************************************************************************** +** Similar Edges +**************************************************************************** */ +#define EDGE_MARK 1 + +/* + * compute the angle of an edge (i.e. the angle between two faces) + */ +static float edge_angle(BMesh *bm, BMEdge *e) +{ + BMIter fiter; + BMFace *f, *f_prev = NULL; + + /* first edge faces, dont account for 3+ */ + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + if (f_prev == NULL) { + f_prev = f; + } + else { + return angle_v3v3(f_prev->no, f->no); + } + } + + return 0.0f; +} +/* + * extra edge information + */ +typedef struct tmp_edge_ext { + BMEdge *e; + union { + float dir[3]; + float angle; /* angle between the face */ + }; + + union { + float length; /* edge length */ + int faces; /* faces count */ + }; +} tmp_edge_ext; + +/* + * select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h + * choices are length, direction, face, ... + */ +void bmesh_similaredges_exec(BMesh *bm, BMOperator *op) +{ + BMOIter es_iter; /* selected edges iterator */ + BMIter e_iter; /* mesh edges iterator */ + BMEdge *es; /* selected edge */ + BMEdge *e; /* mesh edge */ + int idx = 0, i = 0 /* , f = 0 */; + int *indices = NULL; + tmp_edge_ext *e_ext = NULL; + // float *angles = NULL; + float angle; + + int num_sels = 0, num_total = 0; + int type = BMO_slot_int_get(op, "type"); + float thresh = BMO_slot_float_get(op, "thresh"); + + num_total = BM_mesh_elem_count(bm, BM_EDGE); + + /* iterate through all selected edges and mark them */ + BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) { + BMO_elem_flag_enable(bm, es, EDGE_MARK); + num_sels++; + } + + /* allocate memory for the selected edges indices and for all temporary edges */ + indices = (int *)MEM_callocN(sizeof(int) * num_sels, "indices util.c"); + e_ext = (tmp_edge_ext *)MEM_callocN(sizeof(tmp_edge_ext) * num_total, "e_ext util.c"); + + /* loop through all the edges and fill the edges/indices structure */ + BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) { + e_ext[i].e = e; + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + indices[idx] = i; + idx++; + } + i++; + } + + /* save us some computation time by doing heavy computation once */ + if (type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || type == SIMEDGE_FACE_ANGLE) { + for (i = 0; i < num_total; i++) { + switch (type) { + case SIMEDGE_LENGTH: /* compute the length of the edge */ + e_ext[i].length = len_v3v3(e_ext[i].e->v1->co, e_ext[i].e->v2->co); + break; + + case SIMEDGE_DIR: /* compute the direction */ + sub_v3_v3v3(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co); + break; + + case SIMEDGE_FACE: /* count the faces around the edge */ + e_ext[i].faces = BM_edge_face_count(e_ext[i].e); + break; + + case SIMEDGE_FACE_ANGLE: + e_ext[i].faces = BM_edge_face_count(e_ext[i].e); + if (e_ext[i].faces == 2) + e_ext[i].angle = edge_angle(bm, e_ext[i].e); + break; + } + } + } + + /* select the edges if any */ + for (i = 0; i < num_total; i++) { + e = e_ext[i].e; + if (!BMO_elem_flag_test(bm, e, EDGE_MARK) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + int cont = 1; + for (idx = 0; idx < num_sels && cont == 1; idx++) { + es = e_ext[indices[idx]].e; + switch (type) { + case SIMEDGE_LENGTH: + if (fabsf(e_ext[i].length - e_ext[indices[idx]].length) <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_DIR: + /* compute the angle between the two edges */ + angle = RAD2DEGF(angle_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir)); + + if (angle > 90.0f) /* use the smallest angle between the edges */ + angle = fabsf(angle - 180.0f); + + if (angle / 90.0f <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_FACE: + if (e_ext[i].faces == e_ext[indices[idx]].faces) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_FACE_ANGLE: + if (e_ext[i].faces == 2) { + if (e_ext[indices[idx]].faces == 2) { + if (fabsf(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + } + } + else { + cont = 0; + } + break; + + case SIMEDGE_CREASE: + if (CustomData_has_layer(&bm->edata, CD_CREASE)) { + float *c1, *c2; + + c1 = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE); + c2 = CustomData_bmesh_get(&bm->edata, es->head.data, CD_CREASE); + + if (c1 && c2 && fabsf(*c1 - *c2) <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + } + break; + + case SIMEDGE_SEAM: + if (BM_elem_flag_test(e, BM_ELEM_SEAM) == BM_elem_flag_test(es, BM_ELEM_SEAM)) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_SHARP: + if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == BM_elem_flag_test(es, BM_ELEM_SMOOTH)) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + } + } + } + } + + MEM_freeN(e_ext); + MEM_freeN(indices); + + /* transfer all marked edges to the output slot */ + BMO_slot_from_flag(bm, op, "edgeout", EDGE_MARK, BM_EDGE); +} + +/****************************************************************************** +** Similar Vertices +**************************************************************************** */ +#define VERT_MARK 1 + +typedef struct tmp_vert_ext { + BMVert *v; + union { + int num_faces; /* adjacent faces */ + MDeformVert *dvert; /* deform vertex */ + }; +} tmp_vert_ext; + +/* + * select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h + * choices are normal, face, vertex group... + */ +void bmesh_similarverts_exec(BMesh *bm, BMOperator *op) +{ + BMOIter vs_iter; /* selected verts iterator */ + BMIter v_iter; /* mesh verts iterator */ + BMVert *vs; /* selected vertex */ + BMVert *v; /* mesh vertex */ + tmp_vert_ext *v_ext = NULL; + int *indices = NULL; + int num_total = 0, num_sels = 0, i = 0, idx = 0; + int type = BMO_slot_int_get(op, "type"); + float thresh = BMO_slot_float_get(op, "thresh"); + + num_total = BM_mesh_elem_count(bm, BM_VERT); + + /* iterate through all selected edges and mark them */ + BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) { + BMO_elem_flag_enable(bm, vs, VERT_MARK); + num_sels++; + } + + /* allocate memory for the selected vertices indices and for all temporary vertices */ + indices = (int *)MEM_mallocN(sizeof(int) * num_sels, "vertex indices"); + v_ext = (tmp_vert_ext *)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra"); + + /* loop through all the vertices and fill the vertices/indices structure */ + BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) { + v_ext[i].v = v; + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + indices[idx] = i; + idx++; + } + + switch (type) { + case SIMVERT_FACE: + /* calling BM_vert_face_count every time is time consumming, so call it only once per vertex */ + v_ext[i].num_faces = BM_vert_face_count(v); + break; + + case SIMVERT_VGROUP: + if (CustomData_has_layer(&(bm->vdata), CD_MDEFORMVERT)) { + v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT); + } + else { + v_ext[i].dvert = NULL; + } + break; + } + + i++; + } + + /* select the vertices if any */ + for (i = 0; i < num_total; i++) { + v = v_ext[i].v; + if (!BMO_elem_flag_test(bm, v, VERT_MARK) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + int cont = 1; + for (idx = 0; idx < num_sels && cont == 1; idx++) { + vs = v_ext[indices[idx]].v; + switch (type) { + case SIMVERT_NORMAL: + /* compare the angle between the normals */ + if (RAD2DEGF(angle_v3v3(v->no, vs->no)) / 180.0f <= thresh) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + cont = 0; + } + break; + case SIMVERT_FACE: + /* number of adjacent faces */ + if (v_ext[i].num_faces == v_ext[indices[idx]].num_faces) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + cont = 0; + } + break; + + case SIMVERT_VGROUP: + if (v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL) { + int v1, v2; + for (v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++) { + for (v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++) { + if (v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + cont = 0; + break; + } + } + } + } + break; + } + } + } + } + + MEM_freeN(indices); + MEM_freeN(v_ext); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +/****************************************************************************** +** Cycle UVs for a face +**************************************************************************** */ + +void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + // int n; + + int dir = BMO_slot_int_get(op, "dir"); + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) { + if (dir == DIRECTION_CW) { /* same loops direction */ + BMLoop *lf; /* current face loops */ + MLoopUV *f_luv; /* first face loop uv */ + float p_uv[2]; /* previous uvs */ + float t_uv[2]; /* tmp uvs */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop uv is the previous loop uv */ + MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + if (n == 0) { + f_luv = luv; + copy_v2_v2(p_uv, luv->uv); + } + else { + copy_v2_v2(t_uv, luv->uv); + copy_v2_v2(luv->uv, p_uv); + copy_v2_v2(p_uv, t_uv); + } + n++; + } + + copy_v2_v2(f_luv->uv, p_uv); + } + else if (dir == DIRECTION_CCW) { /* counter loop direction */ + BMLoop *lf; /* current face loops */ + MLoopUV *p_luv; /* previous loop uv */ + MLoopUV *luv; + float t_uv[2]; /* current uvs */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* previous loop uv is the current loop uv */ + luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + if (n == 0) { + p_luv = luv; + copy_v2_v2(t_uv, luv->uv); + } + else { + copy_v2_v2(p_luv->uv, luv->uv); + p_luv = luv; + } + n++; + } + + copy_v2_v2(luv->uv, t_uv); + } + } + } + +} + +/****************************************************************************** +** Reverse UVs for a face +**************************************************************************** */ + +void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + BLI_array_declare(uvs); + float (*uvs)[2] = NULL; + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) { + BMLoop *lf; /* current face loops */ + int i = 0; + + BLI_array_empty(uvs); + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + + /* current loop uv is the previous loop uv */ + BLI_array_growone(uvs); + uvs[i][0] = luv->uv[0]; + uvs[i][1] = luv->uv[1]; + i++; + } + + /* now that we have the uvs in the array, reverse! */ + i = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop uv is the previous loop uv */ + MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + luv->uv[0] = uvs[(fs->len - i - 1)][0]; + luv->uv[1] = uvs[(fs->len - i - 1)][1]; + i++; + } + } + } + + BLI_array_free(uvs); +} + +/****************************************************************************** +** Cycle colors for a face +**************************************************************************** */ + +void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + // int n; + + int dir = BMO_slot_int_get(op, "dir"); + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) { + if (dir == DIRECTION_CW) { /* same loops direction */ + BMLoop *lf; /* current face loops */ + MLoopCol *f_lcol; /* first face loop color */ + MLoopCol p_col; /* previous color */ + MLoopCol t_col; /* tmp color */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop color is the previous loop color */ + MLoopCol *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + if (n == 0) { + f_lcol = luv; + p_col = *luv; + } + else { + t_col = *luv; + *luv = p_col; + p_col = t_col; + } + n++; + } + + *f_lcol = p_col; + } + else if (dir == DIRECTION_CCW) { /* counter loop direction */ + BMLoop *lf; /* current face loops */ + MLoopCol *p_lcol; /* previous loop color */ + MLoopCol *lcol; + MLoopCol t_col; /* current color */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* previous loop color is the current loop color */ + lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + if (n == 0) { + p_lcol = lcol; + t_col = *lcol; + } + else { + *p_lcol = *lcol; + p_lcol = lcol; + } + n++; + } + + *lcol = t_col; + } + } + } +} + +/****************************************************************************** +** Reverse colors for a face +**************************************************************************** */ + +void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + BLI_array_declare(cols); + MLoopCol *cols = NULL; + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) { + BMLoop *lf; /* current face loops */ + int i = 0; + + BLI_array_empty(cols); + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + + /* current loop uv is the previous loop color */ + BLI_array_growone(cols); + cols[i] = *lcol; + i++; + } + + /* now that we have the uvs in the array, reverse! */ + i = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop uv is the previous loop color */ + MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + *lcol = cols[(fs->len - i - 1)]; + i++; + } + } + } + + BLI_array_free(cols); +} + + +/****************************************************************************** +** shortest vertex path select +**************************************************************************** */ + +typedef struct element_node { + BMVert *v; /* vertex */ + BMVert *parent; /* node parent id */ + float weight; /* node weight */ + HeapNode *hn; /* heap node */ +} element_node; + +void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op) +{ + BMOIter vs_iter /* , vs2_iter */; /* selected verts iterator */ + BMIter v_iter; /* mesh verts iterator */ + BMVert *vs, *sv, *ev; /* starting vertex, ending vertex */ + BMVert *v; /* mesh vertex */ + Heap *h = NULL; + + element_node *vert_list = NULL; + + int num_total = 0 /*, num_sels = 0 */, i = 0; + int type = BMO_slot_int_get(op, "type"); + + BMO_ITER(vs, &vs_iter, bm, op, "startv", BM_VERT) { + sv = vs; + } + BMO_ITER(vs, &vs_iter, bm, op, "endv", BM_VERT) { + ev = vs; + } + + num_total = BM_mesh_elem_count(bm, BM_VERT); + + /* allocate memory for the nodes */ + vert_list = (element_node *)MEM_mallocN(sizeof(element_node) * num_total, "vertex nodes"); + + /* iterate through all the mesh vertices */ + /* loop through all the vertices and fill the vertices/indices structure */ + i = 0; + BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) { + vert_list[i].v = v; + vert_list[i].parent = NULL; + vert_list[i].weight = FLT_MAX; + BM_elem_index_set(v, i); /* set_inline */ + i++; + } + bm->elem_index_dirty &= ~BM_VERT; + + /* + ** we now have everything we need, start Dijkstra path finding algorithm + */ + + /* set the distance/weight of the start vertex to 0 */ + vert_list[BM_elem_index_get(sv)].weight = 0.0f; + + h = BLI_heap_new(); + + for (i = 0; i < num_total; i++) { + vert_list[i].hn = BLI_heap_insert(h, vert_list[i].weight, vert_list[i].v); + } + + while (!BLI_heap_empty(h)) { + BMEdge *e; + BMIter e_i; + float v_weight; + + /* take the vertex with the lowest weight out of the heap */ + BMVert *v = (BMVert *)BLI_heap_popmin(h); + + if (vert_list[BM_elem_index_get(v)].weight == FLT_MAX) /* this means that there is no path */ + break; + + v_weight = vert_list[BM_elem_index_get(v)].weight; + + BM_ITER(e, &e_i, bm, BM_EDGES_OF_VERT, v) { + BMVert *u; + float e_weight = v_weight; + + if (type == VPATH_SELECT_EDGE_LENGTH) + e_weight += len_v3v3(e->v1->co, e->v2->co); + else e_weight += 1.0f; + + u = (e->v1 == v) ? e->v2 : e->v1; + + if (e_weight < vert_list[BM_elem_index_get(u)].weight) { /* is this path shorter ? */ + /* add it if so */ + vert_list[BM_elem_index_get(u)].parent = v; + vert_list[BM_elem_index_get(u)].weight = e_weight; + + /* we should do a heap update node function!!! :-/ */ + BLI_heap_remove(h, vert_list[BM_elem_index_get(u)].hn); + BLI_heap_insert(h, e_weight, u); + } + } + } + + /* now we trace the path (if it exists) */ + v = ev; + + while (vert_list[BM_elem_index_get(v)].parent != NULL) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + v = vert_list[BM_elem_index_get(v)].parent; + } + + BLI_heap_free(h, NULL); + MEM_freeN(vert_list); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} 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 |