Also add: - generic callback for bmesh elements. - ability to pass an existing array to a bmesh operator.
		
			
				
	
	
		
			1953 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1953 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * ***** 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 "BLF_translation.h"
 | 
						|
 | 
						|
#include "bmesh.h"
 | 
						|
#include "intern/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 bmo_name_to_slotcode(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier);
 | 
						|
static int bmo_name_to_slotcode_check(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier);
 | 
						|
 | 
						|
static const char *bmo_error_messages[] = {
 | 
						|
	NULL,
 | 
						|
	N_("Self intersection error"),
 | 
						|
	N_("Could not dissolve vert"),
 | 
						|
	N_("Could not connect vertices"),
 | 
						|
	N_("Could not traverse mesh"),
 | 
						|
	N_("Could not dissolve faces"),
 | 
						|
	N_("Tessellation error"),
 | 
						|
	N_("Cannot deal with non-manifold geometry"),
 | 
						|
	N_("Invalid selection"),
 | 
						|
	N_("Internal mesh error"),
 | 
						|
};
 | 
						|
 | 
						|
BLI_STATIC_ASSERT(ARRAY_SIZE(bmo_error_messages) + 1 == BMERR_TOTAL, "message mismatch");
 | 
						|
 | 
						|
/* operator slot type information - size of one element of the type given. */
 | 
						|
const int BMO_OPSLOT_TYPEINFO[BMO_OP_SLOT_TOTAL_TYPES] = {
 | 
						|
	0,                      /*  0: BMO_OP_SLOT_SENTINEL */
 | 
						|
	sizeof(int),            /*  1: BMO_OP_SLOT_BOOL */ 
 | 
						|
	sizeof(int),            /*  2: BMO_OP_SLOT_INT */ 
 | 
						|
	sizeof(float),          /*  3: BMO_OP_SLOT_FLT */ 
 | 
						|
	sizeof(void *),         /*  4: BMO_OP_SLOT_PNT */ 
 | 
						|
	sizeof(void *),         /*  5: BMO_OP_SLOT_PNT */
 | 
						|
	0,                      /*  6: unused */
 | 
						|
	0,                      /*  7: unused */
 | 
						|
	sizeof(float) * 3,      /*  8: BMO_OP_SLOT_VEC */
 | 
						|
	sizeof(void *),	        /*  9: BMO_OP_SLOT_ELEMENT_BUF */
 | 
						|
	sizeof(void *)          /* 10: BMO_OP_SLOT_MAPPING */
 | 
						|
};
 | 
						|
 | 
						|
/* 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;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief 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++;
 | 
						|
 | 
						|
	BLI_assert(bm->totflags > 0);
 | 
						|
 | 
						|
	/* add flag layer, if appropriate */
 | 
						|
	if (bm->stackdepth > 1)
 | 
						|
		bmo_flag_layer_alloc(bm);
 | 
						|
	else
 | 
						|
		bmo_flag_layer_clear(bm);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief 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--;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* use for both slot_types_in and slot_types_out */
 | 
						|
static void bmo_op_slots_init(const BMOSlotType *slot_types, BMOpSlot *slot_args)
 | 
						|
{
 | 
						|
	BMOpSlot *slot;
 | 
						|
	unsigned int i;
 | 
						|
	for (i = 0; slot_types[i].type; i++) {
 | 
						|
		slot = &slot_args[i];
 | 
						|
		slot->slot_name    = slot_types[i].name;
 | 
						|
		slot->slot_type    = slot_types[i].type;
 | 
						|
		slot->slot_subtype = slot_types[i].subtype;
 | 
						|
		// slot->index = i;  // UNUSED
 | 
						|
 | 
						|
		switch (slot->slot_type) {
 | 
						|
			case BMO_OP_SLOT_MAPPING:
 | 
						|
				slot->data.ghash = BLI_ghash_ptr_new("bmesh slot map hash");
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void bmo_op_slots_free(const BMOSlotType *slot_types, BMOpSlot *slot_args)
 | 
						|
{
 | 
						|
	BMOpSlot *slot;
 | 
						|
	unsigned int i;
 | 
						|
	for (i = 0; slot_types[i].type; i++) {
 | 
						|
		slot = &slot_args[i];
 | 
						|
		switch (slot->slot_type) {
 | 
						|
			case BMO_OP_SLOT_MAPPING:
 | 
						|
				BLI_ghash_free(slot->data.ghash, NULL, NULL);
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMESH OPSTACK INIT OP
 | 
						|
 *
 | 
						|
 * Initializes an operator structure to a certain type
 | 
						|
 */
 | 
						|
void BMO_op_init(BMesh *bm, BMOperator *op, const int flag, const char *opname)
 | 
						|
{
 | 
						|
	int opcode = BMO_opcode_from_opname(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->type_flag = bmo_opdefines[opcode]->type_flag;
 | 
						|
	op->flag = flag;
 | 
						|
	
 | 
						|
	/* initialize the operator slot types */
 | 
						|
	bmo_op_slots_init(bmo_opdefines[opcode]->slot_types_in,  op->slots_in);
 | 
						|
	bmo_op_slots_init(bmo_opdefines[opcode]->slot_types_out, op->slots_out);
 | 
						|
 | 
						|
	/* callback */
 | 
						|
	op->exec = bmo_opdefines[opcode]->exec;
 | 
						|
 | 
						|
	/* memarena, used for operator's slot buffers */
 | 
						|
	op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
 | 
						|
	BLI_memarena_use_calloc(op->arena);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMESH OPSTACK EXEC OP
 | 
						|
 *
 | 
						|
 * Executes a passed in operator.
 | 
						|
 *
 | 
						|
 * This handles the allocation and freeing of temporary flag
 | 
						|
 * layers and starting/stopping the modeling loop.
 | 
						|
 * Can be called from other operators exec callbacks as well.
 | 
						|
 */
 | 
						|
void BMO_op_exec(BMesh *bm, BMOperator *op)
 | 
						|
{
 | 
						|
	/* allocate tool flags on demand */
 | 
						|
	BM_mesh_elem_toolflags_ensure(bm);
 | 
						|
 | 
						|
	BMO_push(bm, op);
 | 
						|
 | 
						|
	if (bm->stackdepth == 2)
 | 
						|
		bmesh_edit_begin(bm, op->type_flag);
 | 
						|
	op->exec(bm, op);
 | 
						|
	
 | 
						|
	if (bm->stackdepth == 2)
 | 
						|
		bmesh_edit_end(bm, op->type_flag);
 | 
						|
	
 | 
						|
	BMO_pop(bm);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMESH OPSTACK FINISH OP
 | 
						|
 *
 | 
						|
 * Does housekeeping chores related to finishing up an operator.
 | 
						|
 */
 | 
						|
void BMO_op_finish(BMesh *bm, BMOperator *op)
 | 
						|
{
 | 
						|
	bmo_op_slots_free(bmo_opdefines[op->type]->slot_types_in,  op->slots_in);
 | 
						|
	bmo_op_slots_free(bmo_opdefines[op->type]->slot_types_out, op->slots_out);
 | 
						|
 | 
						|
	BLI_memarena_free(op->arena);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
	BM_ELEM_INDEX_VALIDATE(bm, "post bmo", bmo_opdefines[op->type]->opname);
 | 
						|
 | 
						|
	/* avoid accidental re-use */
 | 
						|
	memset(op, 0xff, sizeof(*op));
 | 
						|
#else
 | 
						|
	(void)bm;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMESH OPSTACK HAS SLOT
 | 
						|
 *
 | 
						|
 * \return Success if the slot if found.
 | 
						|
 */
 | 
						|
bool BMO_slot_exists(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
 | 
						|
{
 | 
						|
	int slot_code = bmo_name_to_slotcode(slot_args, identifier);
 | 
						|
	return (slot_code >= 0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMESH OPSTACK GET SLOT
 | 
						|
 *
 | 
						|
 * Returns a pointer to the slot of type 'slot_code'
 | 
						|
 */
 | 
						|
BMOpSlot *BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
 | 
						|
{
 | 
						|
	int slot_code = bmo_name_to_slotcode_check(slot_args, identifier);
 | 
						|
 | 
						|
	if (UNLIKELY(slot_code < 0)) {
 | 
						|
		//return &BMOpEmptySlot;
 | 
						|
		BLI_assert(0);
 | 
						|
		return NULL;  /* better crash */
 | 
						|
	}
 | 
						|
 | 
						|
	return &slot_args[slot_code];
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMESH OPSTACK COPY SLOT
 | 
						|
 *
 | 
						|
 * define used.
 | 
						|
 * Copies data from one slot to another.
 | 
						|
 */
 | 
						|
void _bmo_slot_copy(BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src,
 | 
						|
                    BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst,
 | 
						|
                    struct MemArena *arena_dst)
 | 
						|
{
 | 
						|
	BMOpSlot *slot_src = BMO_slot_get(slot_args_src, slot_name_src);
 | 
						|
	BMOpSlot *slot_dst = BMO_slot_get(slot_args_dst, slot_name_dst);
 | 
						|
 | 
						|
	if (slot_src == slot_dst)
 | 
						|
		return;
 | 
						|
 | 
						|
	BLI_assert(slot_src->slot_type == slot_dst->slot_type);
 | 
						|
	if (slot_src->slot_type != slot_dst->slot_type) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (slot_dst->slot_type == BMO_OP_SLOT_ELEMENT_BUF) {
 | 
						|
		/* do buffer copy */
 | 
						|
		slot_dst->data.buf = NULL;
 | 
						|
		slot_dst->len = slot_src->len;
 | 
						|
		if (slot_dst->len) {
 | 
						|
			/* check dest has all flags enabled that the source has */
 | 
						|
			const eBMOpSlotSubType_Elem src_elem_flag = (slot_src->slot_subtype.elem & BM_ALL_NOLOOP);
 | 
						|
			const eBMOpSlotSubType_Elem dst_elem_flag = (slot_dst->slot_subtype.elem & BM_ALL_NOLOOP);
 | 
						|
 | 
						|
			if ((src_elem_flag | dst_elem_flag) == dst_elem_flag) {
 | 
						|
				/* pass */
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				/* check types */
 | 
						|
				const unsigned int tot = slot_src->len;
 | 
						|
				unsigned int i;
 | 
						|
				unsigned int out = 0;
 | 
						|
				BMElem **ele_src = (BMElem **)slot_src->data.buf;
 | 
						|
				for (i = 0; i < tot; i++, ele_src++) {
 | 
						|
					if ((*ele_src)->head.htype & dst_elem_flag) {
 | 
						|
						out++;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				if (out != tot) {
 | 
						|
					slot_dst->len = out;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (slot_dst->len) {
 | 
						|
				const int slot_alloc_size = BMO_OPSLOT_TYPEINFO[slot_dst->slot_type] * slot_dst->len;
 | 
						|
				slot_dst->data.buf = BLI_memarena_alloc(arena_dst, slot_alloc_size);
 | 
						|
				if (slot_src->len == slot_dst->len) {
 | 
						|
					memcpy(slot_dst->data.buf, slot_src->data.buf, slot_alloc_size);
 | 
						|
				}
 | 
						|
				else {
 | 
						|
					/* only copy compatible elements */
 | 
						|
					const unsigned int tot = slot_src->len;
 | 
						|
					unsigned int i;
 | 
						|
					BMElem **ele_src = (BMElem **)slot_src->data.buf;
 | 
						|
					BMElem **ele_dst = (BMElem **)slot_dst->data.buf;
 | 
						|
					for (i = 0; i < tot; i++, ele_src++) {
 | 
						|
						if ((*ele_src)->head.htype & dst_elem_flag) {
 | 
						|
							*ele_dst = *ele_src;
 | 
						|
							ele_dst++;
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (slot_dst->slot_type == BMO_OP_SLOT_MAPPING) {
 | 
						|
		GHashIterator gh_iter;
 | 
						|
		GHASH_ITER (gh_iter, slot_src->data.ghash) {
 | 
						|
			void *key = BLI_ghashIterator_getKey(&gh_iter);
 | 
						|
			void *val = BLI_ghashIterator_getValue(&gh_iter);
 | 
						|
			BLI_ghash_insert(slot_dst->data.ghash, key, val);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		slot_dst->data = slot_src->data;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * BMESH OPSTACK SET XXX
 | 
						|
 *
 | 
						|
 * Sets the value of a slot depending on it's type
 | 
						|
 */
 | 
						|
 | 
						|
void BMO_slot_float_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const float f)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_FLT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_FLT))
 | 
						|
		return;
 | 
						|
 | 
						|
	slot->data.f = f;
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_int_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const int i)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_INT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_INT))
 | 
						|
		return;
 | 
						|
 | 
						|
	slot->data.i = i;
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_bool_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const bool i)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_BOOL);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_BOOL))
 | 
						|
		return;
 | 
						|
 | 
						|
	slot->data.i = i;
 | 
						|
}
 | 
						|
 | 
						|
/* only supports square mats */
 | 
						|
void BMO_slot_mat_set(BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const float *mat, int size)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_MAT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_MAT))
 | 
						|
		return;
 | 
						|
 | 
						|
	slot->len = 4;
 | 
						|
	slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float) * 4 * 4);
 | 
						|
	
 | 
						|
	if (size == 4) {
 | 
						|
		copy_m4_m4(slot->data.p, (float (*)[4])mat);
 | 
						|
	}
 | 
						|
	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);
 | 
						|
 | 
						|
		zero_m4(slot->data.p);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_mat4_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, float r_mat[4][4])
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_MAT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_MAT))
 | 
						|
		return;
 | 
						|
 | 
						|
	if (slot->data.p) {
 | 
						|
		copy_m4_m4(r_mat, BMO_SLOT_AS_MATRIX(slot));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		unit_m4(r_mat);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_mat3_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, float r_mat[3][3])
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_MAT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_MAT))
 | 
						|
		return;
 | 
						|
 | 
						|
	if (slot->data.p) {
 | 
						|
		copy_m3_m4(r_mat, BMO_SLOT_AS_MATRIX(slot));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		unit_m3(r_mat);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_ptr_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, void *p)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_PTR);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_PTR))
 | 
						|
		return;
 | 
						|
 | 
						|
	slot->data.p = p;
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_vec_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const float vec[3])
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_VEC);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_VEC))
 | 
						|
		return;
 | 
						|
 | 
						|
	copy_v3_v3(slot->data.vec, vec);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_FLT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_FLT))
 | 
						|
		return 0.0f;
 | 
						|
 | 
						|
	return slot->data.f;
 | 
						|
}
 | 
						|
 | 
						|
int BMO_slot_int_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_INT);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_INT))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	return slot->data.i;
 | 
						|
}
 | 
						|
 | 
						|
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_BOOL);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_BOOL))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	return slot->data.i;
 | 
						|
}
 | 
						|
 | 
						|
/* if you want a copy of the elem buffer */
 | 
						|
void *BMO_slot_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int *len)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	void **ret;
 | 
						|
 | 
						|
	/* could add support for mapping type */
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
 | 
						|
	ret = MEM_mallocN(sizeof(void *) * slot->len, __func__);
 | 
						|
	memcpy(ret, slot->data.buf, sizeof(void *) * slot->len);
 | 
						|
	*len = slot->len;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void *BMO_slot_ptr_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_PTR);
 | 
						|
	if (!(slot->slot_type == BMO_OP_SLOT_PTR))
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	return slot->data.p;
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_vec_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, float r_vec[3])
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_VEC);
 | 
						|
	if (!(slot->slot_type == 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 enabled (or disabled if test_for_enabled is false).
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
static int bmo_mesh_flag_count(BMesh *bm, const char htype, const short oflag,
 | 
						|
                               const bool test_for_enabled)
 | 
						|
{
 | 
						|
	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;
 | 
						|
	int count = 0;
 | 
						|
	BMElemF *ele_f;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < 3; i++) {
 | 
						|
		if (htype & flag_types[i]) {
 | 
						|
			BM_ITER_MESH (ele_f, &iter, bm, iter_types[i]) {
 | 
						|
				if (BMO_elem_flag_test_bool(bm, ele_f, oflag) == test_for_enabled)
 | 
						|
					count++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int BMO_mesh_enabled_flag_count(BMesh *bm, const char htype, const short oflag)
 | 
						|
{
 | 
						|
	return bmo_mesh_flag_count(bm, htype, oflag, true);
 | 
						|
}
 | 
						|
 | 
						|
int BMO_mesh_disabled_flag_count(BMesh *bm, const char htype, const short oflag)
 | 
						|
{
 | 
						|
	return bmo_mesh_flag_count(bm, htype, oflag, false);
 | 
						|
}
 | 
						|
 | 
						|
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};
 | 
						|
 | 
						|
	BMElemF *ele;
 | 
						|
	int i;
 | 
						|
 | 
						|
#pragma omp parallel for schedule(static) if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
 | 
						|
	for (i = 0; i < 3; i++) {
 | 
						|
		if (htype & flag_types[i]) {
 | 
						|
			BMIter iter;
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, iter_types[i]) {
 | 
						|
				BMO_elem_flag_disable(bm, ele, oflag);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BMO_mesh_selected_remap(BMesh *bm,
 | 
						|
                             BMOpSlot *slot_vert_map,
 | 
						|
                             BMOpSlot *slot_edge_map,
 | 
						|
                             BMOpSlot *slot_face_map,
 | 
						|
                             const bool check_select)
 | 
						|
{
 | 
						|
	if (bm->selected.first) {
 | 
						|
		BMEditSelection *ese, *ese_next;
 | 
						|
		BMOpSlot *slot_elem_map;
 | 
						|
 | 
						|
		for (ese = bm->selected.first; ese; ese = ese_next) {
 | 
						|
			ese_next = ese->next;
 | 
						|
 | 
						|
			switch (ese->htype) {
 | 
						|
				case BM_VERT: slot_elem_map = slot_vert_map; break;
 | 
						|
				case BM_EDGE: slot_elem_map = slot_edge_map; break;
 | 
						|
				default:      slot_elem_map = slot_face_map; break;
 | 
						|
			}
 | 
						|
 | 
						|
			ese->ele = BMO_slot_map_elem_get(slot_elem_map, ese->ele);
 | 
						|
 | 
						|
			if (UNLIKELY((ese->ele == NULL) ||
 | 
						|
			             (check_select && (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT) == false))))
 | 
						|
			{
 | 
						|
				BLI_remlink(&bm->selected, ese);
 | 
						|
				MEM_freeN(ese);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (bm->act_face) {
 | 
						|
		BMFace *f = BMO_slot_map_elem_get(slot_face_map, bm->act_face);
 | 
						|
		if (f) {
 | 
						|
			bm->act_face = f;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int BMO_slot_buffer_count(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	
 | 
						|
	/* check if its actually a buffer */
 | 
						|
	if (slot->slot_type != BMO_OP_SLOT_ELEMENT_BUF)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	return slot->len;
 | 
						|
}
 | 
						|
 | 
						|
int BMO_slot_map_count(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_MAPPING);
 | 
						|
	return BLI_ghash_size(slot->data.ghash);
 | 
						|
}
 | 
						|
 | 
						|
/* inserts a key/value mapping into a mapping slot.  note that it copies the
 | 
						|
 * value, it doesn't store a reference to it. */
 | 
						|
 | 
						|
void BMO_slot_map_insert(BMOperator *op, BMOpSlot *slot,
 | 
						|
                         const void *element, const void *data)
 | 
						|
{
 | 
						|
	(void) op;  /* Ignored in release builds. */
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_MAPPING);
 | 
						|
	BMO_ASSERT_SLOT_IN_OP(slot, op);
 | 
						|
 | 
						|
	BLI_ghash_insert(slot->data.ghash, (void *)element, (void *)data);
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
void *bmo_slot_buffer_grow(BMesh *bm, BMOperator *op, int slot_code, int totadd)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = &op->slots[slot_code];
 | 
						|
	void *tmp;
 | 
						|
	ssize_t allocsize;
 | 
						|
	
 | 
						|
	BLI_assert(slot->slottype == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
 | 
						|
	/* check if its actually a buffer */
 | 
						|
	if (slot->slottype != BMO_OP_SLOT_ELEMENT_BUF)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (slot->flag & BMOS_DYNAMIC_ARRAY) {
 | 
						|
		if (slot->len >= slot->size) {
 | 
						|
			slot->size = (slot->size + 1 + totadd) * 2;
 | 
						|
 | 
						|
			allocsize = BMO_OPSLOT_TYPEINFO[bmo_opdefines[op->type]->slot_types[slot_code].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[bmo_opdefines[op->type]->slot_types[slot_code].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(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                          const char htype, const short oflag)
 | 
						|
{
 | 
						|
	GHashIterator gh_iter;
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BMElemF *ele_f;
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_MAPPING);
 | 
						|
 | 
						|
 | 
						|
	GHASH_ITER (gh_iter, slot->data.ghash) {
 | 
						|
		ele_f = BLI_ghashIterator_getKey(&gh_iter);
 | 
						|
		if (ele_f->head.htype & htype) {
 | 
						|
			BMO_elem_flag_enable(bm, ele_f, oflag);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void *BMO_slot_buffer_alloc(BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const int len)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
 | 
						|
	/* check if its actually a buffer */
 | 
						|
	if (slot->slot_type != BMO_OP_SLOT_ELEMENT_BUF)
 | 
						|
		return NULL;
 | 
						|
	
 | 
						|
	slot->len = len;
 | 
						|
	if (len) {
 | 
						|
		slot->data.buf = BLI_memarena_alloc(op->arena, BMO_OPSLOT_TYPEINFO[slot->slot_type] * len);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		slot->data.buf = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return slot->data.buf;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMO_ALL_TO_SLOT
 | 
						|
 *
 | 
						|
 * Copies all elements of a certain type into an operator slot.
 | 
						|
 */
 | 
						|
void BMO_slot_buffer_from_all(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
 | 
						|
                              const char *slot_name, const char htype)
 | 
						|
{
 | 
						|
	BMOpSlot *output = BMO_slot_get(slot_args, slot_name);
 | 
						|
	int totelement = 0, i = 0;
 | 
						|
	
 | 
						|
	BLI_assert(output->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((output->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
 | 
						|
	if (htype & BM_VERT) totelement += bm->totvert;
 | 
						|
	if (htype & BM_EDGE) totelement += bm->totedge;
 | 
						|
	if (htype & BM_FACE) totelement += bm->totface;
 | 
						|
 | 
						|
	if (totelement) {
 | 
						|
		BMIter iter;
 | 
						|
		BMHeader *ele;
 | 
						|
 | 
						|
		BMO_slot_buffer_alloc(op, slot_args, slot_name, totelement);
 | 
						|
 | 
						|
		/* TODO - collapse these loops into one */
 | 
						|
 | 
						|
		if (htype & BM_VERT) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
				output->data.buf[i] = ele;
 | 
						|
				i++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (htype & BM_EDGE) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
 | 
						|
				output->data.buf[i] = ele;
 | 
						|
				i++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (htype & BM_FACE) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
 | 
						|
				output->data.buf[i] = ele;
 | 
						|
				i++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMO_HEADERFLAG_TO_SLOT
 | 
						|
 *
 | 
						|
 * Copies elements of a certain type, which have a certain header flag
 | 
						|
 * enabled/disabled into a slot for an operator.
 | 
						|
 */
 | 
						|
static void bmo_slot_buffer_from_hflag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                       const char htype, const char hflag,
 | 
						|
                                       const bool test_for_enabled)
 | 
						|
{
 | 
						|
	BMOpSlot *output = BMO_slot_get(slot_args, slot_name);
 | 
						|
	int totelement = 0, i = 0;
 | 
						|
	const bool respecthide = ((op->flag & BMO_FLAG_RESPECT_HIDE) != 0) && ((hflag & BM_ELEM_HIDDEN) == 0);
 | 
						|
 | 
						|
	BLI_assert(output->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((output->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
	BLI_assert((output->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) == 0);
 | 
						|
 | 
						|
	if (test_for_enabled)
 | 
						|
		totelement = BM_mesh_elem_hflag_count_enabled(bm, htype, hflag, respecthide);
 | 
						|
	else
 | 
						|
		totelement = BM_mesh_elem_hflag_count_disabled(bm, htype, hflag, respecthide);
 | 
						|
 | 
						|
	if (totelement) {
 | 
						|
		BMIter iter;
 | 
						|
		BMElem *ele;
 | 
						|
 | 
						|
		BMO_slot_buffer_alloc(op, slot_args, slot_name, totelement);
 | 
						|
 | 
						|
		/* TODO - collapse these loops into one */
 | 
						|
 | 
						|
		if (htype & BM_VERT) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
				if ((!respecthide || !BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) &&
 | 
						|
				    BM_elem_flag_test_bool(ele, hflag) == test_for_enabled)
 | 
						|
				{
 | 
						|
					output->data.buf[i] = ele;
 | 
						|
					i++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (htype & BM_EDGE) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
 | 
						|
				if ((!respecthide || !BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) &&
 | 
						|
				    BM_elem_flag_test_bool(ele, hflag) == test_for_enabled)
 | 
						|
				{
 | 
						|
					output->data.buf[i] = ele;
 | 
						|
					i++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (htype & BM_FACE) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
 | 
						|
				if ((!respecthide || !BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) &&
 | 
						|
				    BM_elem_flag_test_bool(ele, hflag) == test_for_enabled)
 | 
						|
				{
 | 
						|
					output->data.buf[i] = ele;
 | 
						|
					i++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		output->len = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_buffer_from_enabled_hflag(BMesh *bm, BMOperator *op,
 | 
						|
                                        BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                        const char htype, const char hflag)
 | 
						|
{
 | 
						|
	bmo_slot_buffer_from_hflag(bm, op, slot_args, slot_name, htype, hflag, true);
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_buffer_from_disabled_hflag(BMesh *bm, BMOperator *op,
 | 
						|
                                         BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                         const char htype, const char hflag)
 | 
						|
{
 | 
						|
	bmo_slot_buffer_from_hflag(bm, op, slot_args, slot_name, htype, hflag, false);
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_buffer_from_single(BMOperator *op, BMOpSlot *slot, BMHeader *ele)
 | 
						|
{
 | 
						|
	BMO_ASSERT_SLOT_IN_OP(slot, op);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE);
 | 
						|
	BLI_assert(slot->len == 0 || slot->len == 1);
 | 
						|
 | 
						|
	BLI_assert(slot->slot_subtype.elem & ele->htype);
 | 
						|
 | 
						|
	slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(void *) * 4);  /* XXX, why 'x4' ? */
 | 
						|
	slot->len = 1;
 | 
						|
	*slot->data.buf = ele;
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_buffer_from_array(BMOperator *op, BMOpSlot *slot, BMHeader **ele_buffer, int ele_buffer_len)
 | 
						|
{
 | 
						|
	BMO_ASSERT_SLOT_IN_OP(slot, op);
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(slot->len == 0 || slot->len == ele_buffer_len);
 | 
						|
 | 
						|
	if (slot->data.buf == NULL) {
 | 
						|
		slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(*slot->data.buf) * ele_buffer_len);
 | 
						|
	}
 | 
						|
 | 
						|
	slot->len = ele_buffer_len;
 | 
						|
	memcpy(slot->data.buf, ele_buffer, ele_buffer_len * sizeof(*slot->data.buf));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void *BMO_slot_buffer_get_single(BMOpSlot *slot)
 | 
						|
{
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE);
 | 
						|
	BLI_assert(slot->len == 0 || slot->len == 1);
 | 
						|
 | 
						|
	return slot->len ? (BMHeader *)slot->data.buf[0] : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Copies the values from another slot to the end of the output slot.
 | 
						|
 */
 | 
						|
void _bmo_slot_buffer_append(BMOpSlot slot_args_dst[BMO_OP_MAX_SLOTS], const char *slot_name_dst,
 | 
						|
                             BMOpSlot slot_args_src[BMO_OP_MAX_SLOTS], const char *slot_name_src,
 | 
						|
                             struct MemArena *arena_dst)
 | 
						|
{
 | 
						|
	BMOpSlot *slot_dst = BMO_slot_get(slot_args_dst, slot_name_dst);
 | 
						|
	BMOpSlot *slot_src  = BMO_slot_get(slot_args_src,  slot_name_src);
 | 
						|
 | 
						|
	BLI_assert(slot_dst->slot_type == BMO_OP_SLOT_ELEMENT_BUF &&
 | 
						|
	           slot_src->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
 | 
						|
	if (slot_dst->len == 0) {
 | 
						|
		/* output slot is empty, copy rather than append */
 | 
						|
		_bmo_slot_copy(slot_args_src, slot_name_src,
 | 
						|
		               slot_args_dst, slot_name_dst,
 | 
						|
		               arena_dst);
 | 
						|
	}
 | 
						|
	else if (slot_src->len != 0) {
 | 
						|
		int elem_size = BMO_OPSLOT_TYPEINFO[slot_dst->slot_type];
 | 
						|
		int alloc_size = elem_size * (slot_dst->len + slot_src->len);
 | 
						|
		/* allocate new buffer */
 | 
						|
		void *buf = BLI_memarena_alloc(arena_dst, alloc_size);
 | 
						|
 | 
						|
		/* copy slot data */
 | 
						|
		memcpy(buf, slot_dst->data.buf, elem_size * slot_dst->len);
 | 
						|
		memcpy(((char *)buf) + elem_size * slot_dst->len, slot_src->data.buf, elem_size * slot_src->len);
 | 
						|
 | 
						|
		slot_dst->data.buf = buf;
 | 
						|
		slot_dst->len += slot_src->len;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMO_FLAG_TO_SLOT
 | 
						|
 *
 | 
						|
 * Copies elements of a certain type, which have a certain flag set
 | 
						|
 * into an output slot for an operator.
 | 
						|
 */
 | 
						|
static void bmo_slot_buffer_from_flag(BMesh *bm, BMOperator *op,
 | 
						|
                                      BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                      const char htype, const short oflag,
 | 
						|
                                      const bool test_for_enabled)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	int totelement, i = 0;
 | 
						|
 | 
						|
	BLI_assert(op->slots_in == slot_args || op->slots_out == slot_args);
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((slot->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
	BLI_assert((slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) == 0);
 | 
						|
 | 
						|
	if (test_for_enabled)
 | 
						|
		totelement = BMO_mesh_enabled_flag_count(bm, htype, oflag);
 | 
						|
	else
 | 
						|
		totelement = BMO_mesh_disabled_flag_count(bm, htype, oflag);
 | 
						|
 | 
						|
	if (totelement) {
 | 
						|
		BMIter iter;
 | 
						|
		BMHeader *ele;
 | 
						|
		BMHeader **ele_array;
 | 
						|
 | 
						|
		BMO_slot_buffer_alloc(op, slot_args, slot_name, totelement);
 | 
						|
 | 
						|
		ele_array = (BMHeader **)slot->data.buf;
 | 
						|
 | 
						|
		/* TODO - collapse these loops into one */
 | 
						|
 | 
						|
		if (htype & BM_VERT) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
				if (BMO_elem_flag_test_bool(bm, (BMElemF *)ele, oflag) == test_for_enabled) {
 | 
						|
					ele_array[i] = ele;
 | 
						|
					i++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (htype & BM_EDGE) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
 | 
						|
				if (BMO_elem_flag_test_bool(bm, (BMElemF *)ele, oflag) == test_for_enabled) {
 | 
						|
					ele_array[i] = ele;
 | 
						|
					i++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (htype & BM_FACE) {
 | 
						|
			BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
 | 
						|
				if (BMO_elem_flag_test_bool(bm, (BMElemF *)ele, oflag) == test_for_enabled) {
 | 
						|
					ele_array[i] = ele;
 | 
						|
					i++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		slot->len = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op,
 | 
						|
                                       BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                       const char htype, const short oflag)
 | 
						|
{
 | 
						|
	bmo_slot_buffer_from_flag(bm, op, slot_args, slot_name, htype, oflag, true);
 | 
						|
}
 | 
						|
 | 
						|
void BMO_slot_buffer_from_disabled_flag(BMesh *bm, BMOperator *op,
 | 
						|
                                        BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                        const char htype, const short oflag)
 | 
						|
{
 | 
						|
	bmo_slot_buffer_from_flag(bm, op, slot_args, slot_name, htype, oflag, false);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief 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,
 | 
						|
                                  BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                  const char htype, const char hflag, const bool do_flush)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BMElem **data =  (BMElem **)slot->data.buf;
 | 
						|
	int i;
 | 
						|
	const bool do_flush_select = (do_flush && (hflag & BM_ELEM_SELECT));
 | 
						|
	const bool do_flush_hide = (do_flush && (hflag & BM_ELEM_HIDDEN));
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((slot->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
	BLI_assert((slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) == 0);
 | 
						|
 | 
						|
	for (i = 0; i < slot->len; i++, data++) {
 | 
						|
		if (!(htype & (*data)->head.htype))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (do_flush_select) {
 | 
						|
			BM_elem_select_set(bm, *data, true);
 | 
						|
		}
 | 
						|
 | 
						|
		if (do_flush_hide) {
 | 
						|
			BM_elem_hide_set(bm, *data, false);
 | 
						|
		}
 | 
						|
 | 
						|
		BM_elem_flag_enable(*data, hflag);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief 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,
 | 
						|
                                   BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                   const char htype, const char hflag, const bool do_flush)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BMElem **data =  (BMElem **)slot->data.buf;
 | 
						|
	int i;
 | 
						|
	const bool do_flush_select = (do_flush && (hflag & BM_ELEM_SELECT));
 | 
						|
	const bool do_flush_hide = (do_flush && (hflag & BM_ELEM_HIDDEN));
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((slot->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
 | 
						|
	for (i = 0; i < slot->len; i++, data++) {
 | 
						|
		if (!(htype & (*data)->head.htype))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (do_flush_select) {
 | 
						|
			BM_elem_select_set(bm, *data, false);
 | 
						|
		}
 | 
						|
 | 
						|
		if (do_flush_hide) {
 | 
						|
			BM_elem_hide_set(bm, *data, false);
 | 
						|
		}
 | 
						|
 | 
						|
		BM_elem_flag_disable(*data, hflag);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMO_FLAG_BUFFER
 | 
						|
 *
 | 
						|
 * Flags elements in a slots buffer
 | 
						|
 */
 | 
						|
void BMO_slot_buffer_flag_enable(BMesh *bm,
 | 
						|
                                 BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                 const char htype, const short oflag)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BMHeader **data =  slot->data.p;
 | 
						|
	int i;
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((slot->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
 | 
						|
	for (i = 0; i < slot->len; i++) {
 | 
						|
		if (!(htype & data[i]->htype))
 | 
						|
			continue;
 | 
						|
 | 
						|
		BMO_elem_flag_enable(bm, (BMElemF *)data[i], oflag);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief BMO_FLAG_BUFFER
 | 
						|
 *
 | 
						|
 * Removes flags from elements in a slots buffer
 | 
						|
 */
 | 
						|
void BMO_slot_buffer_flag_disable(BMesh *bm,
 | 
						|
                                  BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                                  const char htype, const short oflag)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	BMHeader **data = (BMHeader **)slot->data.buf;
 | 
						|
	int i;
 | 
						|
 | 
						|
	BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF);
 | 
						|
	BLI_assert(((slot->slot_subtype.elem & BM_ALL_NOLOOP) & htype) == htype);
 | 
						|
 | 
						|
	for (i = 0; i < slot->len; i++) {
 | 
						|
		if (!(htype & data[i]->htype))
 | 
						|
			continue;
 | 
						|
 | 
						|
		BMO_elem_flag_disable(bm, (BMElemF *)data[i], oflag);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief 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)
 | 
						|
{
 | 
						|
	/* set the index values since we are looping over all data anyway,
 | 
						|
	 * may save time later on */
 | 
						|
 | 
						|
	BLI_mempool *voldpool = bm->vtoolflagpool;  /* old flag pool */
 | 
						|
	BLI_mempool *eoldpool = bm->etoolflagpool;  /* old flag pool */
 | 
						|
	BLI_mempool *foldpool = bm->ftoolflagpool;  /* old flag pool */
 | 
						|
 | 
						|
	/* store memcpy size for reuse */
 | 
						|
	const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer));
 | 
						|
 | 
						|
	bm->totflags++;
 | 
						|
 | 
						|
	bm->vtoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer) * bm->totflags, bm->totvert, 512, BLI_MEMPOOL_NOP);
 | 
						|
	bm->etoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer) * bm->totflags, bm->totedge, 512, BLI_MEMPOOL_NOP);
 | 
						|
	bm->ftoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer) * bm->totflags, bm->totface, 512, BLI_MEMPOOL_NOP);
 | 
						|
 | 
						|
#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
 | 
						|
	{
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
 | 
						|
			BLI_mempool *newpool = bm->vtoolflagpool;
 | 
						|
 | 
						|
			/* now go through and memcpy all the flags. Loops don't get a flag layer at this time.. */
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_VERTS_OF_MESH, i) {
 | 
						|
				void *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_API_FLAG_CLEAR((BMElemF *)ele);
 | 
						|
			}
 | 
						|
		}
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
 | 
						|
			BLI_mempool *newpool = bm->etoolflagpool;
 | 
						|
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_EDGES_OF_MESH, i) {
 | 
						|
				void *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_API_FLAG_CLEAR((BMElemF *)ele);
 | 
						|
			}
 | 
						|
		}
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
 | 
						|
			BLI_mempool *newpool = bm->ftoolflagpool;
 | 
						|
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, i) {
 | 
						|
				void *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_API_FLAG_CLEAR((BMElemF *)ele);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_mempool_destroy(voldpool);
 | 
						|
	BLI_mempool_destroy(eoldpool);
 | 
						|
	BLI_mempool_destroy(foldpool);
 | 
						|
 | 
						|
	bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE);
 | 
						|
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void bmo_flag_layer_free(BMesh *bm)
 | 
						|
{
 | 
						|
	/* set the index values since we are looping over all data anyway,
 | 
						|
	 * may save time later on */
 | 
						|
 | 
						|
	BLI_mempool *voldpool = bm->vtoolflagpool;
 | 
						|
	BLI_mempool *eoldpool = bm->etoolflagpool;
 | 
						|
	BLI_mempool *foldpool = bm->ftoolflagpool;
 | 
						|
 | 
						|
	/* store memcpy size for reuse */
 | 
						|
	const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer));
 | 
						|
 | 
						|
	/* de-increment the totflags first.. */
 | 
						|
	bm->totflags--;
 | 
						|
 | 
						|
	bm->vtoolflagpool = BLI_mempool_create(new_totflags_size, bm->totvert, 512, BLI_MEMPOOL_NOP);
 | 
						|
	bm->etoolflagpool = BLI_mempool_create(new_totflags_size, bm->totedge, 512, BLI_MEMPOOL_NOP);
 | 
						|
	bm->ftoolflagpool = BLI_mempool_create(new_totflags_size, bm->totface, 512, BLI_MEMPOOL_NOP);
 | 
						|
 | 
						|
#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
 | 
						|
	{
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
 | 
						|
			BLI_mempool *newpool = bm->vtoolflagpool;
 | 
						|
 | 
						|
			/* now go through and memcpy all the flag */
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_VERTS_OF_MESH, i) {
 | 
						|
				void *oldflags = ele->oflags;
 | 
						|
				ele->oflags = BLI_mempool_alloc(newpool);
 | 
						|
				memcpy(ele->oflags, oldflags, new_totflags_size);
 | 
						|
				BM_elem_index_set(ele, i); /* set_inline */
 | 
						|
				BM_ELEM_API_FLAG_CLEAR((BMElemF *)ele);
 | 
						|
			}
 | 
						|
		}
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
 | 
						|
			BLI_mempool *newpool = bm->etoolflagpool;
 | 
						|
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_EDGES_OF_MESH, i) {
 | 
						|
				void *oldflags = ele->oflags;
 | 
						|
				ele->oflags = BLI_mempool_alloc(newpool);
 | 
						|
				memcpy(ele->oflags, oldflags, new_totflags_size);
 | 
						|
				BM_elem_index_set(ele, i); /* set_inline */
 | 
						|
				BM_ELEM_API_FLAG_CLEAR((BMElemF *)ele);
 | 
						|
			}
 | 
						|
		}
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
 | 
						|
			BLI_mempool *newpool = bm->ftoolflagpool;
 | 
						|
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, i) {
 | 
						|
				void *oldflags = ele->oflags;
 | 
						|
				ele->oflags = BLI_mempool_alloc(newpool);
 | 
						|
				memcpy(ele->oflags, oldflags, new_totflags_size);
 | 
						|
				BM_elem_index_set(ele, i); /* set_inline */
 | 
						|
				BM_ELEM_API_FLAG_CLEAR((BMElemF *)ele);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	BLI_mempool_destroy(voldpool);
 | 
						|
	BLI_mempool_destroy(eoldpool);
 | 
						|
	BLI_mempool_destroy(foldpool);
 | 
						|
 | 
						|
	bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE);
 | 
						|
}
 | 
						|
 | 
						|
static void bmo_flag_layer_clear(BMesh *bm)
 | 
						|
{
 | 
						|
	/* set the index values since we are looping over all data anyway,
 | 
						|
	 * may save time later on */
 | 
						|
	const BMFlagLayer zero_flag = {0};
 | 
						|
 | 
						|
	const int totflags_offset = bm->totflags - 1;
 | 
						|
 | 
						|
#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
 | 
						|
	{
 | 
						|
		/* now go through and memcpy all the flag */
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_VERTS_OF_MESH, i) {
 | 
						|
				ele->oflags[totflags_offset] = zero_flag;
 | 
						|
				BM_elem_index_set(ele, i); /* set_inline */
 | 
						|
			}
 | 
						|
		}
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_EDGES_OF_MESH, i) {
 | 
						|
				ele->oflags[totflags_offset] = zero_flag;
 | 
						|
				BM_elem_index_set(ele, i); /* set_inline */
 | 
						|
			}
 | 
						|
		}
 | 
						|
#pragma omp section
 | 
						|
		{
 | 
						|
			BMIter iter;
 | 
						|
			BMElemF *ele;
 | 
						|
			int i;
 | 
						|
			BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, i) {
 | 
						|
				ele->oflags[totflags_offset] = zero_flag;
 | 
						|
				BM_elem_index_set(ele, i); /* set_inline */
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE);
 | 
						|
}
 | 
						|
 | 
						|
void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
	
 | 
						|
	if (slot->slot_type != BMO_OP_SLOT_ELEMENT_BUF)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	return slot->data.buf ? *slot->data.buf : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief New Iterator
 | 
						|
 *
 | 
						|
 * \param 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,
 | 
						|
                   BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name,
 | 
						|
                   const char restrictmask)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
 | 
						|
 | 
						|
	memset(iter, 0, sizeof(BMOIter));
 | 
						|
 | 
						|
	iter->slot = slot;
 | 
						|
	iter->cur = 0;
 | 
						|
	iter->restrictmask = restrictmask;
 | 
						|
 | 
						|
	if (iter->slot->slot_type == BMO_OP_SLOT_MAPPING) {
 | 
						|
		BLI_ghashIterator_init(&iter->giter, slot->data.ghash);
 | 
						|
	}
 | 
						|
	else if (iter->slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF) {
 | 
						|
		BLI_assert(restrictmask & slot->slot_subtype.elem);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		BLI_assert(0);
 | 
						|
	}
 | 
						|
 | 
						|
	return BMO_iter_step(iter);
 | 
						|
}
 | 
						|
 | 
						|
void *BMO_iter_step(BMOIter *iter)
 | 
						|
{
 | 
						|
	BMOpSlot *slot = iter->slot;
 | 
						|
	if (slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF) {
 | 
						|
		BMHeader *ele;
 | 
						|
 | 
						|
		if (iter->cur >= slot->len) {
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
 | 
						|
		ele = slot->data.buf[iter->cur++];
 | 
						|
		while (!(iter->restrictmask & ele->htype)) {
 | 
						|
			if (iter->cur >= slot->len) {
 | 
						|
				return NULL;
 | 
						|
			}
 | 
						|
 | 
						|
			ele = slot->data.buf[iter->cur++];
 | 
						|
			BLI_assert((ele == NULL) || (slot->slot_subtype.elem & ele->htype));
 | 
						|
		}
 | 
						|
 | 
						|
		BLI_assert((ele == NULL) || (slot->slot_subtype.elem & ele->htype));
 | 
						|
 | 
						|
		return ele;
 | 
						|
	}
 | 
						|
	else if (slot->slot_type == BMO_OP_SLOT_MAPPING) {
 | 
						|
		void *ret;
 | 
						|
 | 
						|
 | 
						|
		if (BLI_ghashIterator_done(&iter->giter) == false) {
 | 
						|
			ret = BLI_ghashIterator_getKey(&iter->giter);
 | 
						|
			iter->val = BLI_ghashIterator_getValue_p(&iter->giter);
 | 
						|
 | 
						|
			BLI_ghashIterator_step(&iter->giter);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			ret = NULL;
 | 
						|
			iter->val = NULL;
 | 
						|
		}
 | 
						|
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		BLI_assert(0);
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* used for iterating over mappings */
 | 
						|
 | 
						|
/**
 | 
						|
 * 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_p(BMOIter *iter)
 | 
						|
{
 | 
						|
	return iter->val;
 | 
						|
}
 | 
						|
 | 
						|
void *BMO_iter_map_value_ptr(BMOIter *iter)
 | 
						|
{
 | 
						|
	BLI_assert(ELEM(iter->slot->slot_subtype.map,
 | 
						|
	                BMO_OP_SLOT_SUBTYPE_MAP_ELEM, BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL));
 | 
						|
	return iter->val ? *iter->val : NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
float BMO_iter_map_value_float(BMOIter *iter)
 | 
						|
{
 | 
						|
	BLI_assert(iter->slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_FLT);
 | 
						|
	return **((float **)iter->val);
 | 
						|
}
 | 
						|
 | 
						|
int BMO_iter_map_value_int(BMOIter *iter)
 | 
						|
{
 | 
						|
	BLI_assert(iter->slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_INT);
 | 
						|
	return **((int **)iter->val);
 | 
						|
}
 | 
						|
 | 
						|
bool BMO_iter_map_value_bool(BMOIter *iter)
 | 
						|
{
 | 
						|
	BLI_assert(iter->slot->slot_subtype.map == BMO_OP_SLOT_SUBTYPE_MAP_BOOL);
 | 
						|
	return **((bool **)iter->val);
 | 
						|
}
 | 
						|
 | 
						|
/* error system */
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
bool BMO_error_occurred(BMesh *bm)
 | 
						|
{
 | 
						|
	return (BLI_listbase_is_empty(&bm->errorstack) == false);
 | 
						|
}
 | 
						|
 | 
						|
/* returns error code or 0 if no error */
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#define NEXT_CHAR(fmt) ((fmt)[0] != 0 ? (fmt)[1] : 0)
 | 
						|
 | 
						|
static int bmo_name_to_slotcode(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
 | 
						|
{
 | 
						|
	int i = 0;
 | 
						|
 | 
						|
	while (slot_args->slot_name) {
 | 
						|
		if (STREQLEN(identifier, slot_args->slot_name, MAX_SLOTNAME)) {
 | 
						|
			return i;
 | 
						|
		}
 | 
						|
		slot_args++;
 | 
						|
		i++;
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int bmo_name_to_slotcode_check(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
 | 
						|
{
 | 
						|
	int i = bmo_name_to_slotcode(slot_args, identifier);
 | 
						|
	if (i < 0) {
 | 
						|
		fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, identifier);
 | 
						|
	}
 | 
						|
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
int BMO_opcode_from_opname(const char *opname)
 | 
						|
{
 | 
						|
 | 
						|
	const unsigned int tot = bmo_opdefines_total;
 | 
						|
	unsigned int i;
 | 
						|
	for (i = 0; i < tot; i++) {
 | 
						|
		if (STREQ(bmo_opdefines[i]->opname, opname)) {
 | 
						|
			return i;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int BMO_opcode_from_opname_check(const char *opname)
 | 
						|
{
 | 
						|
	int i = BMO_opcode_from_opname(opname);
 | 
						|
	if (i == -1)
 | 
						|
		fprintf(stderr, "%s: could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, opname);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \brief Format Strings for #BMOperator Initialization.
 | 
						|
 *
 | 
						|
 * This system is used to execute or initialize an operator,
 | 
						|
 * using a formatted-string system.
 | 
						|
 *
 | 
						|
 * The basic format for the format string is:
 | 
						|
 * `[operatorname] [slot_name]=%[code] [slot_name]=%[code]`
 | 
						|
 *
 | 
						|
 * Example:
 | 
						|
 *
 | 
						|
 * \code{.c}
 | 
						|
 *     BMO_op_callf(bm, BMO_FLAG_DEFAULTS,
 | 
						|
 *                  "delete context=%i geom=%hv",
 | 
						|
 *                  DEL_ONLYFACES, BM_ELEM_SELECT);
 | 
						|
 * \endcode
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * **Primitive Types**
 | 
						|
 * - `b` - boolean (same as int but 1/0 only). #BMO_OP_SLOT_BOOL
 | 
						|
 * - `i` - int. #BMO_OP_SLOT_INT
 | 
						|
 * - `f` - float. #BMO_OP_SLOT_FLT
 | 
						|
 * - `p` - pointer (normally to a Scene/Mesh/Object/BMesh). #BMO_OP_SLOT_PTR
 | 
						|
 * - `m3` - 3x3 matrix of floats. #BMO_OP_SLOT_MAT
 | 
						|
 * - `m4` - 4x4 matrix of floats. #BMO_OP_SLOT_MAT
 | 
						|
 * - `v` - 3D vector of floats. #BMO_OP_SLOT_VEC
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * **Utility**
 | 
						|
 *
 | 
						|
 * Pass an existing slot which is copied to either an input or output slot.
 | 
						|
 * Taking the operator and slot-name pair of args (BMOperator *, const char *).
 | 
						|
 * - `s` - slot_in (lower case)
 | 
						|
 * - `S` - slot_out (upper case)
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * **Element Buffer** (#BMO_OP_SLOT_ELEMENT_BUF)
 | 
						|
 * - `e` - single element vert/edge/face (use with #BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE).
 | 
						|
 * - `eb` - elem buffer, take an array and a length.
 | 
						|
 * - `av` - all verts
 | 
						|
 * - `ae` - all edges
 | 
						|
 * - `af` - all faces
 | 
						|
 * - `hv` - header flagged verts (hflag)
 | 
						|
 * - `he` - header flagged edges (hflag)
 | 
						|
 * - `hf` - header flagged faces (hflag)
 | 
						|
 * - `Hv` - header flagged verts (hflag off)
 | 
						|
 * - `He` - header flagged edges (hflag off)
 | 
						|
 * - `Hf` - header flagged faces (hflag off)
 | 
						|
 * - `fv` - flagged verts (oflag)
 | 
						|
 * - `fe` - flagged edges (oflag)
 | 
						|
 * - `ff` - flagged faces (oflag)
 | 
						|
 * - `Fv` - flagged verts (oflag off)
 | 
						|
 * - `Fe` - flagged edges (oflag off)
 | 
						|
 * - `Ff` - flagged faces (oflag off)
 | 
						|
 *
 | 
						|
 * \note The common v/e/f suffix can be mixed,
 | 
						|
 * so `avef` is can be used for all verts, edges and faces.
 | 
						|
 * Order is not important so `Hfev` is also valid (all unflagged verts, edges and faces).
 | 
						|
 */
 | 
						|
 | 
						|
bool BMO_op_vinitf(BMesh *bm, BMOperator *op, const int flag, const char *_fmt, va_list vlist)
 | 
						|
{
 | 
						|
//	BMOpDefine *def;
 | 
						|
	char *opname, *ofmt, *fmt;
 | 
						|
	char slot_name[64] = {0};
 | 
						|
	int i, type;
 | 
						|
	bool noslot, state;
 | 
						|
 | 
						|
 | 
						|
	/* basic useful info to help find where bmop formatting strings fail */
 | 
						|
	const char *err_reason = "Unknown";
 | 
						|
	int lineno = -1;
 | 
						|
 | 
						|
#define GOTO_ERROR(reason)   \
 | 
						|
	{                        \
 | 
						|
		err_reason = reason; \
 | 
						|
		lineno = __LINE__;   \
 | 
						|
		goto error;          \
 | 
						|
	} (void)0
 | 
						|
 | 
						|
	/* we muck around in here, so dup it */
 | 
						|
	fmt = ofmt = BLI_strdup(_fmt);
 | 
						|
	
 | 
						|
	/* find operator name */
 | 
						|
	i = strcspn(fmt, " ");
 | 
						|
 | 
						|
	opname = fmt;
 | 
						|
	noslot = (opname[i] == '\0');
 | 
						|
	opname[i] = '\0';
 | 
						|
 | 
						|
	fmt += i + (noslot ? 0 : 1);
 | 
						|
	
 | 
						|
	i = BMO_opcode_from_opname_check(opname);
 | 
						|
 | 
						|
	if (i == -1) {
 | 
						|
		MEM_freeN(ofmt);
 | 
						|
		BLI_assert(0);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	BMO_op_init(bm, op, flag, opname);
 | 
						|
//	def = bmo_opdefines[i];
 | 
						|
	
 | 
						|
	i = 0;
 | 
						|
	state = true;  /* false: not inside slot_code name, true: inside slot_code name */
 | 
						|
 | 
						|
	while (*fmt) {
 | 
						|
		if (state) {
 | 
						|
			/* jump past leading whitespace */
 | 
						|
			i = strspn(fmt, " ");
 | 
						|
			fmt += i;
 | 
						|
			
 | 
						|
			/* ignore trailing whitespace */
 | 
						|
			if (!fmt[i])
 | 
						|
				break;
 | 
						|
 | 
						|
			/* find end of slot name, only "slot=%f", can be used */
 | 
						|
			i = strcspn(fmt, "=");
 | 
						|
			if (!fmt[i]) {
 | 
						|
				GOTO_ERROR("could not match end of slot name");
 | 
						|
			}
 | 
						|
 | 
						|
			fmt[i] = 0;
 | 
						|
 | 
						|
			if (bmo_name_to_slotcode_check(op->slots_in, fmt) < 0) {
 | 
						|
				GOTO_ERROR("name to slot code check failed");
 | 
						|
			}
 | 
						|
			
 | 
						|
			BLI_strncpy(slot_name, fmt, sizeof(slot_name));
 | 
						|
			
 | 
						|
			state = false;
 | 
						|
			fmt += i;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			switch (*fmt) {
 | 
						|
				case ' ':
 | 
						|
				case '=':
 | 
						|
				case '%':
 | 
						|
					break;
 | 
						|
				case 'm':
 | 
						|
				{
 | 
						|
					int size;
 | 
						|
					const char c = NEXT_CHAR(fmt);
 | 
						|
					fmt++;
 | 
						|
 | 
						|
					if      (c == '3') size = 3;
 | 
						|
					else if (c == '4') size = 4;
 | 
						|
					else GOTO_ERROR("matrix size was not 3 or 4");
 | 
						|
 | 
						|
					BMO_slot_mat_set(op, op->slots_in, slot_name, va_arg(vlist, void *), size);
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				case 'v':
 | 
						|
				{
 | 
						|
					BMO_slot_vec_set(op->slots_in, slot_name, va_arg(vlist, float *));
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				case 'e':
 | 
						|
				{
 | 
						|
					BMOpSlot *slot = BMO_slot_get(op->slots_in, slot_name);
 | 
						|
 | 
						|
					if (NEXT_CHAR(fmt) == 'b') {
 | 
						|
						BMHeader **ele_buffer = va_arg(vlist, void *);
 | 
						|
						int ele_buffer_len = va_arg(vlist, int);
 | 
						|
 | 
						|
						BMO_slot_buffer_from_array(op, slot, ele_buffer, ele_buffer_len);
 | 
						|
						fmt++;
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						/* single vert/edge/face */
 | 
						|
						BMHeader *ele = va_arg(vlist, void *);
 | 
						|
 | 
						|
						BMO_slot_buffer_from_single(op, slot, ele);
 | 
						|
					}
 | 
						|
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				case 's':
 | 
						|
				case 'S':
 | 
						|
				{
 | 
						|
					BMOperator *op_other        = va_arg(vlist, void *);
 | 
						|
					const char *slot_name_other = va_arg(vlist, char *);
 | 
						|
 | 
						|
					if (*fmt == 's') {
 | 
						|
						BLI_assert(bmo_name_to_slotcode_check(op_other->slots_in, slot_name_other) != -1);
 | 
						|
						BMO_slot_copy(op_other, slots_in, slot_name_other,
 | 
						|
						              op,       slots_in, slot_name);
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						BLI_assert(bmo_name_to_slotcode_check(op_other->slots_out, slot_name_other) != -1);
 | 
						|
						BMO_slot_copy(op_other, slots_out, slot_name_other,
 | 
						|
						              op,       slots_in, slot_name);
 | 
						|
					}
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				case 'i':
 | 
						|
					BMO_slot_int_set(op->slots_in, slot_name, va_arg(vlist, int));
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				case 'b':
 | 
						|
					BMO_slot_bool_set(op->slots_in, slot_name, va_arg(vlist, int));
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				case 'p':
 | 
						|
					BMO_slot_ptr_set(op->slots_in, slot_name, va_arg(vlist, void *));
 | 
						|
					state = true;
 | 
						|
					break;
 | 
						|
				case 'f':
 | 
						|
				case 'F':
 | 
						|
				case 'h':
 | 
						|
				case 'H':
 | 
						|
				case 'a':
 | 
						|
					type = *fmt;
 | 
						|
 | 
						|
					if (NEXT_CHAR(fmt) == ' ' || NEXT_CHAR(fmt) == '\0') {
 | 
						|
						BMO_slot_float_set(op->slots_in, slot_name, va_arg(vlist, double));
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						char htype = 0;
 | 
						|
 | 
						|
						while (1) {
 | 
						|
							char htype_set;
 | 
						|
							const char c = NEXT_CHAR(fmt);
 | 
						|
							if      (c == 'f') htype_set = BM_FACE;
 | 
						|
							else if (c == 'e') htype_set = BM_EDGE;
 | 
						|
							else if (c == 'v') htype_set = BM_VERT;
 | 
						|
							else {
 | 
						|
								break;
 | 
						|
							}
 | 
						|
 | 
						|
							if (UNLIKELY(htype & htype_set)) {
 | 
						|
								GOTO_ERROR("htype duplicated");
 | 
						|
							}
 | 
						|
 | 
						|
							htype |= htype_set;
 | 
						|
							fmt++;
 | 
						|
						}
 | 
						|
 | 
						|
						if (type == 'h') {
 | 
						|
							BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_in, slot_name, htype, va_arg(vlist, int));
 | 
						|
						}
 | 
						|
						else if (type == 'H') {
 | 
						|
							BMO_slot_buffer_from_disabled_hflag(bm, op, op->slots_in, slot_name, htype, va_arg(vlist, int));
 | 
						|
						}
 | 
						|
						else if (type == 'a') {
 | 
						|
							BMO_slot_buffer_from_all(bm, op, op->slots_in, slot_name, htype);
 | 
						|
						}
 | 
						|
						else if (type == 'f') {
 | 
						|
							BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_in, slot_name, htype, va_arg(vlist, int));
 | 
						|
						}
 | 
						|
						else if (type == 'F') {
 | 
						|
							BMO_slot_buffer_from_disabled_flag(bm, op, op->slots_in, slot_name, htype, va_arg(vlist, int));
 | 
						|
						}
 | 
						|
					}
 | 
						|
 | 
						|
					state = true;
 | 
						|
					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\n", __func__);
 | 
						|
 | 
						|
	fprintf(stderr, "string: '%s', position %d\n", _fmt, (int)(fmt - ofmt));
 | 
						|
	fprintf(stderr, "         ");
 | 
						|
	{
 | 
						|
		int pos = (int)(fmt - ofmt);
 | 
						|
		int i;
 | 
						|
		for (i = 0; i < pos; i++) {
 | 
						|
			fprintf(stderr, " ");
 | 
						|
		}
 | 
						|
		fprintf(stderr, "^\n");
 | 
						|
	}
 | 
						|
 | 
						|
	fprintf(stderr, "source code:  %s:%d\n", __FILE__, lineno);
 | 
						|
 | 
						|
	fprintf(stderr, "reason: %s\n", err_reason);
 | 
						|
 | 
						|
 | 
						|
	MEM_freeN(ofmt);
 | 
						|
 | 
						|
	BMO_op_finish(bm, op);
 | 
						|
	return false;
 | 
						|
 | 
						|
#undef GOTO_ERROR
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool BMO_op_initf(BMesh *bm, BMOperator *op, const int flag, const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list list;
 | 
						|
 | 
						|
	va_start(list, fmt);
 | 
						|
	if (!BMO_op_vinitf(bm, op, flag, fmt, list)) {
 | 
						|
		printf("%s: failed\n", __func__);
 | 
						|
		va_end(list);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	va_end(list);
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
bool BMO_op_callf(BMesh *bm, const int flag, const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list list;
 | 
						|
	BMOperator op;
 | 
						|
 | 
						|
	va_start(list, fmt);
 | 
						|
	if (!BMO_op_vinitf(bm, &op, flag, 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;
 | 
						|
}
 |