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/mask/mask_select.c

796 lines
19 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,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2012 Blender Foundation.
* All rights reserved.
*
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/mask/mask_select.c
* \ingroup edmask
*/
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_rect.h"
#include "BLI_lasso.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_mask.h"
#include "DNA_mask_types.h"
#include "DNA_object_types.h" /* SELECT */
#include "WM_api.h"
#include "WM_types.h"
#include "ED_screen.h"
#include "ED_clip.h"
#include "ED_mask.h" /* own include */
#include "RNA_access.h"
#include "RNA_define.h"
#include "mask_intern.h" /* own include */
/* 'check' select */
int ED_mask_spline_select_check(MaskSpline *spline)
{
int i;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (MASKPOINT_ISSEL_ANY(point))
return TRUE;
}
return FALSE;
}
int ED_mask_layer_select_check(MaskLayer *masklay)
{
MaskSpline *spline;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
return FALSE;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
if (ED_mask_spline_select_check(spline)) {
return TRUE;
}
}
return FALSE;
}
int ED_mask_select_check(Mask *mask)
{
MaskLayer *masklay;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
if (ED_mask_layer_select_check(masklay)) {
return TRUE;
}
}
return FALSE;
}
/* 'sel' select */
void ED_mask_spline_select_set(MaskSpline *spline, const short do_select)
{
int i;
if (do_select)
spline->flag |= SELECT;
else
spline->flag &= ~SELECT;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
BKE_mask_point_select_set(point, do_select);
}
}
void ED_mask_layer_select_set(MaskLayer *masklay, const short do_select)
{
MaskSpline *spline;
if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
if (do_select == TRUE) {
return;
}
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
ED_mask_spline_select_set(spline, do_select);
}
}
void ED_mask_select_toggle_all(Mask *mask, int action)
{
MaskLayer *masklay;
if (action == SEL_TOGGLE) {
if (ED_mask_select_check(mask))
action = SEL_DESELECT;
else
action = SEL_SELECT;
}
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
continue;
}
2012-08-01 13:34:20 +00:00
if (action == SEL_INVERT) {
2012-09-28 06:45:20 +00:00
/* we don't have generic functions for this, its restricted to this operator
2012-08-01 13:34:20 +00:00
* if one day we need to re-use such functionality, they can be split out */
MaskSpline *spline;
if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
BKE_mask_point_select_set(point, !MASKPOINT_ISSEL_ANY(point));
}
}
}
else {
ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? TRUE : FALSE);
}
}
}
void ED_mask_select_flush_all(Mask *mask)
{
MaskLayer *masklay;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
int i;
spline->flag &= ~SELECT;
/* intentionally _dont_ do this in the masklay loop
* so we clear flags on all splines */
if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
continue;
}
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *cur_point = &spline->points[i];
if (MASKPOINT_ISSEL_ANY(cur_point)) {
spline->flag |= SELECT;
}
else {
int j;
for (j = 0; j < cur_point->tot_uw; j++) {
if (cur_point->uw[j].flag & SELECT) {
spline->flag |= SELECT;
break;
}
}
}
}
}
}
}
/******************** toggle selection *********************/
static int select_all_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
int action = RNA_enum_get(op->ptr, "action");
ED_mask_select_toggle_all(mask, action);
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
void MASK_OT_select_all(wmOperatorType *ot)
{
/* identifiers */
2012-06-04 19:43:12 +00:00
ot->name = "(De)select All";
ot->description = "Change selection of all curve points";
ot->idname = "MASK_OT_select_all";
/* api callbacks */
ot->exec = select_all_exec;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_select_all(ot);
}
/******************** select *********************/
static int select_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
MaskSpline *spline;
MaskSplinePoint *point = NULL;
float co[2];
short extend = RNA_boolean_get(op->ptr, "extend");
short deselect = RNA_boolean_get(op->ptr, "deselect");
short toggle = RNA_boolean_get(op->ptr, "toggle");
int is_handle = 0;
const float threshold = 19;
RNA_float_get_array(op->ptr, "location", co);
point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
if (extend == 0 && deselect == 0 && toggle == 0)
ED_mask_select_toggle_all(mask, SEL_DESELECT);
if (point) {
if (is_handle) {
if (extend) {
masklay->act_spline = spline;
masklay->act_point = point;
BKE_mask_point_select_set_handle(point, TRUE);
}
else if (deselect) {
BKE_mask_point_select_set_handle(point, FALSE);
}
else {
masklay->act_spline = spline;
masklay->act_point = point;
if (!MASKPOINT_ISSEL_HANDLE(point)) {
BKE_mask_point_select_set_handle(point, TRUE);
}
else if (toggle) {
BKE_mask_point_select_set_handle(point, FALSE);
}
}
}
else {
if (extend) {
masklay->act_spline = spline;
masklay->act_point = point;
BKE_mask_point_select_set(point, TRUE);
}
else if (deselect) {
BKE_mask_point_select_set(point, FALSE);
}
else {
masklay->act_spline = spline;
masklay->act_point = point;
if (!MASKPOINT_ISSEL_ANY(point)) {
BKE_mask_point_select_set(point, TRUE);
}
else if (toggle) {
BKE_mask_point_select_set(point, FALSE);
}
}
}
masklay->act_spline = spline;
masklay->act_point = point;
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
else {
MaskSplinePointUW *uw;
if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) {
if (extend) {
masklay->act_spline = spline;
masklay->act_point = point;
if (uw) uw->flag |= SELECT;
}
else if (deselect) {
if (uw) uw->flag &= ~SELECT;
}
else {
masklay->act_spline = spline;
masklay->act_point = point;
if (uw) {
if (!(uw->flag & SELECT)) {
uw->flag |= SELECT;
}
else if (toggle) {
uw->flag &= ~SELECT;
}
}
}
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
}
return OPERATOR_PASS_THROUGH;
}
static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(sa, ar, event->mval, co);
RNA_float_set_array(op->ptr, "location", co);
return select_exec(C, op);
}
void MASK_OT_select(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select";
ot->description = "Select spline points";
ot->idname = "MASK_OT_select";
/* api callbacks */
ot->exec = select_exec;
ot->invoke = select_invoke;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_mouse_select(ot);
2012-08-07 19:49:38 +00:00
RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
"Location", "Location of vertex in normalized space", -1.0f, 1.0f);
}
/********************** border select operator *********************/
static int border_select_exec(bContext *C, wmOperator *op)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int i;
rcti rect;
rctf rectf;
int change = FALSE, mode, extend;
/* get rectangle from operator */
WM_operator_properties_border_to_rcti(op, &rect);
ED_mask_point_pos(sa, ar, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
ED_mask_point_pos(sa, ar, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
mode = RNA_int_get(op->ptr, "gesture_mode");
extend = RNA_boolean_get(op->ptr, "extend");
/* do actual selection */
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
/* TODO: uw? */
if (BLI_rctf_isect_pt_v(&rectf, point_deform->bezt.vec[1])) {
BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
}
else if (!extend) {
BKE_mask_point_select_set(point, FALSE);
BKE_mask_point_select_set_handle(point, FALSE);
}
change = TRUE;
}
}
}
if (change) {
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_border(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Border Select";
ot->description = "Select markers using border selection";
ot->idname = "MASK_OT_select_border";
/* api callbacks */
ot->invoke = WM_border_select_invoke;
ot->exec = border_select_exec;
ot->modal = WM_border_select_modal;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_border(ot, TRUE);
}
static int do_lasso_select_mask(bContext *C, const int mcords[][2], short moves, short select)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int i;
rcti rect;
int change = FALSE;
/* get rectangle from operator */
BLI_lasso_boundbox(&rect, mcords, moves);
/* do actual selection */
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
/* TODO: uw? */
float screen_co[2];
/* marker in screen coords */
ED_mask_point_pos__reverse(sa, ar,
point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1],
&screen_co[0], &screen_co[1]);
if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
{
BKE_mask_point_select_set(point, select);
BKE_mask_point_select_set_handle(point, select);
}
change = TRUE;
}
}
}
if (change) {
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
}
return change;
}
static int clip_lasso_select_exec(bContext *C, wmOperator *op)
{
int mcords_tot;
const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
if (mcords) {
short select;
select = !RNA_boolean_get(op->ptr, "deselect");
do_lasso_select_mask(C, mcords, mcords_tot, select);
MEM_freeN((void *)mcords);
return OPERATOR_FINISHED;
}
return OPERATOR_PASS_THROUGH;
}
void MASK_OT_select_lasso(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Lasso Select";
ot->description = "Select markers using lasso selection";
ot->idname = "MASK_OT_select_lasso";
/* api callbacks */
ot->invoke = WM_gesture_lasso_invoke;
ot->modal = WM_gesture_lasso_modal;
ot->exec = clip_lasso_select_exec;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
ot->cancel = WM_gesture_lasso_cancel;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
}
/********************** circle select operator *********************/
static int mask_spline_point_inside_ellipse(BezTriple *bezt, float offset[2], float ellipse[2])
{
/* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
float x, y;
x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
return x * x + y * y < 1.0f;
}
static int circle_select_exec(bContext *C, wmOperator *op)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int i;
int x, y, radius, width, height, mode, change = FALSE;
float zoomx, zoomy, offset[2], ellipse[2];
/* get operator properties */
x = RNA_int_get(op->ptr, "x");
y = RNA_int_get(op->ptr, "y");
radius = RNA_int_get(op->ptr, "radius");
mode = RNA_int_get(op->ptr, "gesture_mode");
/* compute ellipse and position in unified coordinates */
ED_mask_get_size(sa, &width, &height);
ED_mask_zoom(sa, ar, &zoomx, &zoomy);
width = height = max_ii(width, height);
ellipse[0] = width * zoomx / radius;
ellipse[1] = height * zoomy / radius;
ED_mask_point_pos(sa, ar, x, y, &offset[0], &offset[1]);
/* do actual selection */
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
MaskSplinePoint *point_deform = &points_array[i];
if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
change = TRUE;
}
}
}
}
if (change) {
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_circle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Circle Select";
ot->description = "Select markers using circle selection";
ot->idname = "MASK_OT_select_circle";
/* api callbacks */
ot->invoke = WM_gesture_circle_invoke;
ot->modal = WM_gesture_circle_modal;
ot->exec = circle_select_exec;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
}
static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
MaskSpline *spline;
MaskSplinePoint *point = NULL;
float co[2];
int do_select = !RNA_boolean_get(op->ptr, "deselect");
int is_handle = 0;
const float threshold = 19;
int change = FALSE;
ED_mask_mouse_pos(sa, ar, event->mval, co);
point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
if (point) {
ED_mask_spline_select_set(spline, do_select);
masklay->act_spline = spline;
masklay->act_point = point;
change = TRUE;
}
if (change) {
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_linked_pick(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Linked";
ot->idname = "MASK_OT_select_linked_pick";
ot->description = "(De)select all points linked to the curve under the mouse cursor";
/* api callbacks */
ot->invoke = mask_select_linked_pick_invoke;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
}
static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int change = FALSE;
/* do actual selection */
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
if (ED_mask_spline_select_check(spline)) {
ED_mask_spline_select_set(spline, TRUE);
change = TRUE;
}
}
}
if (change) {
ED_mask_select_flush_all(mask);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_linked(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Linked All";
ot->idname = "MASK_OT_select_linked";
ot->description = "Select all vertices linked to the active mesh";
/* api callbacks */
ot->exec = mask_select_linked_exec;
2012-06-04 17:30:54 +00:00
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}