This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/metaball/mball_edit.c

687 lines
16 KiB
C
Raw Normal View History

/*
* ***** 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,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
2011-02-27 20:29:51 +00:00
/** \file blender/editors/metaball/mball_edit.c
* \ingroup edmeta
*/
#include <math.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_rand.h"
#include "BLI_utildefines.h"
#include "DNA_defs.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "RNA_define.h"
#include "RNA_access.h"
#include "BKE_context.h"
2010-09-07 05:47:34 +00:00
#include "BKE_mball.h"
#include "BKE_layer.h"
#include "DEG_depsgraph.h"
#include "ED_mball.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "WM_api.h"
#include "WM_types.h"
#include "mball_intern.h"
/* This function is used to free all MetaElems from MetaBall */
void ED_mball_editmball_free(Object *obedit)
{
2012-05-08 18:29:02 +00:00
MetaBall *mb = (MetaBall *)obedit->data;
2012-05-08 18:29:02 +00:00
mb->editelems = NULL;
mb->lastelem = NULL;
}
/* This function is called, when MetaBall Object is
* switched from object mode to edit mode */
void ED_mball_editmball_make(Object *obedit)
{
2012-05-08 18:29:02 +00:00
MetaBall *mb = (MetaBall *)obedit->data;
MetaElem *ml; /*, *newml;*/
2012-05-08 18:29:02 +00:00
ml = mb->elems.first;
while (ml) {
if (ml->flag & SELECT) mb->lastelem = ml;
2012-05-08 18:29:02 +00:00
ml = ml->next;
}
mb->editelems = &mb->elems;
}
/* This function is called, when MetaBall Object switched from
2014-01-17 17:35:03 +11:00
* edit mode to object mode. List of MetaElements is copied
2010-04-25 15:24:18 +00:00
* from object->data->edit_elems to object->data->elems. */
void ED_mball_editmball_load(Object *UNUSED(obedit))
{
}
/* Add metaelem primitive to metaball object (which is in edit mode) */
MetaElem *ED_mball_add_primitive(bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type)
{
2012-05-08 18:29:02 +00:00
MetaBall *mball = (MetaBall *)obedit->data;
MetaElem *ml;
/* Deselect all existing metaelems */
2012-05-08 18:29:02 +00:00
ml = mball->editelems->first;
while (ml) {
ml->flag &= ~SELECT;
2012-05-08 18:29:02 +00:00
ml = ml->next;
}
2012-05-08 18:29:02 +00:00
ml = BKE_mball_element_add(mball, type);
ml->rad *= dia;
mball->wiresize *= dia;
mball->rendersize *= dia;
2010-09-07 05:47:34 +00:00
copy_v3_v3(&ml->x, mat[3]);
ml->flag |= SELECT;
2012-05-08 18:29:02 +00:00
mball->lastelem = ml;
return ml;
}
/***************************** Select/Deselect operator *****************************/
/* Select or deselect all MetaElements */
static int mball_select_all_exec(bContext *C, wmOperator *op)
{
2012-05-08 18:29:02 +00:00
Object *obedit = CTX_data_edit_object(C);
MetaBall *mb = (MetaBall *)obedit->data;
MetaElem *ml;
int action = RNA_enum_get(op->ptr, "action");
if (BLI_listbase_is_empty(mb->editelems))
return OPERATOR_CANCELLED;
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (ml = mb->editelems->first; ml; ml = ml->next) {
if (ml->flag & SELECT) {
action = SEL_DESELECT;
break;
}
}
}
switch (action) {
case SEL_SELECT:
BKE_mball_select_all(mb);
break;
case SEL_DESELECT:
BKE_mball_deselect_all(mb);
break;
case SEL_INVERT:
BKE_mball_select_swap(mb);
break;
}
DEG_id_tag_update(&mb->id, DEG_TAG_SELECT_UPDATE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
return OPERATOR_FINISHED;
}
void MBALL_OT_select_all(wmOperatorType *ot)
{
/* identifiers */
ot->name = "(De)select All";
ot->description = "Change selection of all meta elements";
ot->idname = "MBALL_OT_select_all";
/* callback functions */
ot->exec = mball_select_all_exec;
ot->poll = ED_operator_editmball;
/* flags */
2012-05-08 18:29:02 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
WM_operator_properties_select_all(ot);
}
/* -------------------------------------------------------------------- */
/* Select Similar */
enum {
SIMMBALL_TYPE = 1,
SIMMBALL_RADIUS,
SIMMBALL_STIFFNESS,
SIMMBALL_ROTATION
};
static const EnumPropertyItem prop_similar_types[] = {
{SIMMBALL_TYPE, "TYPE", 0, "Type", ""},
{SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""},
2014-05-14 15:00:47 +10:00
{SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""},
{SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""},
{0, NULL, 0, NULL, NULL}
};
static bool mball_select_similar_type(MetaBall *mb)
{
MetaElem *ml;
bool changed = false;
for (ml = mb->editelems->first; ml; ml = ml->next) {
if (ml->flag & SELECT) {
MetaElem *ml_iter;
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
if ((ml_iter->flag & SELECT) == 0) {
if (ml->type == ml_iter->type) {
ml_iter->flag |= SELECT;
changed = true;
}
}
}
}
}
return changed;
}
static bool mball_select_similar_radius(MetaBall *mb, const float thresh)
{
MetaElem *ml;
bool changed = false;
for (ml = mb->editelems->first; ml; ml = ml->next) {
if (ml->flag & SELECT) {
MetaElem *ml_iter;
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
if ((ml_iter->flag & SELECT) == 0) {
if (fabsf(ml_iter->rad - ml->rad) <= (thresh * ml->rad)) {
ml_iter->flag |= SELECT;
changed = true;
}
}
}
}
}
return changed;
}
static bool mball_select_similar_stiffness(MetaBall *mb, const float thresh)
{
MetaElem *ml;
bool changed = false;
for (ml = mb->editelems->first; ml; ml = ml->next) {
if (ml->flag & SELECT) {
MetaElem *ml_iter;
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
if ((ml_iter->flag & SELECT) == 0) {
if (fabsf(ml_iter->s - ml->s) <= thresh) {
ml_iter->flag |= SELECT;
changed = true;
}
}
}
}
}
return changed;
}
static bool mball_select_similar_rotation(MetaBall *mb, const float thresh)
{
const float thresh_rad = thresh * (float)M_PI_2;
MetaElem *ml;
bool changed = false;
for (ml = mb->editelems->first; ml; ml = ml->next) {
if (ml->flag & SELECT) {
MetaElem *ml_iter;
float ml_mat[3][3];
unit_m3(ml_mat);
mul_qt_v3(ml->quat, ml_mat[0]);
mul_qt_v3(ml->quat, ml_mat[1]);
mul_qt_v3(ml->quat, ml_mat[2]);
normalize_m3(ml_mat);
for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
if ((ml_iter->flag & SELECT) == 0) {
float ml_iter_mat[3][3];
unit_m3(ml_iter_mat);
mul_qt_v3(ml_iter->quat, ml_iter_mat[0]);
mul_qt_v3(ml_iter->quat, ml_iter_mat[1]);
mul_qt_v3(ml_iter->quat, ml_iter_mat[2]);
normalize_m3(ml_iter_mat);
if ((angle_normalized_v3v3(ml_mat[0], ml_iter_mat[0]) +
angle_normalized_v3v3(ml_mat[1], ml_iter_mat[1]) +
angle_normalized_v3v3(ml_mat[2], ml_iter_mat[2])) < thresh_rad)
{
ml_iter->flag |= SELECT;
changed = true;
}
}
}
}
}
return changed;
}
static int mball_select_similar_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
MetaBall *mb = (MetaBall *)obedit->data;
int type = RNA_enum_get(op->ptr, "type");
float thresh = RNA_float_get(op->ptr, "threshold");
bool changed = false;
switch (type) {
case SIMMBALL_TYPE:
changed = mball_select_similar_type(mb);
break;
case SIMMBALL_RADIUS:
changed = mball_select_similar_radius(mb, thresh);
break;
case SIMMBALL_STIFFNESS:
changed = mball_select_similar_stiffness(mb, thresh);
break;
case SIMMBALL_ROTATION:
changed = mball_select_similar_rotation(mb, thresh);
break;
default:
BLI_assert(0);
2013-10-29 03:04:46 +00:00
break;
}
if (changed) {
DEG_id_tag_update(&mb->id, DEG_TAG_SELECT_UPDATE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
}
return OPERATOR_FINISHED;
}
void MBALL_OT_select_similar(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Similar";
ot->idname = "MBALL_OT_select_similar";
/* callback functions */
ot->invoke = WM_menu_invoke;
ot->exec = mball_select_similar_exec;
ot->poll = ED_operator_editmball;
ot->description = "Select similar metaballs by property types";
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
RNA_def_float(ot->srna, "threshold", 0.1, 0.0, 1.0, "Threshold", "", 0.01, 1.0);
}
/***************************** Select random operator *****************************/
/* Random metaball selection */
static int select_random_metaelems_exec(bContext *C, wmOperator *op)
{
const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
MetaBall *mb = (MetaBall *)obedit->data;
if (!BKE_mball_is_any_unselected(mb)) {
continue;
}
int seed_iter = seed;
/* This gives a consistent result regardless of object order. */
if (ob_index) {
seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
}
RNG *rng = BLI_rng_new_srandom(seed_iter);
for (MetaElem *ml = mb->editelems->first; ml; ml = ml->next) {
if (BLI_rng_get_float(rng) < randfac) {
if (select) {
ml->flag |= SELECT;
}
else {
ml->flag &= ~SELECT;
}
}
}
BLI_rng_free(rng);
DEG_id_tag_update(&mb->id, DEG_TAG_SELECT_UPDATE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
}
MEM_freeN(objects);
return OPERATOR_FINISHED;
}
void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Random";
ot->description = "Randomly select metaelements";
ot->idname = "MBALL_OT_select_random_metaelems";
/* callback functions */
ot->exec = select_random_metaelems_exec;
ot->poll = ED_operator_editmball;
/* flags */
2012-05-08 18:29:02 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_select_random(ot);
}
/***************************** Duplicate operator *****************************/
/* Duplicate selected MetaElements */
static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-05-08 18:29:02 +00:00
Object *obedit = CTX_data_edit_object(C);
MetaBall *mb = (MetaBall *)obedit->data;
MetaElem *ml, *newml;
2012-05-08 18:29:02 +00:00
ml = mb->editelems->last;
if (ml) {
while (ml) {
if (ml->flag & SELECT) {
2012-05-08 18:29:02 +00:00
newml = MEM_dupallocN(ml);
BLI_addtail(mb->editelems, newml);
2012-05-08 18:29:02 +00:00
mb->lastelem = newml;
ml->flag &= ~SELECT;
}
2012-05-08 18:29:02 +00:00
ml = ml->prev;
}
2012-05-08 18:29:02 +00:00
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
DEG_id_tag_update(obedit->data, 0);
}
return OPERATOR_FINISHED;
}
void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Metaelements";
ot->description = "Duplicate selected metaelement(s)";
ot->idname = "MBALL_OT_duplicate_metaelems";
/* callback functions */
ot->exec = duplicate_metaelems_exec;
ot->poll = ED_operator_editmball;
/* flags */
2012-05-08 18:29:02 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/***************************** Delete operator *****************************/
/* Delete all selected MetaElems (not MetaBall) */
static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-05-08 18:29:02 +00:00
Object *obedit = CTX_data_edit_object(C);
MetaBall *mb = (MetaBall *)obedit->data;
MetaElem *ml, *next;
2012-05-08 18:29:02 +00:00
ml = mb->editelems->first;
if (ml) {
while (ml) {
2012-05-08 18:29:02 +00:00
next = ml->next;
if (ml->flag & SELECT) {
2012-05-08 18:29:02 +00:00
if (mb->lastelem == ml) mb->lastelem = NULL;
BLI_remlink(mb->editelems, ml);
MEM_freeN(ml);
}
2012-05-08 18:29:02 +00:00
ml = next;
}
2012-05-08 18:29:02 +00:00
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
DEG_id_tag_update(obedit->data, 0);
}
return OPERATOR_FINISHED;
}
void MBALL_OT_delete_metaelems(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete";
ot->description = "Delete selected metaelement(s)";
ot->idname = "MBALL_OT_delete_metaelems";
/* callback functions */
ot->exec = delete_metaelems_exec;
ot->poll = ED_operator_editmball;
/* flags */
2012-05-08 18:29:02 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/***************************** Hide operator *****************************/
/* Hide selected MetaElems */
static int hide_metaelems_exec(bContext *C, wmOperator *op)
{
2012-05-08 18:29:02 +00:00
Object *obedit = CTX_data_edit_object(C);
MetaBall *mb = (MetaBall *)obedit->data;
MetaElem *ml;
2014-04-11 11:25:41 +10:00
const bool invert = RNA_boolean_get(op->ptr, "unselected") ? SELECT : 0;
2012-05-08 18:29:02 +00:00
ml = mb->editelems->first;
if (ml) {
while (ml) {
if ((ml->flag & SELECT) != invert)
ml->flag |= MB_HIDE;
2012-05-08 18:29:02 +00:00
ml = ml->next;
}
2012-05-08 18:29:02 +00:00
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
DEG_id_tag_update(obedit->data, 0);
}
return OPERATOR_FINISHED;
}
void MBALL_OT_hide_metaelems(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hide";
ot->description = "Hide (un)selected metaelement(s)";
ot->idname = "MBALL_OT_hide_metaelems";
/* callback functions */
ot->exec = hide_metaelems_exec;
ot->poll = ED_operator_editmball;
/* flags */
2012-05-08 18:29:02 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
}
/***************************** Unhide operator *****************************/
/* Unhide all edited MetaElems */
static int reveal_metaelems_exec(bContext *C, wmOperator *op)
{
2012-05-08 18:29:02 +00:00
Object *obedit = CTX_data_edit_object(C);
MetaBall *mb = (MetaBall *)obedit->data;
const bool select = RNA_boolean_get(op->ptr, "select");
bool changed = false;
for (MetaElem *ml = mb->editelems->first; ml; ml = ml->next) {
if (ml->flag & MB_HIDE) {
SET_FLAG_FROM_TEST(ml->flag, select, SELECT);
ml->flag &= ~MB_HIDE;
changed = true;
}
}
if (changed) {
2012-05-08 18:29:02 +00:00
WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
DEG_id_tag_update(obedit->data, 0);
}
return OPERATOR_FINISHED;
}
void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reveal";
ot->description = "Reveal all hidden metaelements";
ot->idname = "MBALL_OT_reveal_metaelems";
/* callback functions */
ot->exec = reveal_metaelems_exec;
ot->poll = ED_operator_editmball;
/* flags */
2012-05-08 18:29:02 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
RNA_def_boolean(ot->srna, "select", true, "Select", "");
}
/* Select MetaElement with mouse click (user can select radius circle or
* stiffness circle) */
bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
{
2012-05-08 18:29:02 +00:00
static MetaElem *startelem = NULL;
Object *obedit = CTX_data_edit_object(C);
ViewContext vc;
2012-05-08 18:29:02 +00:00
MetaBall *mb = (MetaBall *)obedit->data;
MetaElem *ml, *ml_act = NULL;
int a, hits;
unsigned int buffer[MAXPICKBUF];
rcti rect;
ED_view3d_viewcontext_init(C, &vc);
2017-03-08 23:10:31 +11:00
BLI_rcti_init_pt_radius(&rect, mval, 12);
hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST);
/* does startelem exist? */
2012-05-08 18:29:02 +00:00
ml = mb->editelems->first;
while (ml) {
2012-05-08 18:29:02 +00:00
if (ml == startelem) break;
ml = ml->next;
}
2012-05-08 18:29:02 +00:00
if (ml == NULL) startelem = mb->editelems->first;
2012-05-08 18:29:02 +00:00
if (hits > 0) {
ml = startelem;
while (ml) {
2012-05-08 18:29:02 +00:00
for (a = 0; a < hits; a++) {
/* index converted for gl stuff */
2012-05-08 18:29:02 +00:00
if (ml->selcol1 == buffer[4 * a + 3]) {
ml->flag |= MB_SCALE_RAD;
ml_act = ml;
}
2012-05-08 18:29:02 +00:00
if (ml->selcol2 == buffer[4 * a + 3]) {
ml->flag &= ~MB_SCALE_RAD;
ml_act = ml;
}
}
if (ml_act) break;
2012-05-08 18:29:02 +00:00
ml = ml->next;
if (ml == NULL) ml = mb->editelems->first;
if (ml == startelem) break;
}
/* When some metaelem was found, then it is necessary to select or
* deselect it. */
if (ml_act) {
if (extend) {
ml_act->flag |= SELECT;
}
else if (deselect) {
ml_act->flag &= ~SELECT;
}
else if (toggle) {
if (ml_act->flag & SELECT)
ml_act->flag &= ~SELECT;
else
ml_act->flag |= SELECT;
}
else {
/* Deselect all existing metaelems */
BKE_mball_deselect_all(mb);
/* Select only metaelem clicked on */
ml_act->flag |= SELECT;
}
mb->lastelem = ml_act;
DEG_id_tag_update(&mb->id, DEG_TAG_SELECT_UPDATE);
2012-05-08 18:29:02 +00:00
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
return true;
}
}
return false;
}