This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/mask/mask_select.c
Sergey Sharybin 66f8835f9c Fix T84850: "Lock to Selection" causes unwanted jumps
Adding new tracks, mask points, mask primitives, changing selection was
causing an unwanted jumps in the view.

This change makes it so those operations are preserving view offset.

Differential Revision: https://developer.blender.org/D10146
2021-01-26 16:23:09 +01:00

983 lines
25 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2012 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup edmask
*/
#include "MEM_guardedalloc.h"
#include "BLI_lasso_2d.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_mask.h"
#include "DNA_mask_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_clip.h"
#include "ED_mask.h" /* own include */
#include "ED_select_utils.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "DEG_depsgraph.h"
#include "mask_intern.h" /* own include */
/* -------------------------------------------------------------------- */
/** \name Public Mask Selection API
* \{ */
/* 'check' select */
bool ED_mask_spline_select_check(const MaskSpline *spline)
{
for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (MASKPOINT_ISSEL_ANY(point)) {
return true;
}
}
return false;
}
bool ED_mask_layer_select_check(const MaskLayer *mask_layer)
{
if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
return false;
}
LISTBASE_FOREACH (const MaskSpline *, spline, &mask_layer->splines) {
if (ED_mask_spline_select_check(spline)) {
return true;
}
}
return false;
}
bool ED_mask_select_check(const Mask *mask)
{
LISTBASE_FOREACH (const MaskLayer *, mask_layer, &mask->masklayers) {
if (ED_mask_layer_select_check(mask_layer)) {
return true;
}
}
return false;
}
/* 'sel' select */
void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select)
{
if (do_select) {
spline->flag |= SELECT;
}
else {
spline->flag &= ~SELECT;
}
for (int 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 *mask_layer, const bool do_select)
{
if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
if (do_select == true) {
return;
}
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
ED_mask_spline_select_set(spline, do_select);
}
}
void ED_mask_select_toggle_all(Mask *mask, int action)
{
if (action == SEL_TOGGLE) {
if (ED_mask_select_check(mask)) {
action = SEL_DESELECT;
}
else {
action = SEL_SELECT;
}
}
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) {
continue;
}
if (action == SEL_INVERT) {
/* we don't have generic functions for this, its restricted to this operator
* if one day we need to re-use such functionality, they can be split out */
if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
for (int 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(mask_layer, (action == SEL_SELECT) ? true : false);
}
}
}
void ED_mask_select_flush_all(Mask *mask)
{
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
spline->flag &= ~SELECT;
/* intentionally _dont_ do this in the mask layer loop
* so we clear flags on all splines */
if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) {
continue;
}
for (int 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;
}
}
}
}
}
}
}
void ED_mask_deselect_all(const bContext *C)
{
Mask *mask = CTX_data_edit_mask(C);
if (mask) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name (De)select All Operator
* \{ */
static int select_all_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
int action = RNA_enum_get(op->ptr, "action");
MaskViewLockState lock_state;
ED_mask_view_lock_state_store(C, &lock_state);
ED_mask_select_toggle_all(mask, action);
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
void MASK_OT_select_all(wmOperatorType *ot)
{
/* identifiers */
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;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_select_all(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select (Cursor Pick) Operator
* \{ */
static int select_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = NULL;
float co[2];
bool extend = RNA_boolean_get(op->ptr, "extend");
bool deselect = RNA_boolean_get(op->ptr, "deselect");
bool toggle = RNA_boolean_get(op->ptr, "toggle");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
eMaskWhichHandle which_handle;
const float threshold = 19;
MaskViewLockState lock_state;
ED_mask_view_lock_state_store(C, &lock_state);
RNA_float_get_array(op->ptr, "location", co);
point = ED_mask_point_find_nearest(
C, mask, co, threshold, &mask_layer, &spline, &which_handle, NULL);
if (extend == false && deselect == false && toggle == false) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
}
if (point) {
if (which_handle != MASK_WHICH_HANDLE_NONE) {
if (extend) {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
BKE_mask_point_select_set_handle(point, which_handle, true);
}
else if (deselect) {
BKE_mask_point_select_set_handle(point, which_handle, false);
}
else {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (!MASKPOINT_ISSEL_HANDLE(point, which_handle)) {
BKE_mask_point_select_set_handle(point, which_handle, true);
}
else if (toggle) {
BKE_mask_point_select_set_handle(point, which_handle, false);
}
}
}
else {
if (extend) {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
BKE_mask_point_select_set(point, true);
}
else if (deselect) {
BKE_mask_point_select_set(point, false);
}
else {
mask_layer->act_spline = spline;
mask_layer->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);
}
}
}
mask_layer->act_spline = spline;
mask_layer->act_point = point;
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
MaskSplinePointUW *uw;
if (ED_mask_feather_find_nearest(
C, mask, co, threshold, &mask_layer, &spline, &point, &uw, NULL)) {
if (extend) {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (uw) {
uw->flag |= SELECT;
}
}
else if (deselect) {
if (uw) {
uw->flag &= ~SELECT;
}
}
else {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (uw) {
if (!(uw->flag & SELECT)) {
uw->flag |= SELECT;
}
else if (toggle) {
uw->flag &= ~SELECT;
}
}
}
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
if (deselect_all) {
/* For clip editor tracks, leave deselect all to clip editor. */
if (!ED_clip_can_select(C)) {
ED_mask_deselect_all(C);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
}
return OPERATOR_PASS_THROUGH;
}
static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(area, region, 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;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_mouse_select(ot);
RNA_def_float_vector(ot->srna,
"location",
2,
NULL,
-FLT_MAX,
FLT_MAX,
"Location",
"Location of vertex in normalized space",
-1.0f,
1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Box Select Operator
* \{ */
static int box_select_exec(bContext *C, wmOperator *op)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
rcti rect;
rctf rectf;
bool changed = false;
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
changed = true;
}
/* get rectangle from operator */
WM_operator_properties_border_to_rcti(op, &rect);
ED_mask_point_pos(area, region, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
ED_mask_point_pos(area, region, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
for (int 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, select);
BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
changed = true;
}
}
}
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_box(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Box Select";
ot->description = "Select curve points using box selection";
ot->idname = "MASK_OT_select_box";
/* api callbacks */
ot->invoke = WM_gesture_box_invoke;
ot->exec = box_select_exec;
ot->modal = WM_gesture_box_modal;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_box(ot);
WM_operator_properties_select_operation_simple(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lasso Select Operator
* \{ */
static bool do_lasso_select_mask(bContext *C,
const int mcoords[][2],
const int mcoords_len,
const eSelectOp sel_op)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
rcti rect;
bool changed = false;
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
changed = true;
}
/* get rectangle from operator */
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
/* TODO: uw? */
if (MASKPOINT_ISSEL_ANY(point) && select) {
continue;
}
float screen_co[2];
/* point in screen coords */
ED_mask_point_pos__reverse(area,
region,
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(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) {
BKE_mask_point_select_set(point, select);
BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
changed = true;
}
}
}
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
}
return changed;
}
static int clip_lasso_select_exec(bContext *C, wmOperator *op)
{
int mcoords_len;
const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
if (mcoords) {
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
do_lasso_select_mask(C, mcoords, mcoords_len, sel_op);
MEM_freeN((void *)mcoords);
return OPERATOR_FINISHED;
}
return OPERATOR_PASS_THROUGH;
}
void MASK_OT_select_lasso(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Lasso Select";
ot->description = "Select curve points 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;
ot->poll = ED_maskedit_mask_poll;
ot->cancel = WM_gesture_lasso_cancel;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_lasso(ot);
WM_operator_properties_select_operation_simple(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Circle Select Operator
* \{ */
static int mask_spline_point_inside_ellipse(BezTriple *bezt,
const float offset[2],
const 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 *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
float zoomx, zoomy, offset[2], ellipse[2];
int width, height;
bool changed = false;
/* get operator properties */
const int x = RNA_int_get(op->ptr, "x");
const int y = RNA_int_get(op->ptr, "y");
const int radius = RNA_int_get(op->ptr, "radius");
/* compute ellipse and position in unified coordinates */
ED_mask_get_size(area, &width, &height);
ED_mask_zoom(area, region, &zoomx, &zoomy);
width = height = max_ii(width, height);
ellipse[0] = width * zoomx / radius;
ellipse[1] = height * zoomy / radius;
ED_mask_point_pos(area, region, x, y, &offset[0], &offset[1]);
const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
WM_gesture_is_modal_first(op->customdata));
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
changed = true;
}
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
for (int 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, select);
BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
changed = true;
}
}
}
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
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 curve points 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;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_circle(ot);
WM_operator_properties_select_operation_simple(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Linked (Cursor Pick) Operator
* \{ */
static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = NULL;
float co[2];
bool do_select = !RNA_boolean_get(op->ptr, "deselect");
const float threshold = 19;
bool changed = false;
ED_mask_mouse_pos(area, region, event->mval, co);
point = ED_mask_point_find_nearest(C, mask, co, threshold, &mask_layer, &spline, NULL, NULL);
if (point) {
ED_mask_spline_select_set(spline, do_select);
mask_layer->act_spline = spline;
mask_layer->act_point = point;
changed = true;
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
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;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Linked Operator
* \{ */
static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
{
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
if (ED_mask_spline_select_check(spline)) {
ED_mask_spline_select_set(spline, true);
changed = true;
}
}
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
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 curve points linked to already selected ones";
/* api callbacks */
ot->exec = mask_select_linked_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select More/Less Operators
* \{ */
static int mask_select_more_less(bContext *C, bool more)
{
Mask *mask = CTX_data_edit_mask(C);
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
const bool cyclic = (spline->flag & MASK_SPLINE_CYCLIC) != 0;
bool start_sel, end_sel, prev_sel, cur_sel;
/* reselect point if any handle is selected to make the result more predictable */
for (int i = 0; i < spline->tot_point; i++) {
BKE_mask_point_select_set(spline->points + i, MASKPOINT_ISSEL_ANY(spline->points + i));
}
/* select more/less does not affect empty/single point splines */
if (spline->tot_point < 2) {
continue;
}
if (cyclic) {
start_sel = !!MASKPOINT_ISSEL_KNOT(spline->points);
end_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[spline->tot_point - 1]);
}
else {
start_sel = false;
end_sel = false;
}
for (int i = 0; i < spline->tot_point; i++) {
if (i == 0 && !cyclic) {
continue;
}
prev_sel = (i > 0) ? !!MASKPOINT_ISSEL_KNOT(&spline->points[i - 1]) : end_sel;
cur_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[i]);
if (cur_sel != more) {
if (prev_sel == more) {
BKE_mask_point_select_set(&spline->points[i], more);
}
i++;
}
}
for (int i = spline->tot_point - 1; i >= 0; i--) {
if (i == spline->tot_point - 1 && !cyclic) {
continue;
}
prev_sel = (i < spline->tot_point - 1) ? !!MASKPOINT_ISSEL_KNOT(&spline->points[i + 1]) :
start_sel;
cur_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[i]);
if (cur_sel != more) {
if (prev_sel == more) {
BKE_mask_point_select_set(&spline->points[i], more);
}
i--;
}
}
}
}
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
static int mask_select_more_exec(bContext *C, wmOperator *UNUSED(op))
{
return mask_select_more_less(C, true);
}
void MASK_OT_select_more(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select More";
ot->idname = "MASK_OT_select_more";
ot->description = "Select more spline points connected to initial selection";
/* api callbacks */
ot->exec = mask_select_more_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int mask_select_less_exec(bContext *C, wmOperator *UNUSED(op))
{
return mask_select_more_less(C, false);
}
void MASK_OT_select_less(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Less";
ot->idname = "MASK_OT_select_less";
ot->description = "Deselect spline points at the boundary of each selection region";
/* api callbacks */
ot->exec = mask_select_less_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */