2
* ***** BEGIN GPL LICENSE BLOCK *****
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version 2
7
* of the License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software Foundation,
16
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
* The Original Code is Copyright (C) 2012 Blender Foundation.
19
* All rights reserved.
22
* Contributor(s): Blender Foundation,
25
* ***** END GPL LICENSE BLOCK *****
28
/** \file blender/editors/mask/mask_ops.c
32
#include "MEM_guardedalloc.h"
34
#include "BLI_listbase.h"
37
#include "BKE_context.h"
38
#include "BKE_depsgraph.h"
42
#include "DNA_scene_types.h"
43
#include "DNA_mask_types.h"
44
#include "DNA_object_types.h" /* SELECT */
51
#include "ED_keyframing.h"
53
#include "ED_screen.h"
55
#include "RNA_access.h"
56
#include "RNA_define.h"
58
#include "mask_intern.h" /* own include */
60
/******************** utility functions *********************/
62
MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask, float normal_co[2], int threshold,
63
MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
66
ScrArea *sa = CTX_wm_area(C);
67
ARegion *ar = CTX_wm_region(C);
70
MaskLayer *point_masklay = NULL;
71
MaskSpline *point_spline = NULL;
72
MaskSplinePoint *point = NULL;
74
float len = FLT_MAX, scalex, scaley;
75
int is_handle = FALSE, width, height;
77
ED_mask_get_size(sa, &width, &height);
78
ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
80
co[0] = normal_co[0] * scalex;
81
co[1] = normal_co[1] * scaley;
83
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
86
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
90
for (spline = masklay->splines.first; spline; spline = spline->next) {
91
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
95
for (i = 0; i < spline->tot_point; i++) {
96
MaskSplinePoint *cur_point = &spline->points[i];
97
MaskSplinePoint *cur_point_deform = &points_array[i];
98
float cur_len, vec[2], handle[2];
100
vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
101
vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
103
if (BKE_mask_point_has_handle(cur_point)) {
104
BKE_mask_point_handle(cur_point_deform, handle);
108
cur_len = len_v2v2(co, handle);
111
point_masklay = masklay;
112
point_spline = spline;
119
cur_len = len_v2v2(co, vec);
122
point_spline = spline;
123
point_masklay = masklay;
132
if (len < threshold) {
134
*masklay_r = point_masklay;
137
*spline_r = point_spline;
140
*is_handle_r = is_handle;
155
*is_handle_r = FALSE;
160
int ED_mask_feather_find_nearest(const bContext *C, Mask *mask, float normal_co[2], int threshold,
161
MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
162
MaskSplinePointUW **uw_r, float *score)
164
ScrArea *sa = CTX_wm_area(C);
165
ARegion *ar = CTX_wm_region(C);
167
MaskLayer *masklay, *point_masklay = NULL;
168
MaskSpline *point_spline = NULL;
169
MaskSplinePoint *point = NULL;
170
MaskSplinePointUW *uw = NULL;
171
float len = FLT_MAX, co[2];
172
float scalex, scaley;
175
ED_mask_get_size(sa, &width, &height);
176
ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
178
co[0] = normal_co[0] * scalex;
179
co[1] = normal_co[1] * scaley;
181
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
184
for (spline = masklay->splines.first; spline; spline = spline->next) {
185
//MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
187
int i, tot_feather_point;
188
float (*feather_points)[2], (*fp)[2];
190
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
194
feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
196
for (i = 0; i < spline->tot_point; i++) {
198
MaskSplinePoint *cur_point = &spline->points[i];
200
for (j = 0; j < cur_point->tot_uw + 1; j++) {
201
float cur_len, vec[2];
203
vec[0] = (*fp)[0] * scalex;
204
vec[1] = (*fp)[1] * scaley;
206
cur_len = len_v2v2(vec, co);
208
if (point == NULL || cur_len < len) {
212
uw = &cur_point->uw[j - 1];
214
point_masklay = masklay;
215
point_spline = spline;
224
MEM_freeN(feather_points);
228
if (len < threshold) {
230
*masklay_r = point_masklay;
233
*spline_r = point_spline;
260
/******************** create new mask *********************/
262
Mask *ED_mask_new(bContext *C, const char *name)
264
ScrArea *sa = CTX_wm_area(C);
265
Main *bmain = CTX_data_main(C);
268
mask = BKE_mask_new(bmain, name);
270
if (sa && sa->spacedata.first) {
271
switch (sa->spacetype) {
274
SpaceClip *sc = sa->spacedata.first;
275
ED_space_clip_set_mask(C, sc, mask);
285
SpaceImage *sima = sa->spacedata.first;
286
ED_space_image_set_mask(C, sima, mask);
295
static int mask_new_exec(bContext *C, wmOperator *op)
297
char name[MAX_ID_NAME - 2];
299
RNA_string_get(op->ptr, "name", name);
301
ED_mask_new(C, name);
303
return OPERATOR_FINISHED;
306
void MASK_OT_new(wmOperatorType *ot)
309
ot->name = "New Mask";
310
ot->description = "Create new mask";
311
ot->idname = "MASK_OT_new";
314
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
317
ot->exec = mask_new_exec;
318
ot->poll = ED_operator_mask;
321
RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
324
/******************** create new masklay *********************/
326
static int masklay_new_exec(bContext *C, wmOperator *op)
328
Mask *mask = CTX_data_edit_mask(C);
329
char name[MAX_ID_NAME - 2];
331
RNA_string_get(op->ptr, "name", name);
333
BKE_mask_layer_new(mask, name);
334
mask->masklay_act = mask->masklay_tot - 1;
336
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
338
return OPERATOR_FINISHED;
341
void MASK_OT_layer_new(wmOperatorType *ot)
344
ot->name = "Add Mask Layer";
345
ot->description = "Add new mask layer for masking";
346
ot->idname = "MASK_OT_layer_new";
349
ot->exec = masklay_new_exec;
350
ot->poll = ED_maskedit_poll;
353
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
356
RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
359
/******************** remove mask layer *********************/
361
static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
363
Mask *mask = CTX_data_edit_mask(C);
364
MaskLayer *masklay = BKE_mask_layer_active(mask);
367
BKE_mask_layer_remove(mask, masklay);
369
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
372
return OPERATOR_FINISHED;
375
void MASK_OT_layer_remove(wmOperatorType *ot)
378
ot->name = "Remove Mask Layer";
379
ot->description = "Remove mask layer";
380
ot->idname = "MASK_OT_layer_remove";
383
ot->exec = masklay_remove_exec;
384
ot->poll = ED_maskedit_poll;
387
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
390
/******************** slide *********************/
393
SLIDE_ACTION_NONE = 0,
394
SLIDE_ACTION_POINT = 1,
395
SLIDE_ACTION_HANDLE = 2,
396
SLIDE_ACTION_FEATHER = 3
399
typedef struct SlidePointData {
407
MaskSpline *spline, *orig_spline;
408
MaskSplinePoint *point;
409
MaskSplinePointUW *uw;
410
float handle[2], no[2], feather[2];
412
float weight, weight_scalar;
414
short curvature_only, accurate;
415
short initial_feather, overall_feather;
418
static int slide_point_check_initial_feather(MaskSpline *spline)
422
for (i = 0; i < spline->tot_point; i++) {
423
MaskSplinePoint *point = &spline->points[i];
425
if (point->bezt.weight != 0.0f)
428
/* comment for now. if all bezt weights are zero - this is as good-as initial */
431
for (j = 0; j < point->tot_uw; j++) {
432
if (point->uw[j].w != 0.0f)
441
static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
443
ScrArea *sa = CTX_wm_area(C);
444
ARegion *ar = CTX_wm_region(C);
446
Mask *mask = CTX_data_edit_mask(C);
447
SlidePointData *customdata = NULL;
448
MaskLayer *masklay, *cv_masklay, *feather_masklay;
449
MaskSpline *spline, *cv_spline, *feather_spline;
450
MaskSplinePoint *point, *cv_point, *feather_point;
451
MaskSplinePointUW *uw = NULL;
452
int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
453
int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
454
float co[2], cv_score, feather_score;
455
const float threshold = 19;
457
ED_mask_mouse_pos(sa, ar, event->mval, co);
458
ED_mask_get_size(sa, &width, &height);
460
cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
462
if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
463
if (slide_feather || !cv_point || feather_score < cv_score) {
464
action = SLIDE_ACTION_FEATHER;
466
masklay = feather_masklay;
467
spline = feather_spline;
468
point = feather_point;
472
if (cv_point && action == SLIDE_ACTION_NONE) {
474
action = SLIDE_ACTION_HANDLE;
476
action = SLIDE_ACTION_POINT;
478
masklay = cv_masklay;
483
if (action != SLIDE_ACTION_NONE) {
484
customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
486
customdata->mask = mask;
487
customdata->masklay = masklay;
488
customdata->spline = spline;
489
customdata->point = point;
490
customdata->width = width;
491
customdata->height = height;
492
customdata->action = action;
497
float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
499
customdata->weight = uw->w;
500
customdata->weight_scalar = weight_scalar;
501
BKE_mask_point_segment_co(spline, point, uw->u, co_uw);
502
BKE_mask_point_normal(spline, point, uw->u, customdata->no);
504
madd_v2_v2v2fl(customdata->feather, co_uw, customdata->no, uw->w * weight_scalar);
507
BezTriple *bezt = &point->bezt;
509
customdata->weight = bezt->weight;
510
customdata->weight_scalar = 1.0f;
511
BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
513
madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
516
if (customdata->action == SLIDE_ACTION_FEATHER)
517
customdata->initial_feather = slide_point_check_initial_feather(spline);
519
copy_m3_m3(customdata->vec, point->bezt.vec);
520
if (BKE_mask_point_has_handle(point))
521
BKE_mask_point_handle(point, customdata->handle);
522
ED_mask_mouse_pos(sa, ar, event->mval, customdata->co);
528
static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
530
SlidePointData *slidedata = slide_point_customdata(C, op, event);
533
Mask *mask = CTX_data_edit_mask(C);
535
op->customdata = slidedata;
537
WM_event_add_modal_handler(C, op);
541
if ((slidedata->uw->flag & SELECT) == 0) {
542
ED_mask_select_toggle_all(mask, SEL_DESELECT);
544
slidedata->uw->flag |= SELECT;
546
ED_mask_select_flush_all(mask);
549
else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
550
ED_mask_select_toggle_all(mask, SEL_DESELECT);
552
BKE_mask_point_select_set(slidedata->point, TRUE);
554
ED_mask_select_flush_all(mask);
558
slidedata->masklay->act_spline = slidedata->spline;
559
slidedata->masklay->act_point = slidedata->point;
561
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
563
return OPERATOR_RUNNING_MODAL;
566
return OPERATOR_PASS_THROUGH;
569
static void slide_point_delta_all_feather(SlidePointData *data, float delta)
573
for (i = 0; i < data->spline->tot_point; i++) {
574
MaskSplinePoint *point = &data->spline->points[i];
575
MaskSplinePoint *orig_point = &data->orig_spline->points[i];
577
point->bezt.weight = orig_point->bezt.weight + delta;
578
if (point->bezt.weight < 0.0f)
579
point->bezt.weight = 0.0f;
581
/* not needed anymore */
584
for (j = 0; j < point->tot_uw; j++) {
585
point->uw[j].w = orig_point->uw[j].w + delta;
586
if (point->uw[j].w < 0.0f)
587
point->uw[j].w = 0.0f;
593
static void slide_point_restore_spline(SlidePointData *data)
597
for (i = 0; i < data->spline->tot_point; i++) {
598
MaskSplinePoint *point = &data->spline->points[i];
599
MaskSplinePoint *orig_point = &data->orig_spline->points[i];
602
point->bezt = orig_point->bezt;
604
for (j = 0; j < point->tot_uw; j++)
605
point->uw[j] = orig_point->uw[j];
609
static void cancel_slide_point(SlidePointData *data)
613
if (data->orig_spline) {
614
slide_point_restore_spline(data);
617
if (data->action == SLIDE_ACTION_FEATHER) {
619
data->uw->w = data->weight;
621
data->point->bezt.weight = data->weight;
624
copy_m3_m3(data->point->bezt.vec, data->vec);
629
static void free_slide_point_data(SlidePointData *data)
631
if (data->orig_spline)
632
BKE_mask_spline_free(data->orig_spline);
637
static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
639
SlidePointData *data = (SlidePointData *)op->customdata;
640
BezTriple *bezt = &data->point->bezt;
643
switch (event->type) {
648
if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) {
649
if (data->action == SLIDE_ACTION_FEATHER)
650
data->overall_feather = (event->val == KM_PRESS);
652
data->curvature_only = (event->val == KM_PRESS);
655
if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
656
data->accurate = (event->val == KM_PRESS);
658
/* no break! update CV position */
662
ScrArea *sa = CTX_wm_area(C);
663
ARegion *ar = CTX_wm_region(C);
665
ED_mask_mouse_pos(sa, ar, event->mval, co);
666
sub_v2_v2v2(dco, co, data->co);
668
if (data->action == SLIDE_ACTION_HANDLE) {
669
float delta[2], offco[2];
671
sub_v2_v2v2(delta, data->handle, data->co);
673
sub_v2_v2v2(offco, co, data->co);
675
mul_v2_fl(offco, 0.2f);
676
add_v2_v2(offco, data->co);
677
add_v2_v2(offco, delta);
679
BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
681
else if (data->action == SLIDE_ACTION_POINT) {
684
copy_v2_v2(delta, dco);
686
mul_v2_fl(delta, 0.2f);
688
add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
689
add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
690
add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
692
else if (data->action == SLIDE_ACTION_FEATHER) {
693
float vec[2], no[2], p[2], c[2], w, offco[2];
694
float *weight = NULL;
695
float weight_scalar = 1.0f;
696
int overall_feather = data->overall_feather || data->initial_feather;
698
add_v2_v2v2(offco, data->feather, dco);
701
/* project on both sides and find the closest one,
702
* prevents flickering when projecting onto both sides can happen */
703
const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
704
data->uw->u, offco, MASK_PROJ_NEG);
705
const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
706
data->uw->u, offco, MASK_PROJ_POS);
707
float dist_pos = FLT_MAX;
708
float dist_neg = FLT_MAX;
713
if (u_pos > 0.0f && u_pos < 1.0f) {
714
BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
715
dist_pos = len_squared_v2v2(offco, co_pos);
718
if (u_neg > 0.0f && u_neg < 1.0f) {
719
BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
720
dist_neg = len_squared_v2v2(offco, co_neg);
723
u = dist_pos < dist_neg ? u_pos : u_neg;
725
if (u > 0.0f && u < 1.0f) {
728
data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
729
weight = &data->uw->w;
730
weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
731
if (weight_scalar != 0.0f) {
732
weight_scalar = 1.0f / weight_scalar;
735
BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
736
BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
740
weight = &bezt->weight;
741
/* weight_scalar = 1.0f; keep as is */
742
copy_v2_v2(no, data->no);
743
copy_v2_v2(p, bezt->vec[1]);
747
sub_v2_v2v2(c, offco, p);
748
project_v2_v2v2(vec, c, no);
752
if (overall_feather) {
755
if (dot_v2v2(no, vec) <= 0.0f)
758
delta = w - data->weight * data->weight_scalar;
760
if (data->orig_spline == NULL) {
761
/* restore weight for currently sliding point, so orig_spline would be created
762
* with original weights used
764
*weight = data->weight;
766
data->orig_spline = BKE_mask_spline_copy(data->spline);
769
slide_point_delta_all_feather(data, delta);
772
if (dot_v2v2(no, vec) <= 0.0f)
775
if (data->orig_spline) {
776
/* restore possible overall feather changes */
777
slide_point_restore_spline(data);
779
BKE_mask_spline_free(data->orig_spline);
780
data->orig_spline = NULL;
783
if (weight_scalar != 0.0f) {
784
*weight = w * weight_scalar;
790
WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
791
DAG_id_tag_update(&data->mask->id, 0);
797
if (event->val == KM_RELEASE) {
798
Scene *scene = CTX_data_scene(C);
800
/* dont key sliding feather uw's */
801
if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
802
if (IS_AUTOKEY_ON(scene)) {
803
ED_mask_layer_shape_auto_key(data->masklay, CFRA);
807
WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
808
DAG_id_tag_update(&data->mask->id, 0);
810
free_slide_point_data(op->customdata); /* keep this last! */
811
return OPERATOR_FINISHED;
817
cancel_slide_point(op->customdata);
819
WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
820
DAG_id_tag_update(&data->mask->id, 0);
822
free_slide_point_data(op->customdata); /* keep this last! */
823
return OPERATOR_CANCELLED;
826
return OPERATOR_RUNNING_MODAL;
829
void MASK_OT_slide_point(wmOperatorType *ot)
832
ot->name = "Slide Point";
833
ot->description = "Slide control points";
834
ot->idname = "MASK_OT_slide_point";
837
ot->invoke = slide_point_invoke;
838
ot->modal = slide_point_modal;
839
ot->poll = ED_maskedit_mask_poll;
842
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
844
RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
847
/******************** toggle cyclic *********************/
849
static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
851
Mask *mask = CTX_data_edit_mask(C);
854
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
857
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
861
for (spline = masklay->splines.first; spline; spline = spline->next) {
862
if (ED_mask_spline_select_check(spline)) {
863
spline->flag ^= MASK_SPLINE_CYCLIC;
868
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
870
return OPERATOR_FINISHED;
873
void MASK_OT_cyclic_toggle(wmOperatorType *ot)
876
ot->name = "Toggle Cyclic";
877
ot->description = "Toggle cyclic for selected splines";
878
ot->idname = "MASK_OT_cyclic_toggle";
881
ot->exec = cyclic_toggle_exec;
882
ot->poll = ED_maskedit_mask_poll;
885
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
888
/******************** delete *********************/
890
static void delete_feather_points(MaskSplinePoint *point)
897
for (i = 0; i < point->tot_uw; i++) {
898
if ((point->uw[i].flag & SELECT) == 0)
903
MEM_freeN(point->uw);
908
MaskSplinePointUW *new_uw;
911
new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
913
for (i = 0; i < point->tot_uw; i++) {
914
if ((point->uw[i].flag & SELECT) == 0) {
915
new_uw[j++] = point->uw[i];
919
MEM_freeN(point->uw);
922
point->tot_uw = count;
926
static int delete_exec(bContext *C, wmOperator *UNUSED(op))
928
Scene *scene = CTX_data_scene(C);
929
Mask *mask = CTX_data_edit_mask(C);
932
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
934
int mask_layer_shape_ofs = 0;
936
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
940
spline = masklay->splines.first;
943
const int tot_point_orig = spline->tot_point;
945
MaskSpline *next_spline = spline->next;
947
/* count unselected points */
948
for (i = 0; i < spline->tot_point; i++) {
949
MaskSplinePoint *point = &spline->points[i];
951
if (!MASKPOINT_ISSEL_ANY(point))
957
/* delete the whole spline */
958
BLI_remlink(&masklay->splines, spline);
959
BKE_mask_spline_free(spline);
961
if (spline == masklay->act_spline) {
962
masklay->act_spline = NULL;
963
masklay->act_point = NULL;
966
BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
969
MaskSplinePoint *new_points;
972
new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
974
for (i = 0, j = 0; i < tot_point_orig; i++) {
975
MaskSplinePoint *point = &spline->points[i];
977
if (!MASKPOINT_ISSEL_ANY(point)) {
978
if (point == masklay->act_point)
979
masklay->act_point = &new_points[j];
981
delete_feather_points(point);
983
new_points[j] = *point;
987
if (point == masklay->act_point)
988
masklay->act_point = NULL;
990
BKE_mask_point_free(point);
993
BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
997
mask_layer_shape_ofs += spline->tot_point;
999
MEM_freeN(spline->points);
1000
spline->points = new_points;
1002
ED_mask_select_flush_all(mask);
1005
spline = next_spline;
1008
/* not essential but confuses users when there are keys with no data!
1009
* assume if they delete all data from the layer they also dont care about keys */
1010
if (masklay->splines.first == NULL) {
1011
BKE_mask_layer_free_shapes(masklay);
1015
/* TODO: only update edited splines */
1016
BKE_mask_update_display(mask, CFRA);
1018
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1020
return OPERATOR_FINISHED;
1023
void MASK_OT_delete(wmOperatorType *ot)
1026
ot->name = "Delete";
1027
ot->description = "Delete selected control points or splines";
1028
ot->idname = "MASK_OT_delete";
1031
ot->invoke = WM_operator_confirm;
1032
ot->exec = delete_exec;
1033
ot->poll = ED_maskedit_mask_poll;
1036
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1039
/* *** switch direction *** */
1040
static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
1042
Scene *scene = CTX_data_scene(C);
1043
Mask *mask = CTX_data_edit_mask(C);
1048
/* do actual selection */
1049
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1051
int change_layer = FALSE;
1053
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1057
for (spline = masklay->splines.first; spline; spline = spline->next) {
1058
if (ED_mask_spline_select_check(spline)) {
1059
BKE_mask_spline_direction_switch(masklay, spline);
1061
change_layer = TRUE;
1066
if (IS_AUTOKEY_ON(scene)) {
1067
ED_mask_layer_shape_auto_key(masklay, CFRA);
1073
/* TODO: only update this spline */
1074
BKE_mask_update_display(mask, CFRA);
1076
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1078
return OPERATOR_FINISHED;
1081
return OPERATOR_CANCELLED;
1084
void MASK_OT_switch_direction(wmOperatorType *ot)
1087
ot->name = "Switch Direction";
1088
ot->description = "Switch direction of selected splines";
1089
ot->idname = "MASK_OT_switch_direction";
1092
ot->exec = mask_switch_direction_exec;
1093
ot->poll = ED_maskedit_mask_poll;
1096
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1100
/* *** recalc normals *** */
1101
static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1103
Scene *scene = CTX_data_scene(C);
1104
Mask *mask = CTX_data_edit_mask(C);
1110
/* do actual selection */
1111
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1113
int change_layer = FALSE;
1115
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1119
for (spline = masklay->splines.first; spline; spline = spline->next) {
1120
for (i = 0; i < spline->tot_point; i++) {
1121
MaskSplinePoint *point = &spline->points[i];
1123
if (MASKPOINT_ISSEL_ANY(point)) {
1124
BKE_mask_calc_handle_point_auto(spline, point, FALSE);
1126
change_layer = TRUE;
1132
if (IS_AUTOKEY_ON(scene)) {
1133
ED_mask_layer_shape_auto_key(masklay, CFRA);
1139
/* TODO: only update this spline */
1140
BKE_mask_update_display(mask, CFRA);
1142
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1144
return OPERATOR_FINISHED;
1147
return OPERATOR_CANCELLED;
1150
/* named to match mesh recalc normals */
1151
void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1154
ot->name = "Recalc Normals";
1155
ot->description = "Re-calculate the direction of selected handles";
1156
ot->idname = "MASK_OT_normals_make_consistent";
1159
ot->exec = mask_normals_make_consistent_exec;
1160
ot->poll = ED_maskedit_mask_poll;
1163
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1167
/******************** set handle type *********************/
1169
static int set_handle_type_exec(bContext *C, wmOperator *op)
1171
Mask *mask = CTX_data_edit_mask(C);
1173
int handle_type = RNA_enum_get(op->ptr, "type");
1175
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1179
if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1183
for (spline = masklay->splines.first; spline; spline = spline->next) {
1184
for (i = 0; i < spline->tot_point; i++) {
1185
MaskSplinePoint *point = &spline->points[i];
1187
if (MASKPOINT_ISSEL_ANY(point)) {
1188
BezTriple *bezt = &point->bezt;
1190
bezt->h1 = bezt->h2 = handle_type;
1196
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1197
DAG_id_tag_update(&mask->id, 0);
1199
return OPERATOR_FINISHED;
1202
void MASK_OT_handle_type_set(wmOperatorType *ot)
1204
static EnumPropertyItem editcurve_handle_type_items[] = {
1205
{HD_AUTO, "AUTO", 0, "Auto", ""},
1206
{HD_VECT, "VECTOR", 0, "Vector", ""},
1207
{HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1208
{0, NULL, 0, NULL, NULL}
1212
ot->name = "Set Handle Type";
1213
ot->description = "Set type of handles for selected control points";
1214
ot->idname = "MASK_OT_handle_type_set";
1217
ot->invoke = WM_menu_invoke;
1218
ot->exec = set_handle_type_exec;
1219
ot->poll = ED_maskedit_mask_poll;
1222
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1225
ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1229
/* ********* clear/set restrict view *********/
1230
static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1232
Mask *mask = CTX_data_edit_mask(C);
1234
int changed = FALSE;
1236
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1238
if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1239
ED_mask_layer_select_set(masklay, TRUE);
1240
masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1246
WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1247
DAG_id_tag_update(&mask->id, 0);
1249
return OPERATOR_FINISHED;
1252
return OPERATOR_CANCELLED;
1256
void MASK_OT_hide_view_clear(wmOperatorType *ot)
1260
ot->name = "Clear Restrict View";
1261
ot->description = "Reveal the layer by setting the hide flag";
1262
ot->idname = "MASK_OT_hide_view_clear";
1265
ot->exec = mask_hide_view_clear_exec;
1266
ot->poll = ED_maskedit_mask_poll;
1269
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1272
static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1274
Mask *mask = CTX_data_edit_mask(C);
1276
const int unselected = RNA_boolean_get(op->ptr, "unselected");
1277
int changed = FALSE;
1279
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1281
if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1286
if (ED_mask_layer_select_check(masklay)) {
1287
ED_mask_layer_select_set(masklay, FALSE);
1289
masklay->restrictflag |= OB_RESTRICT_VIEW;
1291
if (masklay == BKE_mask_layer_active(mask)) {
1292
BKE_mask_layer_active_set(mask, NULL);
1297
if (!ED_mask_layer_select_check(masklay)) {
1298
masklay->restrictflag |= OB_RESTRICT_VIEW;
1300
if (masklay == BKE_mask_layer_active(mask)) {
1301
BKE_mask_layer_active_set(mask, NULL);
1308
WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1309
DAG_id_tag_update(&mask->id, 0);
1311
return OPERATOR_FINISHED;
1314
return OPERATOR_CANCELLED;
1318
void MASK_OT_hide_view_set(wmOperatorType *ot)
1321
ot->name = "Set Restrict View";
1322
ot->description = "Hide the layer by setting the hide flag";
1323
ot->idname = "MASK_OT_hide_view_set";
1326
ot->exec = mask_hide_view_set_exec;
1327
ot->poll = ED_maskedit_mask_poll;
1330
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1332
RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1336
static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1338
Scene *scene = CTX_data_scene(C);
1339
Mask *mask = CTX_data_edit_mask(C);
1341
int changed = FALSE;
1344
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1347
if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1351
for (spline = masklay->splines.first; spline; spline = spline->next) {
1352
for (i = 0; i < spline->tot_point; i++) {
1353
MaskSplinePoint *point = &spline->points[i];
1355
if (MASKPOINT_ISSEL_ANY(point)) {
1356
BezTriple *bezt = &point->bezt;
1357
bezt->weight = 0.0f;
1365
/* TODO: only update edited splines */
1366
BKE_mask_update_display(mask, CFRA);
1368
WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1369
DAG_id_tag_update(&mask->id, 0);
1371
return OPERATOR_FINISHED;
1374
return OPERATOR_CANCELLED;
1378
void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1381
ot->name = "Clear Feather Weight";
1382
ot->description = "Reset the feather weight to zero";
1383
ot->idname = "MASK_OT_feather_weight_clear";
1386
ot->exec = mask_feather_weight_clear_exec;
1387
ot->poll = ED_maskedit_mask_poll;
1390
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1393
/******************** move mask layer operator *********************/
1395
static int mask_layer_move_poll(bContext *C)
1397
if (ED_maskedit_mask_poll(C)) {
1398
Mask *mask = CTX_data_edit_mask(C);
1400
return mask->masklay_tot > 0;
1406
static int mask_layer_move_exec(bContext *C, wmOperator *op)
1408
Mask *mask = CTX_data_edit_mask(C);
1409
MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
1410
MaskLayer *mask_layer_other;
1411
int direction = RNA_enum_get(op->ptr, "direction");
1414
return OPERATOR_CANCELLED;
1416
if (direction == -1) {
1417
mask_layer_other = mask_layer->prev;
1419
if (!mask_layer_other)
1420
return OPERATOR_CANCELLED;
1422
BLI_remlink(&mask->masklayers, mask_layer);
1423
BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
1424
mask->masklay_act--;
1426
else if (direction == 1) {
1427
mask_layer_other = mask_layer->next;
1429
if (!mask_layer_other)
1430
return OPERATOR_CANCELLED;
1432
BLI_remlink(&mask->masklayers, mask_layer);
1433
BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
1434
mask->masklay_act++;
1437
return OPERATOR_FINISHED;
1440
void MASK_OT_layer_move(wmOperatorType *ot)
1442
static EnumPropertyItem direction_items[] = {
1443
{-1, "UP", 0, "Up", ""},
1444
{1, "DOWN", 0, "Down", ""},
1445
{0, NULL, 0, NULL, NULL}
1449
ot->name = "Move Layer";
1450
ot->description = "Move the active layer up/down in the list";
1451
ot->idname = "MASK_OT_layer_move";
1454
ot->exec = mask_layer_move_exec;
1455
ot->poll = mask_layer_move_poll;
1458
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1461
RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move the active layer");