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_ops.c
Sergey Sharybin 03dbae07d3 Mask primitives
Currently only circle and square, might be easily
extended in the future.

New primitives are creating at cursor location.
This also implied adding 2d cursor to space clip.

Also fix set 2D cursor location which didn't work
in image editor's mask mode since 2.67.

TODO: draw_image_cursor better be moved to some
      more generic file, but it's not so much
      important for now and might be solved later.

Thanks Campbell for the review!
2013-08-26 20:23:26 +00:00

1597 lines
38 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.
*
* 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_ops.c
* \ingroup edmask
*/
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_main.h"
#include "BKE_mask.h"
#include "DNA_scene_types.h"
#include "DNA_mask_types.h"
#include "DNA_object_types.h" /* SELECT */
#include "WM_api.h"
#include "WM_types.h"
#include "ED_clip.h"
#include "ED_image.h"
#include "ED_keyframing.h"
#include "ED_mask.h"
#include "ED_screen.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "mask_intern.h" /* own include */
/******************** utility functions *********************/
MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask, float normal_co[2], int threshold,
MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
float *score)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
MaskLayer *masklay;
MaskLayer *point_masklay = NULL;
MaskSpline *point_spline = NULL;
MaskSplinePoint *point = NULL;
float co[2];
float len = FLT_MAX, scalex, scaley;
int is_handle = FALSE, width, height;
ED_mask_get_size(sa, &width, &height);
ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
co[0] = normal_co[0] * scalex;
co[1] = normal_co[1] * scaley;
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);
int i;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *cur_point = &spline->points[i];
MaskSplinePoint *cur_point_deform = &points_array[i];
float cur_len, vec[2], handle[2];
vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
if (BKE_mask_point_has_handle(cur_point)) {
BKE_mask_point_handle(cur_point_deform, handle);
handle[0] *= scalex;
handle[1] *= scaley;
cur_len = len_v2v2(co, handle);
if (cur_len < len) {
point_masklay = masklay;
point_spline = spline;
point = cur_point;
len = cur_len;
is_handle = TRUE;
}
}
cur_len = len_v2v2(co, vec);
if (cur_len < len) {
point_spline = spline;
point_masklay = masklay;
point = cur_point;
len = cur_len;
is_handle = FALSE;
}
}
}
}
if (len < threshold) {
if (masklay_r)
*masklay_r = point_masklay;
if (spline_r)
*spline_r = point_spline;
if (is_handle_r)
*is_handle_r = is_handle;
if (score)
*score = len;
return point;
}
if (masklay_r)
*masklay_r = NULL;
if (spline_r)
*spline_r = NULL;
if (is_handle_r)
*is_handle_r = FALSE;
return NULL;
}
int ED_mask_feather_find_nearest(const bContext *C, Mask *mask, float normal_co[2], int threshold,
MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
MaskSplinePointUW **uw_r, float *score)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
MaskLayer *masklay, *point_masklay = NULL;
MaskSpline *point_spline = NULL;
MaskSplinePoint *point = NULL;
MaskSplinePointUW *uw = NULL;
float len = FLT_MAX, co[2];
float scalex, scaley;
int width, height;
ED_mask_get_size(sa, &width, &height);
ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
co[0] = normal_co[0] * scalex;
co[1] = normal_co[1] * scaley;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
for (spline = masklay->splines.first; spline; spline = spline->next) {
//MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
int i, tot_feather_point;
float (*feather_points)[2], (*fp)[2];
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
for (i = 0; i < spline->tot_point; i++) {
int j;
MaskSplinePoint *cur_point = &spline->points[i];
for (j = 0; j <= cur_point->tot_uw; j++) {
float cur_len, vec[2];
vec[0] = (*fp)[0] * scalex;
vec[1] = (*fp)[1] * scaley;
cur_len = len_v2v2(vec, co);
if (point == NULL || cur_len < len) {
if (j == 0)
uw = NULL;
else
uw = &cur_point->uw[j - 1];
point_masklay = masklay;
point_spline = spline;
point = cur_point;
len = cur_len;
}
fp++;
}
}
MEM_freeN(feather_points);
}
}
if (len < threshold) {
if (masklay_r)
*masklay_r = point_masklay;
if (spline_r)
*spline_r = point_spline;
if (point_r)
*point_r = point;
if (uw_r)
*uw_r = uw;
if (score)
*score = len;
return TRUE;
}
if (masklay_r)
*masklay_r = NULL;
if (spline_r)
*spline_r = NULL;
if (point_r)
*point_r = NULL;
return FALSE;
}
/******************** create new mask *********************/
Mask *ED_mask_new(bContext *C, const char *name)
{
ScrArea *sa = CTX_wm_area(C);
Main *bmain = CTX_data_main(C);
Mask *mask;
mask = BKE_mask_new(bmain, name);
if (sa && sa->spacedata.first) {
switch (sa->spacetype) {
case SPACE_CLIP:
{
SpaceClip *sc = sa->spacedata.first;
ED_space_clip_set_mask(C, sc, mask);
break;
}
case SPACE_SEQ:
{
/* do nothing */
break;
}
case SPACE_IMAGE:
{
SpaceImage *sima = sa->spacedata.first;
ED_space_image_set_mask(C, sima, mask);
break;
}
}
}
return mask;
}
/* Get ative layer. Will create mask/layer to be sure there's an active layer. */
MaskLayer *ED_mask_layer_ensure(bContext *C)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer;
if (mask == NULL) {
/* If there's no active mask, create one. */
mask = ED_mask_new(C, NULL);
}
mask_layer = BKE_mask_layer_active(mask);
if (mask_layer == NULL) {
/* If there's no active mask layer, create one. */
mask_layer = BKE_mask_layer_new(mask, "");
}
return mask_layer;
}
static int mask_new_exec(bContext *C, wmOperator *op)
{
char name[MAX_ID_NAME - 2];
RNA_string_get(op->ptr, "name", name);
ED_mask_new(C, name);
return OPERATOR_FINISHED;
}
void MASK_OT_new(wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Mask";
ot->description = "Create new mask";
ot->idname = "MASK_OT_new";
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* api callbacks */
ot->exec = mask_new_exec;
ot->poll = ED_operator_mask;
/* properties */
RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
}
/******************** create new masklay *********************/
static int masklay_new_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
char name[MAX_ID_NAME - 2];
RNA_string_get(op->ptr, "name", name);
BKE_mask_layer_new(mask, name);
mask->masklay_act = mask->masklay_tot - 1;
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
void MASK_OT_layer_new(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Mask Layer";
ot->description = "Add new mask layer for masking";
ot->idname = "MASK_OT_layer_new";
/* api callbacks */
ot->exec = masklay_new_exec;
ot->poll = ED_maskedit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
}
/******************** remove mask layer *********************/
static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay = BKE_mask_layer_active(mask);
if (masklay) {
BKE_mask_layer_remove(mask, masklay);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
}
return OPERATOR_FINISHED;
}
void MASK_OT_layer_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Mask Layer";
ot->description = "Remove mask layer";
ot->idname = "MASK_OT_layer_remove";
/* api callbacks */
ot->exec = masklay_remove_exec;
ot->poll = ED_maskedit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/******************** slide *********************/
enum {
SLIDE_ACTION_NONE = 0,
SLIDE_ACTION_POINT = 1,
SLIDE_ACTION_HANDLE = 2,
SLIDE_ACTION_FEATHER = 3
};
typedef struct SlidePointData {
int action;
float co[2];
float vec[3][3];
Mask *mask;
MaskLayer *masklay;
MaskSpline *spline, *orig_spline;
MaskSplinePoint *point;
MaskSplinePointUW *uw;
float handle[2], no[2], feather[2];
int width, height;
float weight, weight_scalar;
short curvature_only, accurate;
short initial_feather, overall_feather;
} SlidePointData;
static int slide_point_check_initial_feather(MaskSpline *spline)
{
int i;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (point->bezt.weight != 0.0f)
return FALSE;
/* comment for now. if all bezt weights are zero - this is as good-as initial */
#if 0
int j;
for (j = 0; j < point->tot_uw; j++) {
if (point->uw[j].w != 0.0f)
return FALSE;
}
#endif
}
return TRUE;
}
static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *event)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
SlidePointData *customdata = NULL;
MaskLayer *masklay, *cv_masklay, *feather_masklay;
MaskSpline *spline, *cv_spline, *feather_spline;
MaskSplinePoint *point, *cv_point, *feather_point;
MaskSplinePointUW *uw = NULL;
int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
float co[2], cv_score, feather_score;
const float threshold = 19;
ED_mask_mouse_pos(sa, ar, event->mval, co);
ED_mask_get_size(sa, &width, &height);
cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
if (slide_feather || !cv_point || feather_score < cv_score) {
action = SLIDE_ACTION_FEATHER;
masklay = feather_masklay;
spline = feather_spline;
point = feather_point;
}
}
if (cv_point && action == SLIDE_ACTION_NONE) {
if (is_handle)
action = SLIDE_ACTION_HANDLE;
else
action = SLIDE_ACTION_POINT;
masklay = cv_masklay;
spline = cv_spline;
point = cv_point;
}
if (action != SLIDE_ACTION_NONE) {
customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
customdata->mask = mask;
customdata->masklay = masklay;
customdata->spline = spline;
customdata->point = point;
customdata->width = width;
customdata->height = height;
customdata->action = action;
customdata->uw = uw;
if (uw) {
float co_uw[2];
float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
customdata->weight = uw->w;
customdata->weight_scalar = weight_scalar;
BKE_mask_point_segment_co(spline, point, uw->u, co_uw);
BKE_mask_point_normal(spline, point, uw->u, customdata->no);
madd_v2_v2v2fl(customdata->feather, co_uw, customdata->no, uw->w * weight_scalar);
}
else {
BezTriple *bezt = &point->bezt;
customdata->weight = bezt->weight;
customdata->weight_scalar = 1.0f;
BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
}
if (customdata->action == SLIDE_ACTION_FEATHER)
customdata->initial_feather = slide_point_check_initial_feather(spline);
copy_m3_m3(customdata->vec, point->bezt.vec);
if (BKE_mask_point_has_handle(point))
BKE_mask_point_handle(point, customdata->handle);
ED_mask_mouse_pos(sa, ar, event->mval, customdata->co);
}
return customdata;
}
static int slide_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
SlidePointData *slidedata = slide_point_customdata(C, op, event);
if (slidedata) {
Mask *mask = CTX_data_edit_mask(C);
op->customdata = slidedata;
WM_event_add_modal_handler(C, op);
#if 0
if (slidedata->uw) {
if ((slidedata->uw->flag & SELECT) == 0) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
slidedata->uw->flag |= SELECT;
ED_mask_select_flush_all(mask);
}
}
else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
BKE_mask_point_select_set(slidedata->point, TRUE);
ED_mask_select_flush_all(mask);
}
#endif
slidedata->masklay->act_spline = slidedata->spline;
slidedata->masklay->act_point = slidedata->point;
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_PASS_THROUGH;
}
static void slide_point_delta_all_feather(SlidePointData *data, float delta)
{
int i;
for (i = 0; i < data->spline->tot_point; i++) {
MaskSplinePoint *point = &data->spline->points[i];
MaskSplinePoint *orig_point = &data->orig_spline->points[i];
point->bezt.weight = orig_point->bezt.weight + delta;
if (point->bezt.weight < 0.0f)
point->bezt.weight = 0.0f;
/* not needed anymore */
#if 0
int j;
for (j = 0; j < point->tot_uw; j++) {
point->uw[j].w = orig_point->uw[j].w + delta;
if (point->uw[j].w < 0.0f)
point->uw[j].w = 0.0f;
}
#endif
}
}
static void slide_point_restore_spline(SlidePointData *data)
{
int i;
for (i = 0; i < data->spline->tot_point; i++) {
MaskSplinePoint *point = &data->spline->points[i];
MaskSplinePoint *orig_point = &data->orig_spline->points[i];
int j;
point->bezt = orig_point->bezt;
for (j = 0; j < point->tot_uw; j++)
point->uw[j] = orig_point->uw[j];
}
}
static void cancel_slide_point(SlidePointData *data)
{
/* cancel sliding */
if (data->orig_spline) {
slide_point_restore_spline(data);
}
else {
if (data->action == SLIDE_ACTION_FEATHER) {
if (data->uw)
data->uw->w = data->weight;
else
data->point->bezt.weight = data->weight;
}
else {
copy_m3_m3(data->point->bezt.vec, data->vec);
}
}
}
static void free_slide_point_data(SlidePointData *data)
{
if (data->orig_spline)
BKE_mask_spline_free(data->orig_spline);
MEM_freeN(data);
}
static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
SlidePointData *data = (SlidePointData *)op->customdata;
BezTriple *bezt = &data->point->bezt;
float co[2], dco[2];
switch (event->type) {
case LEFTALTKEY:
case RIGHTALTKEY:
case LEFTSHIFTKEY:
case RIGHTSHIFTKEY:
if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) {
if (data->action == SLIDE_ACTION_FEATHER)
data->overall_feather = (event->val == KM_PRESS);
else
data->curvature_only = (event->val == KM_PRESS);
}
if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
data->accurate = (event->val == KM_PRESS);
/* fall-through */ /* update CV position */
case MOUSEMOVE:
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
ED_mask_mouse_pos(sa, ar, event->mval, co);
sub_v2_v2v2(dco, co, data->co);
if (data->action == SLIDE_ACTION_HANDLE) {
float delta[2], offco[2];
sub_v2_v2v2(delta, data->handle, data->co);
sub_v2_v2v2(offco, co, data->co);
if (data->accurate)
mul_v2_fl(offco, 0.2f);
add_v2_v2(offco, data->co);
add_v2_v2(offco, delta);
BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
}
else if (data->action == SLIDE_ACTION_POINT) {
float delta[2];
copy_v2_v2(delta, dco);
if (data->accurate)
mul_v2_fl(delta, 0.2f);
add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
}
else if (data->action == SLIDE_ACTION_FEATHER) {
float vec[2], no[2], p[2], c[2], w, offco[2];
float *weight = NULL;
float weight_scalar = 1.0f;
int overall_feather = data->overall_feather || data->initial_feather;
add_v2_v2v2(offco, data->feather, dco);
if (data->uw) {
/* project on both sides and find the closest one,
* prevents flickering when projecting onto both sides can happen */
const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
data->uw->u, offco, MASK_PROJ_NEG);
const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
data->uw->u, offco, MASK_PROJ_POS);
float dist_pos = FLT_MAX;
float dist_neg = FLT_MAX;
float co_pos[2];
float co_neg[2];
float u;
if (u_pos > 0.0f && u_pos < 1.0f) {
BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
dist_pos = len_squared_v2v2(offco, co_pos);
}
if (u_neg > 0.0f && u_neg < 1.0f) {
BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
dist_neg = len_squared_v2v2(offco, co_neg);
}
u = dist_pos < dist_neg ? u_pos : u_neg;
if (u > 0.0f && u < 1.0f) {
data->uw->u = u;
data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
weight = &data->uw->w;
weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
if (weight_scalar != 0.0f) {
weight_scalar = 1.0f / weight_scalar;
}
BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
}
}
else {
weight = &bezt->weight;
/* weight_scalar = 1.0f; keep as is */
copy_v2_v2(no, data->no);
copy_v2_v2(p, bezt->vec[1]);
}
if (weight) {
sub_v2_v2v2(c, offco, p);
project_v2_v2v2(vec, c, no);
w = len_v2(vec);
if (overall_feather) {
float delta;
if (dot_v2v2(no, vec) <= 0.0f)
w = -w;
delta = w - data->weight * data->weight_scalar;
if (data->orig_spline == NULL) {
/* restore weight for currently sliding point, so orig_spline would be created
* with original weights used
*/
*weight = data->weight;
data->orig_spline = BKE_mask_spline_copy(data->spline);
}
slide_point_delta_all_feather(data, delta);
}
else {
if (dot_v2v2(no, vec) <= 0.0f)
w = 0.0f;
if (data->orig_spline) {
/* restore possible overall feather changes */
slide_point_restore_spline(data);
BKE_mask_spline_free(data->orig_spline);
data->orig_spline = NULL;
}
if (weight_scalar != 0.0f) {
*weight = w * weight_scalar;
}
}
}
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
DAG_id_tag_update(&data->mask->id, 0);
break;
}
case LEFTMOUSE:
if (event->val == KM_RELEASE) {
Scene *scene = CTX_data_scene(C);
/* dont key sliding feather uw's */
if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
if (IS_AUTOKEY_ON(scene)) {
ED_mask_layer_shape_auto_key(data->masklay, CFRA);
}
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
DAG_id_tag_update(&data->mask->id, 0);
free_slide_point_data(op->customdata); /* keep this last! */
return OPERATOR_FINISHED;
}
break;
case ESCKEY:
cancel_slide_point(op->customdata);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
DAG_id_tag_update(&data->mask->id, 0);
free_slide_point_data(op->customdata); /* keep this last! */
return OPERATOR_CANCELLED;
}
return OPERATOR_RUNNING_MODAL;
}
void MASK_OT_slide_point(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Slide Point";
ot->description = "Slide control points";
ot->idname = "MASK_OT_slide_point";
/* api callbacks */
ot->invoke = slide_point_invoke;
ot->modal = slide_point_modal;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
}
/******************** toggle cyclic *********************/
static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
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)) {
spline->flag ^= MASK_SPLINE_CYCLIC;
}
}
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
void MASK_OT_cyclic_toggle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle Cyclic";
ot->description = "Toggle cyclic for selected splines";
ot->idname = "MASK_OT_cyclic_toggle";
/* api callbacks */
ot->exec = cyclic_toggle_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/******************** delete *********************/
static void delete_feather_points(MaskSplinePoint *point)
{
int i, count = 0;
if (!point->tot_uw)
return;
for (i = 0; i < point->tot_uw; i++) {
if ((point->uw[i].flag & SELECT) == 0)
count++;
}
if (count == 0) {
MEM_freeN(point->uw);
point->uw = NULL;
point->tot_uw = 0;
}
else {
MaskSplinePointUW *new_uw;
int j = 0;
new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
for (i = 0; i < point->tot_uw; i++) {
if ((point->uw[i].flag & SELECT) == 0) {
new_uw[j++] = point->uw[i];
}
}
MEM_freeN(point->uw);
point->uw = new_uw;
point->tot_uw = count;
}
}
static int delete_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
int mask_layer_shape_ofs = 0;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
spline = masklay->splines.first;
while (spline) {
const int tot_point_orig = spline->tot_point;
int i, count = 0;
MaskSpline *next_spline = spline->next;
/* count unselected points */
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (!MASKPOINT_ISSEL_ANY(point))
count++;
}
if (count == 0) {
/* delete the whole spline */
BLI_remlink(&masklay->splines, spline);
BKE_mask_spline_free(spline);
if (spline == masklay->act_spline) {
masklay->act_spline = NULL;
masklay->act_point = NULL;
}
BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
}
else {
MaskSplinePoint *new_points;
int j;
new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
for (i = 0, j = 0; i < tot_point_orig; i++) {
MaskSplinePoint *point = &spline->points[i];
if (!MASKPOINT_ISSEL_ANY(point)) {
if (point == masklay->act_point)
masklay->act_point = &new_points[j];
delete_feather_points(point);
new_points[j] = *point;
j++;
}
else {
if (point == masklay->act_point)
masklay->act_point = NULL;
BKE_mask_point_free(point);
spline->tot_point--;
BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
}
}
mask_layer_shape_ofs += spline->tot_point;
MEM_freeN(spline->points);
spline->points = new_points;
ED_mask_select_flush_all(mask);
}
spline = next_spline;
}
/* not essential but confuses users when there are keys with no data!
* assume if they delete all data from the layer they also dont care about keys */
if (masklay->splines.first == NULL) {
BKE_mask_layer_free_shapes(masklay);
}
}
/* TODO: only update edited splines */
BKE_mask_update_display(mask, CFRA);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
void MASK_OT_delete(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete";
ot->description = "Delete selected control points or splines";
ot->idname = "MASK_OT_delete";
/* api callbacks */
ot->invoke = WM_operator_confirm;
ot->exec = delete_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *** switch direction *** */
static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
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;
int change_layer = FALSE;
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)) {
BKE_mask_spline_direction_switch(masklay, spline);
change = TRUE;
change_layer = TRUE;
}
}
if (change_layer) {
if (IS_AUTOKEY_ON(scene)) {
ED_mask_layer_shape_auto_key(masklay, CFRA);
}
}
}
if (change) {
/* TODO: only update this spline */
BKE_mask_update_display(mask, CFRA);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_switch_direction(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Switch Direction";
ot->description = "Switch direction of selected splines";
ot->idname = "MASK_OT_switch_direction";
/* api callbacks */
ot->exec = mask_switch_direction_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *** recalc normals *** */
static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int i;
int change = FALSE;
/* do actual selection */
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
int change_layer = FALSE;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (MASKPOINT_ISSEL_ANY(point)) {
BKE_mask_calc_handle_point_auto(spline, point, FALSE);
change = TRUE;
change_layer = TRUE;
}
}
}
if (change_layer) {
if (IS_AUTOKEY_ON(scene)) {
ED_mask_layer_shape_auto_key(masklay, CFRA);
}
}
}
if (change) {
/* TODO: only update this spline */
BKE_mask_update_display(mask, CFRA);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
/* named to match mesh recalc normals */
void MASK_OT_normals_make_consistent(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Recalc Normals";
ot->description = "Re-calculate the direction of selected handles";
ot->idname = "MASK_OT_normals_make_consistent";
/* api callbacks */
ot->exec = mask_normals_make_consistent_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/******************** set handle type *********************/
static int set_handle_type_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int handle_type = RNA_enum_get(op->ptr, "type");
bool change = false;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
int i;
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (MASKPOINT_ISSEL_ANY(point)) {
BezTriple *bezt = &point->bezt;
bezt->h1 = bezt->h2 = handle_type;
change = true;
}
}
}
}
if (change) {
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DAG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_handle_type_set(wmOperatorType *ot)
{
static EnumPropertyItem editcurve_handle_type_items[] = {
{HD_AUTO, "AUTO", 0, "Auto", ""},
{HD_VECT, "VECTOR", 0, "Vector", ""},
{HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
{0, NULL, 0, NULL, NULL}
};
/* identifiers */
ot->name = "Set Handle Type";
ot->description = "Set type of handles for selected control points";
ot->idname = "MASK_OT_handle_type_set";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = set_handle_type_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
}
/* ********* clear/set restrict view *********/
static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int changed = FALSE;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
if (masklay->restrictflag & OB_RESTRICT_VIEW) {
ED_mask_layer_select_set(masklay, TRUE);
masklay->restrictflag &= ~OB_RESTRICT_VIEW;
changed = 1;
}
}
if (changed) {
WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
DAG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void MASK_OT_hide_view_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Restrict View";
ot->description = "Reveal the layer by setting the hide flag";
ot->idname = "MASK_OT_hide_view_clear";
/* api callbacks */
ot->exec = mask_hide_view_clear_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
const int unselected = RNA_boolean_get(op->ptr, "unselected");
int changed = FALSE;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
continue;
}
if (!unselected) {
if (ED_mask_layer_select_check(masklay)) {
ED_mask_layer_select_set(masklay, FALSE);
masklay->restrictflag |= OB_RESTRICT_VIEW;
changed = 1;
if (masklay == BKE_mask_layer_active(mask)) {
BKE_mask_layer_active_set(mask, NULL);
}
}
}
else {
if (!ED_mask_layer_select_check(masklay)) {
masklay->restrictflag |= OB_RESTRICT_VIEW;
changed = 1;
if (masklay == BKE_mask_layer_active(mask)) {
BKE_mask_layer_active_set(mask, NULL);
}
}
}
}
if (changed) {
WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
DAG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void MASK_OT_hide_view_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Restrict View";
ot->description = "Hide the layer by setting the hide flag";
ot->idname = "MASK_OT_hide_view_set";
/* api callbacks */
ot->exec = mask_hide_view_set_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
}
static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
int changed = FALSE;
int i;
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
continue;
}
for (spline = masklay->splines.first; spline; spline = spline->next) {
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (MASKPOINT_ISSEL_ANY(point)) {
BezTriple *bezt = &point->bezt;
bezt->weight = 0.0f;
changed = TRUE;
}
}
}
}
if (changed) {
/* TODO: only update edited splines */
BKE_mask_update_display(mask, CFRA);
WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
DAG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void MASK_OT_feather_weight_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Feather Weight";
ot->description = "Reset the feather weight to zero";
ot->idname = "MASK_OT_feather_weight_clear";
/* api callbacks */
ot->exec = mask_feather_weight_clear_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/******************** move mask layer operator *********************/
static int mask_layer_move_poll(bContext *C)
{
if (ED_maskedit_mask_poll(C)) {
Mask *mask = CTX_data_edit_mask(C);
return mask->masklay_tot > 0;
}
return FALSE;
}
static int mask_layer_move_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
MaskLayer *mask_layer_other;
int direction = RNA_enum_get(op->ptr, "direction");
if (!mask_layer)
return OPERATOR_CANCELLED;
if (direction == -1) {
mask_layer_other = mask_layer->prev;
if (!mask_layer_other)
return OPERATOR_CANCELLED;
BLI_remlink(&mask->masklayers, mask_layer);
BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
mask->masklay_act--;
}
else if (direction == 1) {
mask_layer_other = mask_layer->next;
if (!mask_layer_other)
return OPERATOR_CANCELLED;
BLI_remlink(&mask->masklayers, mask_layer);
BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
mask->masklay_act++;
}
return OPERATOR_FINISHED;
}
void MASK_OT_layer_move(wmOperatorType *ot)
{
static EnumPropertyItem direction_items[] = {
{-1, "UP", 0, "Up", ""},
{1, "DOWN", 0, "Down", ""},
{0, NULL, 0, NULL, NULL}
};
/* identifiers */
ot->name = "Move Layer";
ot->description = "Move the active layer up/down in the list";
ot->idname = "MASK_OT_layer_move";
/* api callbacks */
ot->exec = mask_layer_move_exec;
ot->poll = mask_layer_move_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move the active layer");
}
/******************** duplicate *********************/
static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer = BKE_mask_layer_active(mask);
MaskSpline *spline;
if (mask_layer == NULL) {
return OPERATOR_CANCELLED;
}
for (spline = mask_layer->splines.last;
spline;
spline = spline->prev)
{
MaskSplinePoint *point = spline->points;
int i = 0;
while (i < spline->tot_point) {
int start = i, end = -1;
/* Find next selected segment. */
while (MASKPOINT_ISSEL_ANY(point)) {
BKE_mask_point_select_set(point, false);
end = i;
if (i >= spline->tot_point - 1) {
break;
}
i++;
point++;
}
if (end >= start) {
MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
MaskSplinePoint *new_point;
int b;
/* BKE_mask_spline_add might allocate the points, need to free them in this case. */
if (new_spline->points) {
MEM_freeN(new_spline->points);
}
/* Copy options from old spline. */
new_spline->flag = spline->flag;
new_spline->offset_mode = spline->offset_mode;
new_spline->weight_interp = spline->weight_interp;
new_spline->parent = spline->parent;
/* Allocate new points and copy them from old spline. */
new_spline->tot_point = end - start + 1;
new_spline->points = MEM_mallocN(sizeof(MaskSplinePoint) * new_spline->tot_point,
"duplicated mask points");
memcpy(new_spline->points, spline->points + start,
new_spline->tot_point * sizeof(MaskSplinePoint));
/* Select points and duplicate their UWs (if needed). */
for (b = 0, new_point = new_spline->points;
b < new_spline->tot_point;
b++, new_point++)
{
if (new_point->uw) {
new_point->uw = MEM_dupallocN(new_point->uw);
}
BKE_mask_point_select_set(new_point, true);
}
/* Clear cyclic flag if we didn't copy the whole spline. */
if (new_spline->flag & MASK_SPLINE_CYCLIC) {
if (start != 0 || end != spline->tot_point - 1) {
new_spline->flag &= ~MASK_SPLINE_CYCLIC;
}
}
/* Flush selection to splines. */
new_spline->flag |= SELECT;
spline->flag &= ~SELECT;
mask_layer->act_spline = new_spline;
}
i++;
point++;
}
}
/* TODO: only update edited splines */
BKE_mask_update_display(mask, CFRA);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
void MASK_OT_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Mask";
ot->description = "Duplicate selected control points and segments between them";
ot->idname = "MASK_OT_duplicate";
/* api callbacks */
ot->exec = mask_duplicate_exec;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}