While \file doesn't need an argument, it can't have another doxy command after it.
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
 | 
						|
/** \file
 | 
						|
 * \ingroup bmesh
 | 
						|
 *
 | 
						|
 * Rotate edges topology that share two faces.
 | 
						|
 */
 | 
						|
 | 
						|
#include "MEM_guardedalloc.h"
 | 
						|
 | 
						|
#include "BLI_math.h"
 | 
						|
#include "BLI_heap.h"
 | 
						|
 | 
						|
#include "bmesh.h"
 | 
						|
 | 
						|
#include "intern/bmesh_operators_private.h" /* own include */
 | 
						|
 | 
						|
#define EDGE_OUT   1
 | 
						|
#define FACE_MARK 1
 | 
						|
 | 
						|
/**
 | 
						|
 * Rotate edges where every edge has it's own faces (we can rotate in any order).
 | 
						|
 */
 | 
						|
static void bm_rotate_edges_simple(
 | 
						|
        BMesh *bm, BMOperator *op,
 | 
						|
        const short check_flag, const bool use_ccw)
 | 
						|
{
 | 
						|
	BMOIter siter;
 | 
						|
	BMEdge *e;
 | 
						|
 | 
						|
	BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
 | 
						|
		/**
 | 
						|
		 * this ends up being called twice, could add option to not to call check in
 | 
						|
		 * #BM_edge_rotate to get some extra speed */
 | 
						|
		if (BM_edge_rotate_check(e)) {
 | 
						|
			BMEdge *e_rotate = BM_edge_rotate(bm, e, use_ccw, check_flag);
 | 
						|
			if (e_rotate != NULL) {
 | 
						|
				BMO_edge_flag_enable(bm, e_rotate, EDGE_OUT);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Edge length is just a way of ordering that's independent of order in the edges argument,
 | 
						|
 * we could use some other method since ideally all edges will be rotated,
 | 
						|
 * this just happens to be simple to calculate.
 | 
						|
 */
 | 
						|
static float bm_edge_calc_rotate_cost(const BMEdge *e)
 | 
						|
{
 | 
						|
	return -BM_edge_calc_length_squared(e);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check if this edge is a boundary: Are more than one of the connected faces edges rotating too?
 | 
						|
 */
 | 
						|
static float bm_edge_rotate_is_boundary(const BMEdge *e)
 | 
						|
{
 | 
						|
	/* Number of adjacent shared faces. */
 | 
						|
	int count = 0;
 | 
						|
	BMLoop *l_radial_iter = e->l;
 | 
						|
	do {
 | 
						|
		/* Skip this edge. */
 | 
						|
		BMLoop *l_iter = l_radial_iter->next;
 | 
						|
		do {
 | 
						|
			BMEdge *e_iter = l_iter->e;
 | 
						|
			const int e_iter_index = BM_elem_index_get(e_iter);
 | 
						|
			if ((e_iter_index != -1)) {
 | 
						|
				if (count == 1) {
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
				count += 1;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		} while ((l_iter = l_iter->next) != l_radial_iter);
 | 
						|
	} while ((l_radial_iter = l_radial_iter->radial_next) != e->l);
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Rotate edges where edges share faces,
 | 
						|
 * edges which could not rotate need to be re-considered after neighbors are rotated.
 | 
						|
 */
 | 
						|
static void bm_rotate_edges_shared(
 | 
						|
        BMesh *bm, BMOperator *op,
 | 
						|
        short check_flag, const bool use_ccw, const int edges_len)
 | 
						|
{
 | 
						|
	Heap *heap = BLI_heap_new_ex(edges_len);
 | 
						|
	HeapNode **eheap_table = MEM_mallocN(sizeof(*eheap_table) * edges_len, __func__);
 | 
						|
	int edges_len_rotate = 0;
 | 
						|
 | 
						|
	{
 | 
						|
		BMIter iter;
 | 
						|
		BMEdge *e;
 | 
						|
		BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
 | 
						|
			BM_elem_index_set(e, -1);  /* set_dirty! */
 | 
						|
		}
 | 
						|
		bm->elem_index_dirty |= BM_EDGE;
 | 
						|
	}
 | 
						|
 | 
						|
	{
 | 
						|
		BMOIter siter;
 | 
						|
		BMEdge *e;
 | 
						|
		uint i;
 | 
						|
		BMO_ITER_INDEX (e, &siter, op->slots_in, "edges", BM_EDGE, i) {
 | 
						|
			BM_elem_index_set(e, BM_edge_is_manifold(e) ? i : -1);  /* set_dirty! */
 | 
						|
			eheap_table[i] = NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* First operate on boundary edges, this is often all that's needed,
 | 
						|
	 * regions that have no boundaries are handles after. */
 | 
						|
	enum {
 | 
						|
		PASS_TYPE_BOUNDARY = 0,
 | 
						|
		PASS_TYPE_ALL = 1,
 | 
						|
		PASS_TYPE_DONE = 2,
 | 
						|
	};
 | 
						|
	uint pass_type = PASS_TYPE_BOUNDARY;
 | 
						|
 | 
						|
 | 
						|
	while ((pass_type != PASS_TYPE_DONE) && (edges_len_rotate != edges_len)) {
 | 
						|
		BLI_assert(BLI_heap_is_empty(heap));
 | 
						|
		{
 | 
						|
			BMOIter siter;
 | 
						|
			BMEdge *e;
 | 
						|
			uint i;
 | 
						|
			BMO_ITER_INDEX (e, &siter, op->slots_in, "edges", BM_EDGE, i) {
 | 
						|
				BLI_assert(eheap_table[i] == NULL);
 | 
						|
 | 
						|
				bool ok = (BM_elem_index_get(e) != -1) && BM_edge_rotate_check(e);
 | 
						|
 | 
						|
				if (ok) {
 | 
						|
					if (pass_type == PASS_TYPE_BOUNDARY) {
 | 
						|
						ok = bm_edge_rotate_is_boundary(e);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (ok) {
 | 
						|
					float cost = bm_edge_calc_rotate_cost(e);
 | 
						|
					if (pass_type == PASS_TYPE_BOUNDARY) {
 | 
						|
						/* Trick to ensure once started, non boundaries are handled before other boundary edges.
 | 
						|
						 * This means the first longest boundary defines the starting point which is rotated
 | 
						|
						 * until all its connected edges are exhausted and the next boundary is popped off the heap.
 | 
						|
						 *
 | 
						|
						 * Without this we may rotate from different starting points and meet in the middle
 | 
						|
						 * with obviously uneven topology.
 | 
						|
						 *
 | 
						|
						 * Move from negative to positive value, inverting so large values are still handled first.
 | 
						|
						 */
 | 
						|
						cost = cost != 0.0f ? -1.0f / cost : FLT_MAX;
 | 
						|
					}
 | 
						|
					eheap_table[i] = BLI_heap_insert(heap, cost, e);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (BLI_heap_is_empty(heap)) {
 | 
						|
			pass_type += 1;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		const int edges_len_rotate_prev = edges_len_rotate;
 | 
						|
		while (!BLI_heap_is_empty(heap)) {
 | 
						|
			BMEdge *e_best = BLI_heap_pop_min(heap);
 | 
						|
			eheap_table[BM_elem_index_get(e_best)] = NULL;
 | 
						|
 | 
						|
			/* No problem if this fails, re-evaluate if faces connected to this edge are touched. */
 | 
						|
			if (BM_edge_rotate_check(e_best)) {
 | 
						|
				BMEdge *e_rotate = BM_edge_rotate(bm, e_best, use_ccw, check_flag);
 | 
						|
				if (e_rotate != NULL) {
 | 
						|
					BMO_edge_flag_enable(bm, e_rotate, EDGE_OUT);
 | 
						|
 | 
						|
					/* invalidate so we don't try touch this again. */
 | 
						|
					BM_elem_index_set(e_rotate, -1);  /* set_dirty! */
 | 
						|
 | 
						|
					edges_len_rotate += 1;
 | 
						|
 | 
						|
					/* Note: we could validate all edges which have not been rotated
 | 
						|
					 * (not just previously degenerate edges).
 | 
						|
					 * However there is no real need - they can be left until they're popped off the queue. */
 | 
						|
 | 
						|
					/* We don't know the exact topology after rotating the edge,
 | 
						|
					 * so loop over all faces attached to the new edge, typically this will only be two faces. */
 | 
						|
					BMLoop *l_radial_iter = e_rotate->l;
 | 
						|
					do {
 | 
						|
						/* Skip this edge. */
 | 
						|
						BMLoop *l_iter = l_radial_iter->next;
 | 
						|
						do {
 | 
						|
							BMEdge *e_iter = l_iter->e;
 | 
						|
							const int e_iter_index = BM_elem_index_get(e_iter);
 | 
						|
							if ((e_iter_index != -1) && (eheap_table[e_iter_index] == NULL)) {
 | 
						|
								if (BM_edge_rotate_check(e_iter)) {
 | 
						|
									/* Previously degenerate, now valid. */
 | 
						|
									float cost = bm_edge_calc_rotate_cost(e_iter);
 | 
						|
									eheap_table[e_iter_index] = BLI_heap_insert(heap, cost, e_iter);
 | 
						|
								}
 | 
						|
							}
 | 
						|
						} while ((l_iter = l_iter->next) != l_radial_iter);
 | 
						|
					} while ((l_radial_iter = l_radial_iter->radial_next) != e_rotate->l);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* If no actions were taken, move onto the next pass. */
 | 
						|
		if (edges_len_rotate == edges_len_rotate_prev) {
 | 
						|
			pass_type += 1;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_heap_free(heap, NULL);
 | 
						|
	MEM_freeN(eheap_table);
 | 
						|
}
 | 
						|
 | 
						|
void bmo_rotate_edges_exec(BMesh *bm, BMOperator *op)
 | 
						|
{
 | 
						|
	BMOIter siter;
 | 
						|
	BMEdge *e;
 | 
						|
	const int edges_len  = BMO_slot_buffer_count(op->slots_in, "edges");
 | 
						|
	const bool use_ccw   = BMO_slot_bool_get(op->slots_in, "use_ccw");
 | 
						|
	const bool is_single = (edges_len ==  1);
 | 
						|
	short check_flag = is_single ?
 | 
						|
	        BM_EDGEROT_CHECK_EXISTS :
 | 
						|
	        BM_EDGEROT_CHECK_EXISTS | BM_EDGEROT_CHECK_DEGENERATE;
 | 
						|
 | 
						|
	bool is_simple = true;
 | 
						|
	if (is_single == false) {
 | 
						|
		BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
 | 
						|
			BMFace *f_pair[2];
 | 
						|
			if (BM_edge_face_pair(e, &f_pair[0], &f_pair[1])) {
 | 
						|
				for (uint i = 0; i < ARRAY_SIZE(f_pair); i += 1) {
 | 
						|
					if (BMO_face_flag_test(bm, f_pair[i], FACE_MARK)) {
 | 
						|
						is_simple = false;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					BMO_face_flag_enable(bm, f_pair[i], FACE_MARK);
 | 
						|
				}
 | 
						|
				if (is_simple == false) {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (is_simple) {
 | 
						|
		bm_rotate_edges_simple(bm, op, check_flag, use_ccw);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		bm_rotate_edges_shared(bm, op, check_flag, use_ccw, edges_len);
 | 
						|
	}
 | 
						|
 | 
						|
	BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT);
 | 
						|
}
 |