BMesh: flatten faces operator
authorCampbell Barton <ideasman42@gmail.com>
Thu, 11 Jun 2015 11:46:51 +0000 (21:46 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 11 Jun 2015 11:54:06 +0000 (21:54 +1000)
release/scripts/startup/bl_ui/space_view3d.py
source/blender/bmesh/CMakeLists.txt
source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/intern/bmesh_operators_private.h
source/blender/bmesh/operators/bmo_planar_faces.c [new file with mode: 0644]
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/mesh/mesh_intern.h
source/blender/editors/mesh/mesh_ops.c

index 4bbd3cc8f5fe58b78b615a5638d057e546bfe1c1..eb681b7cc6a42c51c5158f410701830f35c5ebc6 100644 (file)
@@ -2439,6 +2439,7 @@ class VIEW3D_MT_edit_mesh_clean(Menu):
 
         layout.operator("mesh.dissolve_degenerate")
         layout.operator("mesh.dissolve_limited")
+        layout.operator("mesh.face_make_planar")
         layout.operator("mesh.vert_connect_nonplanar")
         layout.operator("mesh.vert_connect_concave")
         layout.operator("mesh.fill_holes")
index 80adb595ac9d08e5037e16a2123b30fd2003e4ad..2946e1b52ce319fb8a4bcfc905a694797a6e0146 100644 (file)
@@ -61,6 +61,7 @@ set(SRC
        operators/bmo_mesh_conv.c
        operators/bmo_mirror.c
        operators/bmo_normals.c
+       operators/bmo_planar_faces.c
        operators/bmo_poke.c
        operators/bmo_primitive.c
        operators/bmo_removedoubles.c
index ce988c6c6ddf077f83a36088d27715ce19bd8671..096c307d9184f40be9428cd4622b5030db659a03 100644 (file)
@@ -159,6 +159,28 @@ static BMOpDefine bmo_recalc_face_normals_def = {
         BMO_OPTYPE_FLAG_NORMALS_CALC),
 };
 
+/*
+ * Planar Faces.
+ *
+ * Iteratively flatten faces.
+ */
+static BMOpDefine bmo_planar_faces_def = {
+       "planar_faces",
+       /* slots_in */
+       {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}},    /* input geometry. */
+        {"iterations", BMO_OP_SLOT_INT},
+        {"factor", BMO_OP_SLOT_FLT},           /* planar factor */
+        {{'\0'}},
+       },
+       /* slots_out */
+       {{"geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, /* output slot, computed boundary geometry. */
+        {{'\0'}},
+       },
+       bmo_planar_faces_exec,
+       (BMO_OPTYPE_FLAG_SELECT_FLUSH |
+        BMO_OPTYPE_FLAG_SELECT_VALIDATE),
+};
+
 /*
  * Region Extend.
  *
@@ -2018,6 +2040,7 @@ const BMOpDefine *bmo_opdefines[] = {
        &bmo_pointmerge_facedata_def,
        &bmo_poke_def,
        &bmo_recalc_face_normals_def,
+       &bmo_planar_faces_def,
        &bmo_region_extend_def,
        &bmo_remove_doubles_def,
        &bmo_reverse_colors_def,
index 979f7d2640acb4fb944d5352d2f55e4ba270d2db..25340eee86838b7c0544d93a021dc3c907dc634e 100644 (file)
@@ -82,6 +82,7 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op);
 void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op);
 void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op);
 void bmo_poke_exec(BMesh *bm, BMOperator *op);
+void bmo_planar_faces_exec(BMesh *bm, BMOperator *op);
 void bmo_region_extend_exec(BMesh *bm, BMOperator *op);
 void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op);
 void bmo_reverse_colors_exec(BMesh *bm, BMOperator *op);
diff --git a/source/blender/bmesh/operators/bmo_planar_faces.c b/source/blender/bmesh/operators/bmo_planar_faces.c
new file mode 100644 (file)
index 0000000..4e3ac58
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * ***** 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 *****
+ */
+
+/** \file blender/bmesh/operators/bmo_planar_faces.c
+ *  \ingroup bmesh
+ *
+ * Iternatively flatten 4+ sided faces.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_ghash.h"
+
+#include "bmesh.h"
+
+#include "intern/bmesh_operators_private.h" /* own include */
+
+#define ELE_VERT_ADJUST (1 << 0)
+#define ELE_FACE_ADJUST (1 << 1)
+
+struct VertAccum {
+       float co[3];
+       int co_tot;
+};
+
+void bmo_planar_faces_exec(BMesh *bm, BMOperator *op)
+{
+       const float fac = BMO_slot_float_get(op->slots_in, "factor");
+       const int iterations = BMO_slot_int_get(op->slots_in, "iterations");
+       const int faces_num = BMO_slot_buffer_count(op->slots_in, "faces");
+
+       const float eps = 0.00001f;
+       const float eps_sq = SQUARE(eps);
+
+       BMOIter oiter;
+       BMFace *f;
+       BLI_mempool *vert_accum_pool;
+       GHash *vaccum_map;
+       float (*faces_center)[3];
+       int i, iter_step, shared_vert_num;
+
+       faces_center = MEM_mallocN(sizeof(*faces_center) * faces_num, __func__);
+
+       shared_vert_num = 0;
+       BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
+               BMLoop *l_iter, *l_first;
+
+               if (f->len == 3) {
+                       continue;
+               }
+
+               BM_face_calc_center_mean_weighted(f, faces_center[i]);
+
+               l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+               do {
+                       if (!BMO_elem_flag_test(bm, l_iter->v, ELE_VERT_ADJUST)) {
+                               BMO_elem_flag_enable(bm, l_iter->v, ELE_VERT_ADJUST);
+                               shared_vert_num += 1;
+                       }
+               } while ((l_iter = l_iter->next) != l_first);
+
+               BMO_elem_flag_enable(bm, f, ELE_FACE_ADJUST);
+       }
+
+       vert_accum_pool = BLI_mempool_create(sizeof(struct VertAccum), 0, 512, BLI_MEMPOOL_NOP);
+       vaccum_map = BLI_ghash_ptr_new_ex(__func__, shared_vert_num);
+
+       for (iter_step = 0; iter_step < iterations; iter_step++) {
+               GHashIterator gh_iter;
+               bool changed = false;
+
+               BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
+                       BMLoop *l_iter, *l_first;
+                       float plane[4];
+
+                       if (!BMO_elem_flag_test(bm, f, ELE_FACE_ADJUST)) {
+                               continue;
+                       }
+                       BMO_elem_flag_disable(bm, f, ELE_FACE_ADJUST);
+
+                       BLI_assert(f->len != 3);
+
+                       /* keep original face data (else we 'move' the face) */
+#if 0
+                       BM_face_normal_update(f);
+                       BM_face_calc_center_mean_weighted(f, f_center);
+#endif
+
+                       plane_from_point_normal_v3(plane, faces_center[i], f->no);
+
+                       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+                       do {
+                               struct VertAccum *va;
+                               void **va_p;
+                               float co[3];
+
+                               if (!BLI_ghash_ensure_p(vaccum_map, l_iter->v, &va_p)) {
+                                       *va_p = BLI_mempool_calloc(vert_accum_pool);
+                               }
+                               va = *va_p;
+
+                               closest_to_plane_v3(co, plane, l_iter->v->co);
+                               va->co_tot += 1;
+
+                               interp_v3_v3v3(va->co, va->co, co, 1.0f / (float)va->co_tot);
+                       } while ((l_iter = l_iter->next) != l_first);
+               }
+
+               GHASH_ITER (gh_iter, vaccum_map) {
+                       BMVert *v = BLI_ghashIterator_getKey(&gh_iter);
+                       struct VertAccum *va = BLI_ghashIterator_getValue(&gh_iter);
+                       BMIter iter;
+
+                       if (len_squared_v3v3(v->co, va->co) > eps_sq) {
+                               BMO_elem_flag_enable(bm, v, ELE_VERT_ADJUST);
+                               interp_v3_v3v3(v->co, v->co, va->co, fac);
+                               changed = true;
+                       }
+
+                       /* tag for re-calculation */
+                       BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+                               if (f->len != 3) {
+                                       BMO_elem_flag_enable(bm, f, ELE_FACE_ADJUST);
+                               }
+                       }
+               }
+
+               /* if nothing changed, break out early */
+               if (changed == false) {
+                       break;
+               }
+
+               BLI_ghash_clear(vaccum_map, NULL, NULL);
+               BLI_mempool_clear(vert_accum_pool);
+       }
+
+       MEM_freeN(faces_center);
+       BLI_ghash_free(vaccum_map, NULL, NULL);
+       BLI_mempool_destroy(vert_accum_pool);
+}
index 50419cb734792119c39ee3ee916a39d9c87fdf08..62aec7883060da13b8707e1d84dd76869c096d43 100644 (file)
@@ -1309,6 +1309,44 @@ void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot)
        RNA_def_property_float_default(prop, DEG2RADF(5.0f));
 }
 
+static int edbm_face_make_planar_exec(bContext *C, wmOperator *op)
+{
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = BKE_editmesh_from_object(obedit);
+       const int repeat = RNA_int_get(op->ptr, "repeat");
+       const float fac = RNA_float_get(op->ptr, "factor");
+
+       if (!EDBM_op_callf(
+               em, op, "planar_faces faces=%hf iterations=%i factor=%f",
+               BM_ELEM_SELECT, repeat, fac))
+       {
+               return OPERATOR_CANCELLED;
+       }
+
+       EDBM_update_generic(em, true, true);
+       return OPERATOR_FINISHED;
+}
+
+void MESH_OT_face_make_planar(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Make Planar Faces";
+       ot->idname = "MESH_OT_face_make_planar";
+       ot->description = "Flatten selected faces";
+
+       /* api callbacks */
+       ot->exec = edbm_face_make_planar_exec;
+       ot->poll = ED_operator_editmesh;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* props */
+       RNA_def_float(ot->srna, "factor", 0.5f, -10.0f, 10.0f, "Factor", "", 0.0f, 1.0f);
+       RNA_def_int(ot->srna, "repeat", 1, 1, 200,
+                   "Number of iterations to flatten faces", "", 1, 200);
+}
+
 
 static int edbm_edge_split_exec(bContext *C, wmOperator *op)
 {
index 9226146bc516a0515ca61b2be672fb5cb3d5e7d5..8c44d5a7b3f25dd472c126e1f8a47e5ec7244ed4 100644 (file)
@@ -173,6 +173,7 @@ void MESH_OT_vert_connect(struct wmOperatorType *ot);
 void MESH_OT_vert_connect_path(struct wmOperatorType *ot);
 void MESH_OT_vert_connect_concave(struct wmOperatorType *ot);
 void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot);
+void MESH_OT_face_make_planar(struct wmOperatorType *ot);
 void MESH_OT_edge_split(struct wmOperatorType *ot);
 void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot);
 void MESH_OT_wireframe(struct wmOperatorType *ot);
index 197df48d68812ecf606dda3991c374b418ce6559..20f222bf4254352cad8b85a3b2bdc9b07ae0451b 100644 (file)
@@ -167,6 +167,7 @@ void ED_operatortypes_mesh(void)
        WM_operatortype_append(MESH_OT_vert_connect_path);
        WM_operatortype_append(MESH_OT_vert_connect_concave);
        WM_operatortype_append(MESH_OT_vert_connect_nonplanar);
+       WM_operatortype_append(MESH_OT_face_make_planar);
        WM_operatortype_append(MESH_OT_knife_tool);
        WM_operatortype_append(MESH_OT_knife_project);