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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>2013-01-06 02:24:05 +0400
committerTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>2013-01-06 02:24:05 +0400
commitfeccbaabbd39c18b2f552964ebd8dfab4eeaed89 (patch)
tree0069f654883a7a02dd320649ce61bd7933748111 /source/blender/bmesh/intern
parenta22096e8019c461128a0907e4026859996ec1b5c (diff)
parent37ba969c74840142682cf22f34610f3b65b86cf4 (diff)
Merged changes in the trunk up to revision 53584.
Conflicts resolved: release/scripts/startup/bl_ui/properties_render.py source/blender/blenloader/intern/readfile.c source/blender/editors/interface/interface_templates.c source/blender/makesrna/RNA_enum_types.h Also made additional code updates for: r53355 UIList - Python-extendable list of UI items r53460 Alpha premul pipeline cleanup
Diffstat (limited to 'source/blender/bmesh/intern')
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c72
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_log.c962
-rw-r--r--source/blender/bmesh/intern/bmesh_log.h102
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c3
-rw-r--r--source/blender/bmesh/intern/bmesh_operator_api.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.c10
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.h1
8 files changed, 1116 insertions, 38 deletions
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index 2362ae7237a..5eeee45e7a9 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -173,15 +173,16 @@ void BM_face_copy_shared(BMesh *bm, BMFace *f)
*/
BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, const int create_flag)
{
- BMEdge **edges2 = BLI_array_alloca(edges2, len);
- BMVert **verts = BLI_array_alloca(verts, len + 1);
- int e2_index = 0;
- int v_index = 0;
+ BMEdge **edges_sort = BLI_array_alloca(edges_sort, len);
+ BMVert **verts_sort = BLI_array_alloca(verts_sort, len + 1);
+ int esort_index = 0;
+ int vsort_index = 0;
BMFace *f = NULL;
BMEdge *e;
BMVert *v, *ev1, *ev2;
- int i, /* j, */ v1found, reverse;
+ int i;
+ bool is_v1_found, is_reverse;
/* this code is hideous, yeek. I'll have to think about ways of
@@ -189,10 +190,7 @@ BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, i
* _and_ the old bmesh_mf functions, so its kindof smashed together
* - joeedh */
- if (!len || !v1 || !v2 || !edges || !bm) {
- BLI_assert(0);
- return NULL;
- }
+ BLI_assert(len && v1 && v2 && edges && bm);
/* put edges in correct order */
for (i = 0; i < len; i++) {
@@ -209,14 +207,19 @@ BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, i
SWAP(BMVert *, ev1, ev2);
}
- verts[v_index++] = ev1;
+ verts_sort[vsort_index++] = ev1;
v = ev2;
e = edges[0];
do {
BMEdge *e2 = e;
- verts[v_index++] = v;
- edges2[e2_index++] = e;
+ /* vertex array is (len + 1) */
+ if (UNLIKELY(vsort_index > len)) {
+ goto err; /* vertex in loop twice */
+ }
+
+ verts_sort[vsort_index++] = v;
+ edges_sort[esort_index++] = e;
/* we only flag the verts to check if they are in the face more then once */
BM_ELEM_API_FLAG_ENABLE(v, _FLAG_MV);
@@ -229,66 +232,67 @@ BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, i
}
} while (e2 != e);
- if (e2 == e)
+ if (UNLIKELY(e2 == e)) {
goto err; /* the edges do not form a closed loop */
+ }
e = e2;
} while (e != edges[0]);
- if (e2_index != len) {
+ if (UNLIKELY(esort_index != 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;
+ is_v1_found = is_reverse = false;
for (i = 0; i < len; i++) {
- if (BM_vert_in_edge(edges2[i], v1)) {
+ if (BM_vert_in_edge(edges_sort[i], v1)) {
/* see if v1 and v2 are in the same edge */
- if (BM_vert_in_edge(edges2[i], v2)) {
+ if (BM_vert_in_edge(edges_sort[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;
+ if (BM_vert_in_edge(edges_sort[(i + 1) % len], v1)) {
+ is_reverse = true;
break;
}
}
- v1found = TRUE;
+ is_v1_found = true;
}
- if ((v1found == FALSE) && BM_vert_in_edge(edges2[i], v2)) {
- reverse = TRUE;
+ if ((is_v1_found == false) && BM_vert_in_edge(edges_sort[i], v2)) {
+ is_reverse = true;
break;
}
}
- if (reverse) {
+ if (is_reverse) {
for (i = 0; i < len / 2; i++) {
- v = verts[i];
- verts[i] = verts[len - i - 1];
- verts[len - i - 1] = v;
+ v = verts_sort[i];
+ verts_sort[i] = verts_sort[len - i - 1];
+ verts_sort[len - i - 1] = v;
}
}
for (i = 0; i < len; i++) {
- edges2[i] = BM_edge_exists(verts[i], verts[(i + 1) % len]);
- if (!edges2[i]) {
+ edges_sort[i] = BM_edge_exists(verts_sort[i], verts_sort[(i + 1) % len]);
+ if (UNLIKELY(edges_sort[i] == NULL)) {
goto err;
}
/* check if vert is in face more then once. if the flag is disabled. we've already visited */
- if (!BM_ELEM_API_FLAG_TEST(verts[i], _FLAG_MV)) {
+ if (UNLIKELY(!BM_ELEM_API_FLAG_TEST(verts_sort[i], _FLAG_MV))) {
goto err;
}
- BM_ELEM_API_FLAG_DISABLE(verts[i], _FLAG_MV);
+ BM_ELEM_API_FLAG_DISABLE(verts_sort[i], _FLAG_MV);
}
- f = BM_face_create(bm, verts, edges2, len, create_flag);
+ f = BM_face_create(bm, verts_sort, edges_sort, len, create_flag);
/* clean up flags */
for (i = 0; i < len; i++) {
- BM_ELEM_API_FLAG_DISABLE(edges2[i], _FLAG_MF);
+ BM_ELEM_API_FLAG_DISABLE(edges_sort[i], _FLAG_MF);
}
return f;
@@ -297,8 +301,8 @@ err:
for (i = 0; i < len; i++) {
BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF);
}
- for (i = 0; i < v_index; i++) {
- BM_ELEM_API_FLAG_DISABLE(verts[i], _FLAG_MV);
+ for (i = 0; i < vsort_index; i++) {
+ BM_ELEM_API_FLAG_DISABLE(verts_sort[i], _FLAG_MV);
}
return NULL;
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index df58b90bc03..4ec91b8d8d7 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -603,7 +603,7 @@ void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
{
BMLoop *l_iter;
BMLoop *l_first;
- void **vblocks = BLI_array_alloca(vblocks, do_vertex ? source->len : 0);
+ void **vblocks = do_vertex ? BLI_array_alloca(vblocks, source->len) : NULL;
void **blocks = BLI_array_alloca(blocks, source->len);
float (*cos)[3] = BLI_array_alloca(cos, source->len);
float *w = BLI_array_alloca(w, source->len);
diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c
new file mode 100644
index 00000000000..b821c9875db
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_log.c
@@ -0,0 +1,962 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+#include "bmesh_log.h"
+#include "range_tree_c_api.h"
+
+struct BMLogEntry {
+ struct BMLogEntry *next, *prev;
+
+ /* The following GHashes map from an element ID to one of the log
+ * types above */
+
+ /* Elements that were in the previous entry, but have been
+ * deleted */
+ GHash *deleted_verts;
+ GHash *deleted_faces;
+ /* Elements that were not in the previous entry, but are in the
+ * result of this entry */
+ GHash *added_verts;
+ GHash *added_faces;
+
+ /* Vertices whose coordinates, mask value, or hflag have changed */
+ GHash *modified_verts;
+
+ BLI_mempool *pool_verts;
+ BLI_mempool *pool_faces;
+
+ /* This is only needed for dropping BMLogEntries while still in
+ * dynamic-topology mode, as that should release vert/face IDs
+ * back to the BMLog but no BMLog pointer is available at that
+ * time.
+ *
+ * This field is not guaranteed to be valid, any use of it should
+ * check for NULL. */
+ BMLog *log;
+};
+
+struct BMLog {
+ /* Tree of free IDs */
+ struct RangeTreeUInt *unused_ids;
+
+ /* Mapping from unique IDs to vertices and faces
+ *
+ * Each vertex and face in the log gets a unique unsigned integer
+ * assigned. That ID is taken from the set managed by the
+ * unused_ids range tree.
+ *
+ * The ID is needed because element pointers will change as they
+ * are created and deleted.
+ */
+ GHash *id_to_elem;
+ GHash *elem_to_id;
+
+ /* All BMLogEntrys, ordered from earliest to most recent */
+ ListBase entries;
+
+ /* The current log entry from entries list
+ *
+ * If null, then the original mesh from before any of the log
+ * entries is current (i.e. there is nothing left to undo.)
+ *
+ * If equal to the last entry in the entries list, then all log
+ * entries have been applied (i.e. there is nothing left to redo.)
+ */
+ BMLogEntry *current_entry;
+};
+
+typedef struct {
+ float co[3];
+ float mask;
+ char hflag;
+} BMLogVert;
+
+typedef struct {
+ unsigned int v_ids[3];
+} BMLogFace;
+
+/************************* Get/set element IDs ************************/
+
+/* Get the vertex's unique ID from the log */
+static unsigned int bm_log_vert_id_get(BMLog *log, BMVert *v)
+{
+ BLI_assert(BLI_ghash_haskey(log->elem_to_id, v));
+ return GET_INT_FROM_POINTER(BLI_ghash_lookup(log->elem_to_id, v));
+}
+
+/* Set the vertex's unique ID in the log */
+static void bm_log_vert_id_set(BMLog *log, BMVert *v, unsigned int id)
+{
+ void *vid = SET_INT_IN_POINTER(id);
+
+ BLI_ghash_remove(log->id_to_elem, vid, NULL, NULL);
+ BLI_ghash_insert(log->id_to_elem, vid, v);
+ BLI_ghash_remove(log->elem_to_id, v, NULL, NULL);
+ BLI_ghash_insert(log->elem_to_id, v, vid);
+}
+
+/* Get a vertex from its unique ID */
+static BMVert *bm_log_vert_from_id(BMLog *log, unsigned int id)
+{
+ void *key = SET_INT_IN_POINTER(id);
+ BLI_assert(BLI_ghash_haskey(log->id_to_elem, key));
+ return BLI_ghash_lookup(log->id_to_elem, key);
+}
+
+/* Get the face's unique ID from the log */
+static unsigned int bm_log_face_id_get(BMLog *log, BMFace *f)
+{
+ BLI_assert(BLI_ghash_haskey(log->elem_to_id, f));
+ return GET_INT_FROM_POINTER(BLI_ghash_lookup(log->elem_to_id, f));
+}
+
+/* Set the face's unique ID in the log */
+static void bm_log_face_id_set(BMLog *log, BMFace *f, unsigned int id)
+{
+ void *fid = SET_INT_IN_POINTER(id);
+
+ BLI_ghash_remove(log->id_to_elem, fid, NULL, NULL);
+ BLI_ghash_insert(log->id_to_elem, fid, f);
+ BLI_ghash_remove(log->elem_to_id, f, NULL, NULL);
+ BLI_ghash_insert(log->elem_to_id, f, fid);
+}
+
+/* Get a face from its unique ID */
+static BMFace *bm_log_face_from_id(BMLog *log, unsigned int id)
+{
+ void *key = SET_INT_IN_POINTER(id);
+ BLI_assert(BLI_ghash_haskey(log->id_to_elem, key));
+ return BLI_ghash_lookup(log->id_to_elem, key);
+}
+
+
+/************************ BMLogVert / BMLogFace ***********************/
+
+/* Get a vertex's paint-mask value
+ *
+ * Returns zero if no paint-mask layer is present */
+static float vert_mask_get(BMesh *bm, BMVert *v)
+{
+ CustomData *cd = &bm->vdata;
+ if (CustomData_has_layer(&bm->vdata, CD_PAINT_MASK)) {
+ float *mask = CustomData_bmesh_get(cd, v->head.data, CD_PAINT_MASK);
+ return *mask;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* Set a vertex's paint-mask value
+ *
+ * Has no effect is no paint-mask layer is present */
+static void vert_mask_set(BMesh *bm, BMVert *v, float new_mask)
+{
+ CustomData *cd = &bm->vdata;
+ if (CustomData_has_layer(cd, CD_PAINT_MASK)) {
+ float *mask = CustomData_bmesh_get(cd, v->head.data, CD_PAINT_MASK);
+ (*mask) = new_mask;
+ }
+}
+
+/* Update a BMLogVert with data from a BMVert */
+static void bm_log_vert_bmvert_copy(BMesh *bm, BMLogVert *lv, BMVert *v)
+{
+ copy_v3_v3(lv->co, v->co);
+ lv->mask = vert_mask_get(bm, v);
+ lv->hflag = v->head.hflag;
+}
+
+/* Allocate and initialize a BMLogVert */
+static BMLogVert *bm_log_vert_alloc(BMesh *bm, BMLog *log, BMVert *v)
+{
+ BMLogEntry *entry = log->current_entry;
+ BMLogVert *lv = BLI_mempool_alloc(entry->pool_verts);
+
+ bm_log_vert_bmvert_copy(bm, lv, v);
+
+ return lv;
+}
+
+/* Allocate and initialize a BMLogFace */
+static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f)
+{
+ BMLogEntry *entry = log->current_entry;
+ BMLogFace *lf = BLI_mempool_alloc(entry->pool_faces);
+ BMVert *v[3];
+
+ BLI_assert(f->len == 3);
+
+ BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v, 3);
+
+ lf->v_ids[0] = bm_log_vert_id_get(log, v[0]);
+ lf->v_ids[1] = bm_log_vert_id_get(log, v[1]);
+ lf->v_ids[2] = bm_log_vert_id_get(log, v[2]);
+
+ return lf;
+}
+
+
+/************************ Helpers for undo/redo ***********************/
+
+static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, verts) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter);
+ unsigned int id = GET_INT_FROM_POINTER(key);
+ BMVert *v = bm_log_vert_from_id(log, id);
+
+ /* Ensure the log has the final values of the vertex before
+ * deleting it */
+ bm_log_vert_bmvert_copy(bm, lv, v);
+
+ BM_vert_kill(bm, v);
+ }
+}
+
+static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, faces) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ unsigned int id = GET_INT_FROM_POINTER(key);
+ BMFace *f = bm_log_face_from_id(log, id);
+
+ BM_face_kill(bm, f);
+ }
+}
+
+static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, verts) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter);
+ BMVert *v = BM_vert_create(bm, lv->co, NULL, 0);
+ v->head.hflag = lv->hflag;
+ vert_mask_set(bm, v, lv->mask);
+ bm_log_vert_id_set(log, v, GET_INT_FROM_POINTER(key));
+ }
+}
+
+static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, faces) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter);
+ BMVert *v[3] = {bm_log_vert_from_id(log, lf->v_ids[0]),
+ bm_log_vert_from_id(log, lf->v_ids[1]),
+ bm_log_vert_from_id(log, lf->v_ids[2])};
+ BMFace *f;
+
+ f = BM_face_create_quad_tri_v(bm, v, 3, NULL, FALSE);
+ bm_log_face_id_set(log, f, GET_INT_FROM_POINTER(key));
+ }
+}
+
+static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, verts) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter);
+ unsigned int id = GET_INT_FROM_POINTER(key);
+ BMVert *v = bm_log_vert_from_id(log, id);
+ float mask;
+
+ swap_v3_v3(v->co, lv->co);
+ SWAP(char, v->head.hflag, lv->hflag);
+ mask = lv->mask;
+ lv->mask = vert_mask_get(bm, v);
+ vert_mask_set(bm, v, mask);
+ }
+}
+
+
+/**********************************************************************/
+
+/* Assign unique IDs to all vertices and faces already in the BMesh */
+static void bm_log_assign_ids(BMesh *bm, BMLog *log)
+{
+ BMIter iter;
+ BMVert *v;
+ BMFace *f;
+
+ /* Generate vertex IDs */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ unsigned int id = range_tree_uint_take_any(log->unused_ids);
+ bm_log_vert_id_set(log, v, id);
+ }
+
+ /* Generate face IDs */
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ unsigned int id = range_tree_uint_take_any(log->unused_ids);
+ bm_log_face_id_set(log, f, id);
+ }
+}
+
+/* Allocate an empty log entry */
+static BMLogEntry *bm_log_entry_create(void)
+{
+ BMLogEntry *entry = MEM_callocN(sizeof(BMLogEntry), AT);
+
+ entry->deleted_verts = BLI_ghash_ptr_new(AT);
+ entry->deleted_faces = BLI_ghash_ptr_new(AT);
+ entry->added_verts = BLI_ghash_ptr_new(AT);
+ entry->added_faces = BLI_ghash_ptr_new(AT);
+ entry->modified_verts = BLI_ghash_ptr_new(AT);
+
+ entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 1, 64, 0);
+ entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 1, 64, 0);
+
+ return entry;
+}
+
+/* Free the data in a log entry
+ *
+ * Note: does not free the log entry itself */
+static void bm_log_entry_free(BMLogEntry *entry)
+{
+ BLI_ghash_free(entry->deleted_verts, NULL, NULL);
+ BLI_ghash_free(entry->deleted_faces, NULL, NULL);
+ BLI_ghash_free(entry->added_verts, NULL, NULL);
+ BLI_ghash_free(entry->added_faces, NULL, NULL);
+ BLI_ghash_free(entry->modified_verts, NULL, NULL);
+
+ BLI_mempool_destroy(entry->pool_verts);
+ BLI_mempool_destroy(entry->pool_faces);
+}
+
+static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash)
+{
+ GHashIterator gh_iter;
+
+ GHASH_ITER (gh_iter, id_ghash) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ unsigned int id = GET_INT_FROM_POINTER(key);
+
+ if (range_tree_uint_has(unused_ids, id)) {
+ range_tree_uint_take(unused_ids, id);
+ }
+ }
+}
+
+static int uint_compare(const void *a_v, const void *b_v)
+{
+ const unsigned int *a = a_v;
+ const unsigned int *b = b_v;
+ return (*a) < (*b);
+}
+
+/* Remap IDs to contiguous indices
+ *
+ * E.g. if the vertex IDs are (4, 1, 10, 3), the mapping will be:
+ * 4 -> 2
+ * 1 -> 0
+ * 10 -> 3
+ * 3 -> 1
+ */
+static GHash *bm_log_compress_ids_to_indices(unsigned int *ids, int totid)
+{
+ GHash *map = BLI_ghash_int_new(AT);
+ int i;
+
+ qsort(ids, totid, sizeof(*ids), uint_compare);
+
+ for (i = 0; i < totid; i++) {
+ void *key = SET_INT_IN_POINTER(ids[i]);
+ void *val = SET_INT_IN_POINTER(i);
+ BLI_ghash_insert(map, key, val);
+ }
+
+ return map;
+}
+
+/* Release all ID keys in id_ghash */
+static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash)
+{
+ GHashIterator gh_iter;
+
+ GHASH_ITER (gh_iter, id_ghash) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ unsigned int id = GET_INT_FROM_POINTER(key);
+ range_tree_uint_release(log->unused_ids, id);
+ }
+}
+
+/***************************** Public API *****************************/
+
+/* Allocate, initialize, and assign a new BMLog */
+BMLog *BM_log_create(BMesh *bm)
+{
+ BMLog *log = MEM_callocN(sizeof(*log), AT);
+
+ log->unused_ids = range_tree_uint_alloc(0, (unsigned)-1);
+ log->id_to_elem = BLI_ghash_ptr_new(AT);
+ log->elem_to_id = BLI_ghash_ptr_new(AT);
+
+ /* Assign IDs to all existing vertices and faces */
+ bm_log_assign_ids(bm, log);
+
+ return log;
+}
+
+/* Allocate and initialize a new BMLog using existing BMLogEntries
+ *
+ * The 'entry' should be the last entry in the BMLog. Its prev pointer
+ * will be followed back to find the first entry.
+ *
+ * The unused IDs field of the log will be initialized by taking all
+ * keys from all GHashes in the log entry.
+ */
+BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
+{
+ BMLog *log = BM_log_create(bm);
+
+ if (entry->prev)
+ log->current_entry = entry;
+ else
+ log->current_entry = NULL;
+
+ /* Let BMLog manage the entry list again */
+ log->entries.first = log->entries.last = entry;
+ if (entry) {
+ while (entry->prev) {
+ entry = entry->prev;
+ log->entries.first = entry;
+ }
+ entry = log->entries.last;
+ while (entry->next) {
+ entry = entry->next;
+ log->entries.last = entry;
+ }
+ }
+
+ for (entry = log->entries.first; entry; entry = entry->next) {
+ entry->log = log;
+
+ /* Take all used IDs */
+ bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts);
+ bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces);
+ bm_log_id_ghash_retake(log->unused_ids, entry->added_verts);
+ bm_log_id_ghash_retake(log->unused_ids, entry->added_faces);
+ bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts);
+ }
+
+ return log;
+}
+
+/* Free all the data in a BMLog including the log itself */
+void BM_log_free(BMLog *log)
+{
+ BMLogEntry *entry;
+
+ if (log->unused_ids)
+ range_tree_uint_free(log->unused_ids);
+
+ if (log->id_to_elem)
+ BLI_ghash_free(log->id_to_elem, NULL, NULL);
+
+ if (log->elem_to_id)
+ BLI_ghash_free(log->elem_to_id, NULL, NULL);
+
+ /* Clear the BMLog references within each entry, but do not free
+ * the entries themselves */
+ for (entry = log->entries.first; entry; entry = entry->next)
+ entry->log = NULL;
+
+ MEM_freeN(log);
+}
+
+/* Get the number of log entries */
+int BM_log_length(const BMLog *log)
+{
+ return BLI_countlist(&log->entries);
+}
+
+/* Apply a consistent ordering to BMesh vertices */
+void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log)
+{
+ void *varr;
+ void *farr;
+
+ GHash *id_to_idx;
+
+ BMIter bm_iter;
+ BMVert *v;
+ BMFace *f;
+
+ int i;
+
+ /* Put all vertex IDs into an array */
+ i = 0;
+ varr = MEM_mallocN(sizeof(int) * bm->totvert, AT);
+ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) {
+ ((unsigned int*)varr)[i++] = bm_log_vert_id_get(log, v);
+ }
+
+ /* Put all face IDs into an array */
+ i = 0;
+ farr = MEM_mallocN(sizeof(int) * bm->totface, AT);
+ BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) {
+ ((unsigned int*)farr)[i++] = bm_log_face_id_get(log, f);
+ }
+
+ /* Create BMVert index remap array */
+ id_to_idx = bm_log_compress_ids_to_indices(varr, bm->totvert);
+ i = 0;
+ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) {
+ const unsigned id = bm_log_vert_id_get(log, v);
+ const void *key = SET_INT_IN_POINTER(id);
+ const void *val = BLI_ghash_lookup(id_to_idx, key);
+ ((int *)varr)[i++] = GET_INT_FROM_POINTER(val);
+ }
+ BLI_ghash_free(id_to_idx, NULL, NULL);
+
+ /* Create BMFace index remap array */
+ id_to_idx = bm_log_compress_ids_to_indices(farr, bm->totface);
+ i = 0;
+ BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) {
+ const unsigned id = bm_log_face_id_get(log, f);
+ const void *key = SET_INT_IN_POINTER(id);
+ const void *val = BLI_ghash_lookup(id_to_idx, key);
+ ((int *)farr)[i++] = GET_INT_FROM_POINTER(val);
+ }
+ BLI_ghash_free(id_to_idx, NULL, NULL);
+
+ BM_mesh_remap(bm, varr, NULL, farr);
+
+ MEM_freeN(varr);
+ MEM_freeN(farr);
+}
+
+/* Start a new log entry and update the log entry list
+ *
+ * If the log entry list is empty, or if the current log entry is the
+ * last entry, the new entry is simply appended to the end.
+ *
+ * Otherwise, the new entry is added after the current entry and all
+ * following entries are deleted.
+ *
+ * In either case, the new entry is set as the current log entry.
+ */
+BMLogEntry *BM_log_entry_add(BMLog *log)
+{
+ BMLogEntry *entry, *next;
+
+ /* Delete any entries after the current one */
+ entry = log->current_entry;
+ if (entry) {
+ for (entry = entry->next; entry; entry = next) {
+ next = entry->next;
+ bm_log_entry_free(entry);
+ BLI_freelinkN(&log->entries, entry);
+ }
+ }
+
+ /* Create and append the new entry */
+ entry = bm_log_entry_create();
+ BLI_addtail(&log->entries, entry);
+ entry->log = log;
+ log->current_entry = entry;
+
+ return entry;
+}
+
+/* Remove an entry from the log
+ *
+ * Uses entry->log as the log. If the log is NULL, the entry will be
+ * free'd but not removed from any list, nor shall its IDs be
+ * released.
+ *
+ * This operation is only valid on the first and last entries in the
+ * log. Deleting from the middle will assert.
+ */
+void BM_log_entry_drop(BMLogEntry *entry)
+{
+ BMLog *log = entry->log;
+
+ if (!log) {
+ /* Unlink */
+ BLI_assert(!(entry->prev && entry->next));
+ if (entry->prev)
+ entry->prev->next = NULL;
+ else if (entry->next)
+ entry->next->prev = NULL;
+
+ bm_log_entry_free(entry);
+ MEM_freeN(entry);
+ return;
+ }
+
+ if (!entry->prev) {
+ /* Release IDs of elements that are deleted by this
+ * entry. Since the entry is at the beginning of the undo
+ * stack, and it's being deleted, those elements can never be
+ * restored. Their IDs can go back into the pool. */
+ bm_log_id_ghash_release(log, entry->deleted_faces);
+ bm_log_id_ghash_release(log, entry->deleted_verts);
+ }
+ else if (!entry->next) {
+ /* Release IDs of elements that are added by this entry. Since
+ * the entry is at the end of the undo stack, and it's being
+ * deleted, those elements can never be restored. Their IDs
+ * can go back into the pool. */
+ bm_log_id_ghash_release(log, entry->added_faces);
+ bm_log_id_ghash_release(log, entry->added_verts);
+ }
+ else {
+ BLI_assert(!"Cannot drop BMLogEntry from middle");
+ }
+
+ if (log->current_entry == entry)
+ log->current_entry = entry->prev;
+
+ bm_log_entry_free(entry);
+ BLI_freelinkN(&log->entries, entry);
+}
+
+/* Undo one BMLogEntry
+ *
+ * Has no effect if there's nothing left to undo */
+void BM_log_undo(BMesh *bm, BMLog *log)
+{
+ BMLogEntry *entry = log->current_entry;
+
+ if (entry) {
+ log->current_entry = entry->prev;
+
+ /* Delete added faces and verts */
+ bm_log_faces_unmake(bm, log, entry->added_faces);
+ bm_log_verts_unmake(bm, log, entry->added_verts);
+
+ /* Restore deleted verts and faces */
+ bm_log_verts_restore(bm, log, entry->deleted_verts);
+ bm_log_faces_restore(bm, log, entry->deleted_faces);
+
+ /* Restore vertex coordinates, mask, and hflag */
+ bm_log_vert_values_swap(bm, log, entry->modified_verts);
+ }
+}
+
+/* Redo one BMLogEntry
+ *
+ * Has no effect if there's nothing left to redo */
+void BM_log_redo(BMesh *bm, BMLog *log)
+{
+ BMLogEntry *entry = log->current_entry;
+
+ if (!entry) {
+ /* Currently at the beginning of the undo stack, move to first entry */
+ entry = log->entries.first;
+ }
+ else if (entry && entry->next) {
+ /* Move to next undo entry */
+ entry = entry->next;
+ }
+ else {
+ /* Currently at the end of the undo stack, nothing left to redo */
+ return;
+ }
+
+ log->current_entry = entry;
+
+ if (entry) {
+ /* Re-delete previously deleted faces and verts */
+ bm_log_faces_unmake(bm, log, entry->deleted_faces);
+ bm_log_verts_unmake(bm, log, entry->deleted_verts);
+
+ /* Restore previously added verts and faces */
+ bm_log_verts_restore(bm, log, entry->added_verts);
+ bm_log_faces_restore(bm, log, entry->added_faces);
+
+ /* Restore vertex coordinates, mask, and hflag */
+ bm_log_vert_values_swap(bm, log, entry->modified_verts);
+ }
+}
+
+/* Log a vertex before it is modified
+ *
+ * Before modifying vertex coordinates, masks, or hflags, call this
+ * function to log it's current values. This is better than logging
+ * after the coordinates have been modified, because only those
+ * vertices that are modified need to have their original values
+ * stored.
+ *
+ * Handles two separate cases:
+ *
+ * If the vertex was added in the current log entry, update the
+ * vertex in the map of added vertices.
+ *
+ * If the vertex already existed prior to the current log entry, a
+ * seperate key/value map of modified vertices is used (using the
+ * vertex's ID as the key). The values stored in that case are
+ * the vertex's original state so that an undo can restore the
+ * previous state.
+ *
+ * On undo, the current vertex state will be swapped with the stored
+ * state so that a subsequent redo operation will restore the newer
+ * vertex state.
+ */
+void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v)
+{
+ BMLogEntry *entry = log->current_entry;
+ BMLogVert *lv;
+ unsigned int v_id = bm_log_vert_id_get(log, v);
+ void *key = SET_INT_IN_POINTER(v_id);
+
+ /* Find or create the BMLogVert entry */
+ if ((lv = BLI_ghash_lookup(entry->added_verts, key))) {
+ bm_log_vert_bmvert_copy(bm, lv, v);
+ }
+ else if (!BLI_ghash_haskey(entry->modified_verts, key)) {
+ lv = bm_log_vert_alloc(bm, log, v);
+ BLI_ghash_insert(entry->modified_verts, key, lv);
+ }
+}
+
+/* Log a new vertex as added to the BMesh
+ *
+ * The new vertex gets a unique ID assigned. It is then added to a map
+ * of added vertices, with the key being its ID and the value
+ * containing everything needed to reconstruct that vertex.
+ */
+void BM_log_vert_added(BMesh *bm, BMLog *log, BMVert *v)
+{
+ BMLogVert *lv;
+ unsigned int v_id = range_tree_uint_take_any(log->unused_ids);
+ void *key = SET_INT_IN_POINTER(v_id);
+
+ bm_log_vert_id_set(log, v, v_id);
+ lv = bm_log_vert_alloc(bm, log, v);
+ BLI_ghash_insert(log->current_entry->added_verts, key, lv);
+}
+
+/* Log a new face as added to the BMesh
+ *
+ * The new face gets a unique ID assigned. It is then added to a map
+ * of added faces, with the key being its ID and the value containing
+ * everything needed to reconstruct that face.
+ */
+void BM_log_face_added(BMLog *log, BMFace *f)
+{
+ BMLogFace *lf;
+ unsigned int f_id = range_tree_uint_take_any(log->unused_ids);
+ void *key = SET_INT_IN_POINTER(f_id);
+
+ /* Only triangles are supported for now */
+ BLI_assert(f->len == 3);
+
+ bm_log_face_id_set(log, f, f_id);
+ lf = bm_log_face_alloc(log, f);
+ BLI_ghash_insert(log->current_entry->added_faces, key, lf);
+}
+
+/* Log a vertex as removed from the BMesh
+ *
+ * A couple things can happen here:
+ *
+ * If the vertex was added as part of the current log entry, then it's
+ * deleted and forgotten about entirely. Its unique ID is returned to
+ * the unused pool.
+ *
+ * If the vertex was already part of the BMesh before the current log
+ * entry, it is added to a map of deleted vertices, with the key being
+ * its ID and the value containing everything needed to reconstruct
+ * that vertex.
+ *
+ * If there's a move record for the vertex, that's used as the
+ * vertices original location, then the move record is deleted.
+ */
+void BM_log_vert_removed(BMesh *bm, BMLog *log, BMVert *v)
+{
+ BMLogEntry *entry = log->current_entry;
+ unsigned int v_id = bm_log_vert_id_get(log, v);
+ void *key = SET_INT_IN_POINTER(v_id);
+
+ if (BLI_ghash_lookup(entry->added_verts, key)) {
+ BLI_ghash_remove(entry->added_verts, key, NULL, NULL);
+ range_tree_uint_release(log->unused_ids, v_id);
+ }
+ else {
+ BMLogVert *lv, *lv_mod;
+
+ lv = bm_log_vert_alloc(bm, log, v);
+ BLI_ghash_insert(entry->deleted_verts, key, lv);
+
+ /* If the vertex was modified before deletion, ensure that the
+ * original vertex values are stored */
+ if ((lv_mod = BLI_ghash_lookup(entry->modified_verts, key))) {
+ (*lv) = (*lv_mod);
+ BLI_ghash_remove(entry->modified_verts, key, NULL, NULL);
+ }
+ }
+}
+
+/* Log a face as removed from the BMesh
+ *
+ * A couple things can happen here:
+ *
+ * If the face was added as part of the current log entry, then it's
+ * deleted and forgotten about entirely. Its unique ID is returned to
+ * the unused pool.
+ *
+ * If the face was already part of the BMesh before the current log
+ * entry, it is added to a map of deleted faces, with the key being
+ * its ID and the value containing everything needed to reconstruct
+ * that face.
+ */
+void BM_log_face_removed(BMLog *log, BMFace *f)
+{
+ BMLogEntry *entry = log->current_entry;
+ unsigned int f_id = bm_log_face_id_get(log, f);
+ void *key = SET_INT_IN_POINTER(f_id);
+
+ if (BLI_ghash_lookup(entry->added_faces, key)) {
+ BLI_ghash_remove(entry->added_faces, key, NULL, NULL);
+ range_tree_uint_release(log->unused_ids, f_id);
+ }
+ else {
+ BMLogFace *lf;
+
+ lf = bm_log_face_alloc(log, f);
+ BLI_ghash_insert(entry->deleted_faces, key, lf);
+ }
+}
+
+/* Log all vertices/faces in the BMesh as added */
+void BM_log_all_added(BMesh *bm, BMLog *log)
+{
+ BMIter bm_iter;
+ BMVert *v;
+ BMFace *f;
+
+ /* Log all vertices as newly created */
+ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) {
+ BM_log_vert_added(bm, log, v);
+ }
+
+ /* Log all faces as newly created */
+ BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) {
+ BM_log_face_added(log, f);
+ }
+}
+
+/* Log all vertices/faces in the BMesh as removed */
+void BM_log_before_all_removed(BMesh *bm, BMLog *log)
+{
+ BMIter bm_iter;
+ BMVert *v;
+ BMFace *f;
+
+ /* Log deletion of all faces */
+ BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) {
+ BM_log_face_removed(log, f);
+ }
+
+ /* Log deletion of all vertices */
+ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) {
+ BM_log_vert_removed(bm, log, v);
+ }
+}
+
+/* Get the logged coordinates of a vertex
+ *
+ * Does not modify the log or the vertex */
+const float *BM_log_original_vert_co(BMLog *log, BMVert *v)
+{
+ BMLogEntry *entry = log->current_entry;
+ const BMLogVert *lv;
+ unsigned v_id = bm_log_vert_id_get(log, v);
+ void *key = SET_INT_IN_POINTER(v_id);
+
+ BLI_assert(entry);
+
+ BLI_assert(BLI_ghash_haskey(entry->modified_verts, key));
+
+ lv = BLI_ghash_lookup(entry->modified_verts, key);
+ return lv->co;
+}
+
+/* Get the logged mask of a vertex
+ *
+ * Does not modify the log or the vertex */
+float BM_log_original_mask(BMLog *log, BMVert *v)
+{
+ BMLogEntry *entry = log->current_entry;
+ const BMLogVert *lv;
+ unsigned v_id = bm_log_vert_id_get(log, v);
+ void *key = SET_INT_IN_POINTER(v_id);
+
+ BLI_assert(entry);
+
+ BLI_assert(BLI_ghash_haskey(entry->modified_verts, key));
+
+ lv = BLI_ghash_lookup(entry->modified_verts, key);
+ return lv->mask;
+}
+
+/************************ Debugging and Testing ***********************/
+
+/* For internal use only (unit testing) */
+BMLogEntry *BM_log_current_entry(BMLog *log)
+{
+ return log->current_entry;
+}
+
+/* For internal use only (unit testing) */
+RangeTreeUInt *BM_log_unused_ids(BMLog *log)
+{
+ return log->unused_ids;
+}
+
+#if 0
+/* Print the list of entries, marking the current one
+ *
+ * Keep around for debugging */
+void bm_log_print(const BMLog *log, const char *description)
+{
+ const BMLogEntry *entry;
+ const char *current = " <-- current";
+ int i;
+
+ printf("%s:\n", description);
+ printf(" % 2d: [ initial ]%s\n", 0,
+ (!log->current_entry) ? current : "");
+ for (entry = log->entries.first, i = 1; entry; entry = entry->next, i++) {
+ printf(" % 2d: [%p]%s\n", i, entry,
+ (entry == log->current_entry) ? current : "");
+ }
+}
+#endif
diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h
new file mode 100644
index 00000000000..958ff340b43
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_log.h
@@ -0,0 +1,102 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_LOG_H__
+#define __BMESH_LOG_H__
+
+/* The BMLog is an interface for storing undo/redo steps as a BMesh is
+ * modified. It only stores changes to the BMesh, not full copies.
+ *
+ * Currently it supports the following types of changes:
+ *
+ * - Adding and removing vertices
+ * - Adding and removing faces
+ * - Moving vertices
+ * - Setting vertex paint-mask values
+ * - Setting vertex hflags
+ */
+
+struct BMFace;
+struct BMVert;
+struct BMesh;
+struct RangeTreeUInt;
+
+typedef struct BMLog BMLog;
+typedef struct BMLogEntry BMLogEntry;
+
+/* Allocate and initialize a new BMLog */
+BMLog *BM_log_create(BMesh *bm);
+
+/* Allocate and initialize a new BMLog using existing BMLogEntries */
+BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry);
+
+/* Free all the data in a BMLog including the log itself */
+void BM_log_free(BMLog *log);
+
+/* Get the number of log entries */
+int BM_log_length(const BMLog *log);
+
+/* Apply a consistent ordering to BMesh vertices and faces */
+void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log);
+
+/* Start a new log entry and update the log entry list */
+BMLogEntry *BM_log_entry_add(BMLog *log);
+
+/* Remove an entry from the log */
+void BM_log_entry_drop(BMLogEntry *entry);
+
+/* Undo one BMLogEntry */
+void BM_log_undo(BMesh *bm, BMLog *log);
+
+/* Redo one BMLogEntry */
+void BM_log_redo(BMesh *bm, BMLog *log);
+
+/* Log a vertex before it is modified */
+void BM_log_vert_before_modified(BMesh *bm, BMLog *log, struct BMVert *v);
+
+/* Log a new vertex as added to the BMesh */
+void BM_log_vert_added(BMesh *bm, BMLog *log, struct BMVert *v);
+
+/* Log a new face as added to the BMesh */
+void BM_log_face_added(BMLog *log, struct BMFace *f);
+
+/* Log a vertex as removed from the BMesh */
+void BM_log_vert_removed(BMesh *bm, BMLog *log, struct BMVert *v);
+
+/* Log a face as removed from the BMesh */
+void BM_log_face_removed(BMLog *log, struct BMFace *f);
+
+/* Log all vertices/faces in the BMesh as added */
+void BM_log_all_added(BMesh *bm, BMLog *log);
+
+/* Log all vertices/faces in the BMesh as removed */
+void BM_log_before_all_removed(BMesh *bm, BMLog *log);
+
+/* Get the logged coordinates of a vertex */
+const float *BM_log_original_vert_co(BMLog *log, BMVert *v);
+
+/* Get the logged mask of a vertex */
+float BM_log_original_mask(BMLog *log, BMVert *v);
+
+/* For internal use only (unit testing) */
+BMLogEntry *BM_log_current_entry(BMLog *log);
+struct RangeTreeUInt *BM_log_unused_ids(BMLog *log);
+
+#endif
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 3a7a1c4eaaa..8461ec6fe08 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -128,7 +128,7 @@ static BMOpDefine bmo_smooth_laplacian_vert_def = {
"smooth_laplacian_vert",
/* slots_in */
{{"verts", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* input vertices */
- {"lambda", BMO_OP_SLOT_FLT}, /* lambda param */
+ {"lambda_factor", BMO_OP_SLOT_FLT}, /* lambda param */
{"lambda_border", BMO_OP_SLOT_FLT}, /* lambda param in border */
{"use_x", BMO_OP_SLOT_BOOL}, /* Smooth object along X axis */
{"use_y", BMO_OP_SLOT_BOOL}, /* Smooth object along Y axis */
@@ -1403,6 +1403,7 @@ static BMOpDefine bmo_bevel_def = {
{{"geom", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* input edges and vertices */
{"offset", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */
{"segments", BMO_OP_SLOT_INT}, /* number of segments in bevel */
+ {"vertex_only", BMO_OP_SLOT_BOOL}, /* only bevel vertices, not edges */
{{'\0'}},
},
/* slots_out */
diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h
index 7df9c94a2f1..b8fbfbee37f 100644
--- a/source/blender/bmesh/intern/bmesh_operator_api.h
+++ b/source/blender/bmesh/intern/bmesh_operator_api.h
@@ -435,7 +435,7 @@ void BMO_slot_buffer_from_all(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_
* //whether it's a float, pointer, whatever.
* //
* // so to get a pointer, for example, use:
- * // *((void**)BMO_iter_map_value(&oiter));
+ * // *((void **)BMO_iter_map_value(&oiter));
* //or something like that.
* }
* \endcode
diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c
index 5f5c60dde3f..b5b6c69bd7e 100644
--- a/source/blender/bmesh/intern/bmesh_queries.c
+++ b/source/blender/bmesh/intern/bmesh_queries.c
@@ -468,7 +468,7 @@ BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e_first)
}
/**
- * Returms edge length
+ * Returns edge length
*/
float BM_edge_calc_length(BMEdge *e)
{
@@ -476,6 +476,14 @@ float BM_edge_calc_length(BMEdge *e)
}
/**
+ * Returns edge length squared (for comparisons)
+ */
+float BM_edge_calc_length_squared(BMEdge *e)
+{
+ return len_squared_v3v3(e->v1->co, e->v2->co);
+}
+
+/**
* Utility function, since enough times we have an edge
* and want to access 2 connected faces.
*
diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h
index 9af792417bf..a9d6719c491 100644
--- a/source/blender/bmesh/intern/bmesh_queries.h
+++ b/source/blender/bmesh/intern/bmesh_queries.h
@@ -37,6 +37,7 @@ int BM_vert_in_edge(BMEdge *e, BMVert *v);
int BM_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e);
float BM_edge_calc_length(BMEdge *e);
+float BM_edge_calc_length_squared(BMEdge *e);
int BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb);
int BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb);
BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v);