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 static void mask_point_scaled_handle(/*const*/ MaskSplinePoint *point, /*const*/ eMaskWhichHandle which_handle,
63 const float scalex, const float scaley, float handle[2])
65 BKE_mask_point_handle(point, which_handle, handle);
70 MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask, const float normal_co[2], const float threshold,
71 MaskLayer **masklay_r, MaskSpline **spline_r,
72 eMaskWhichHandle *which_handle_r, float *score)
74 ScrArea *sa = CTX_wm_area(C);
75 ARegion *ar = CTX_wm_region(C);
78 MaskLayer *point_masklay = NULL;
79 MaskSpline *point_spline = NULL;
80 MaskSplinePoint *point = NULL;
82 const float threshold_sq = threshold * threshold;
83 float len_sq= FLT_MAX, scalex, scaley;
84 eMaskWhichHandle which_handle = MASK_WHICH_HANDLE_NONE;
87 ED_mask_get_size(sa, &width, &height);
88 ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
90 co[0] = normal_co[0] * scalex;
91 co[1] = normal_co[1] * scaley;
93 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
96 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
100 for (spline = masklay->splines.first; spline; spline = spline->next) {
101 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
105 for (i = 0; i < spline->tot_point; i++) {
106 MaskSplinePoint *cur_point = &spline->points[i];
107 MaskSplinePoint *cur_point_deform = &points_array[i];
108 eMaskWhichHandle cur_which_handle;
109 float cur_len_sq, vec[2];
111 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
112 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
114 cur_len_sq = len_squared_v2v2(co, vec);
116 if (cur_len_sq < len_sq) {
117 point_spline = spline;
118 point_masklay = masklay;
121 which_handle = MASK_WHICH_HANDLE_NONE;
124 if (BKE_mask_point_handles_mode_get(cur_point_deform) == MASK_HANDLE_MODE_STICK) {
126 mask_point_scaled_handle(cur_point_deform, MASK_WHICH_HANDLE_STICK, scalex, scaley, handle);
127 cur_len_sq = len_squared_v2v2(co, handle);
128 cur_which_handle = MASK_WHICH_HANDLE_STICK;
131 float handle_left[2], handle_right[2];
132 float len_left_sq, len_right_sq;
133 mask_point_scaled_handle(cur_point_deform, MASK_WHICH_HANDLE_LEFT, scalex, scaley, handle_left);
134 mask_point_scaled_handle(cur_point_deform, MASK_WHICH_HANDLE_RIGHT, scalex, scaley, handle_right);
136 len_left_sq = len_squared_v2v2(co, handle_left);
137 len_right_sq = len_squared_v2v2(co, handle_right);
139 if (len_left_sq <= len_right_sq) {
140 cur_which_handle = MASK_WHICH_HANDLE_LEFT;
141 cur_len_sq = len_left_sq;
144 cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
145 cur_len_sq = len_right_sq;
149 if (len_right_sq <= len_left_sq) {
150 cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
151 cur_len_sq = len_right_sq;
154 cur_which_handle = MASK_WHICH_HANDLE_LEFT;
155 cur_len_sq = len_left_sq;
160 if (cur_len_sq <= len_sq) {
161 point_masklay = masklay;
162 point_spline = spline;
165 which_handle = cur_which_handle;
171 if (len_sq < threshold_sq) {
173 *masklay_r = point_masklay;
176 *spline_r = point_spline;
179 *which_handle_r = which_handle;
182 *score = sqrtf(len_sq);
194 *which_handle_r = MASK_WHICH_HANDLE_NONE;
199 bool ED_mask_feather_find_nearest(const bContext *C, Mask *mask, const float normal_co[2], const float threshold,
200 MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
201 MaskSplinePointUW **uw_r, float *score)
203 ScrArea *sa = CTX_wm_area(C);
204 ARegion *ar = CTX_wm_region(C);
206 MaskLayer *masklay, *point_masklay = NULL;
207 MaskSpline *point_spline = NULL;
208 MaskSplinePoint *point = NULL;
209 MaskSplinePointUW *uw = NULL;
210 const float threshold_sq = threshold * threshold;
211 float len = FLT_MAX, co[2];
212 float scalex, scaley;
215 ED_mask_get_size(sa, &width, &height);
216 ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
218 co[0] = normal_co[0] * scalex;
219 co[1] = normal_co[1] * scaley;
221 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
224 for (spline = masklay->splines.first; spline; spline = spline->next) {
225 //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
227 int i, tot_feather_point;
228 float (*feather_points)[2], (*fp)[2];
230 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
234 feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
236 for (i = 0; i < spline->tot_point; i++) {
238 MaskSplinePoint *cur_point = &spline->points[i];
240 for (j = 0; j <= cur_point->tot_uw; j++) {
241 float cur_len_sq, vec[2];
243 vec[0] = (*fp)[0] * scalex;
244 vec[1] = (*fp)[1] * scaley;
246 cur_len_sq = len_squared_v2v2(vec, co);
248 if (point == NULL || cur_len_sq < len) {
252 uw = &cur_point->uw[j - 1];
254 point_masklay = masklay;
255 point_spline = spline;
264 MEM_freeN(feather_points);
268 if (len < threshold_sq) {
270 *masklay_r = point_masklay;
273 *spline_r = point_spline;
300 /******************** create new mask *********************/
302 Mask *ED_mask_new(bContext *C, const char *name)
304 ScrArea *sa = CTX_wm_area(C);
305 Main *bmain = CTX_data_main(C);
308 mask = BKE_mask_new(bmain, name);
310 if (sa && sa->spacedata.first) {
311 switch (sa->spacetype) {
314 SpaceClip *sc = sa->spacedata.first;
315 ED_space_clip_set_mask(C, sc, mask);
325 SpaceImage *sima = sa->spacedata.first;
326 ED_space_image_set_mask(C, sima, mask);
335 /* Get ative layer. Will create mask/layer to be sure there's an active layer. */
336 MaskLayer *ED_mask_layer_ensure(bContext *C)
338 Mask *mask = CTX_data_edit_mask(C);
339 MaskLayer *mask_layer;
342 /* If there's no active mask, create one. */
343 mask = ED_mask_new(C, NULL);
346 mask_layer = BKE_mask_layer_active(mask);
347 if (mask_layer == NULL) {
348 /* If there's no active mask layer, create one. */
349 mask_layer = BKE_mask_layer_new(mask, "");
355 static int mask_new_exec(bContext *C, wmOperator *op)
357 char name[MAX_ID_NAME - 2];
359 RNA_string_get(op->ptr, "name", name);
361 ED_mask_new(C, name);
363 return OPERATOR_FINISHED;
366 void MASK_OT_new(wmOperatorType *ot)
369 ot->name = "New Mask";
370 ot->description = "Create new mask";
371 ot->idname = "MASK_OT_new";
374 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
377 ot->exec = mask_new_exec;
378 ot->poll = ED_operator_mask;
381 RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask");
384 /******************** create new masklay *********************/
386 static int masklay_new_exec(bContext *C, wmOperator *op)
388 Mask *mask = CTX_data_edit_mask(C);
389 char name[MAX_ID_NAME - 2];
391 RNA_string_get(op->ptr, "name", name);
393 BKE_mask_layer_new(mask, name);
394 mask->masklay_act = mask->masklay_tot - 1;
396 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
398 return OPERATOR_FINISHED;
401 void MASK_OT_layer_new(wmOperatorType *ot)
404 ot->name = "Add Mask Layer";
405 ot->description = "Add new mask layer for masking";
406 ot->idname = "MASK_OT_layer_new";
409 ot->exec = masklay_new_exec;
410 ot->poll = ED_maskedit_poll;
413 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
416 RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask layer");
419 /******************** remove mask layer *********************/
421 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
423 Mask *mask = CTX_data_edit_mask(C);
424 MaskLayer *masklay = BKE_mask_layer_active(mask);
427 BKE_mask_layer_remove(mask, masklay);
429 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
432 return OPERATOR_FINISHED;
435 void MASK_OT_layer_remove(wmOperatorType *ot)
438 ot->name = "Remove Mask Layer";
439 ot->description = "Remove mask layer";
440 ot->idname = "MASK_OT_layer_remove";
443 ot->exec = masklay_remove_exec;
444 ot->poll = ED_maskedit_poll;
447 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
450 /******************** slide *********************/
453 SLIDE_ACTION_NONE = 0,
454 SLIDE_ACTION_POINT = 1,
455 SLIDE_ACTION_HANDLE = 2,
456 SLIDE_ACTION_FEATHER = 3
459 typedef struct SlidePointData {
468 MaskSpline *spline, *orig_spline;
469 MaskSplinePoint *point;
470 MaskSplinePointUW *uw;
471 eMaskWhichHandle which_handle;
472 float handle[2], no[2], feather[2];
474 float weight, weight_scalar;
476 short curvature_only, accurate;
477 short initial_feather, overall_feather;
479 bool is_sliding_new_point;
482 static bool slide_point_check_initial_feather(MaskSpline *spline)
486 for (i = 0; i < spline->tot_point; i++) {
487 MaskSplinePoint *point = &spline->points[i];
489 if (point->bezt.weight != 0.0f)
492 /* comment for now. if all bezt weights are zero - this is as good-as initial */
495 for (j = 0; j < point->tot_uw; j++) {
496 if (point->uw[j].w != 0.0f)
505 static void select_sliding_point(Mask *mask, MaskLayer *mask_layer, MaskSpline *spline,
506 MaskSplinePoint *point)
508 ED_mask_select_toggle_all(mask, SEL_DESELECT);
509 BKE_mask_point_select_set(point, TRUE);
511 mask_layer->act_spline = spline;
512 mask_layer->act_point = point;
513 ED_mask_select_flush_all(mask);
516 static void check_sliding_handle_type(MaskSplinePoint *point, eMaskWhichHandle which_handle)
518 BezTriple *bezt = &point->bezt;
520 if (which_handle == MASK_WHICH_HANDLE_LEFT) {
521 if (bezt->h1 == HD_VECT) {
524 else if (bezt->h1 == HD_AUTO) {
525 bezt->h1 = HD_ALIGN_DOUBLESIDE;
526 bezt->h2 = HD_ALIGN_DOUBLESIDE;
529 else if (which_handle == MASK_WHICH_HANDLE_RIGHT) {
530 if (bezt->h2 == HD_VECT) {
533 else if (bezt->h2 == HD_AUTO) {
534 bezt->h1 = HD_ALIGN_DOUBLESIDE;
535 bezt->h2 = HD_ALIGN_DOUBLESIDE;
540 static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *event)
542 ScrArea *sa = CTX_wm_area(C);
543 ARegion *ar = CTX_wm_region(C);
545 Mask *mask = CTX_data_edit_mask(C);
546 SlidePointData *customdata = NULL;
547 MaskLayer *masklay, *cv_masklay, *feather_masklay;
548 MaskSpline *spline, *cv_spline, *feather_spline;
549 MaskSplinePoint *point, *cv_point, *feather_point;
550 MaskSplinePointUW *uw = NULL;
551 int width, height, action = SLIDE_ACTION_NONE;
552 const bool slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
553 float co[2], cv_score, feather_score;
554 const float threshold = 19;
555 eMaskWhichHandle which_handle;
557 ED_mask_mouse_pos(sa, ar, event->mval, co);
558 ED_mask_get_size(sa, &width, &height);
560 cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &which_handle, &cv_score);
562 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
563 if (slide_feather || !cv_point || feather_score < cv_score) {
564 action = SLIDE_ACTION_FEATHER;
566 masklay = feather_masklay;
567 spline = feather_spline;
568 point = feather_point;
572 if (cv_point && action == SLIDE_ACTION_NONE) {
573 if (which_handle != MASK_WHICH_HANDLE_NONE)
574 action = SLIDE_ACTION_HANDLE;
576 action = SLIDE_ACTION_POINT;
578 masklay = cv_masklay;
583 if (action != SLIDE_ACTION_NONE) {
584 select_sliding_point(mask, masklay, spline, point);
586 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
588 customdata->mask = mask;
589 customdata->masklay = masklay;
590 customdata->spline = spline;
591 customdata->point = point;
592 customdata->width = width;
593 customdata->height = height;
594 customdata->action = action;
597 customdata->old_h1 = point->bezt.h1;
598 customdata->old_h2 = point->bezt.h2;
600 customdata->is_sliding_new_point = RNA_boolean_get(op->ptr, "is_new_point");
602 check_sliding_handle_type(point, which_handle);
606 float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
608 customdata->weight = uw->w;
609 customdata->weight_scalar = weight_scalar;
610 BKE_mask_point_segment_co(spline, point, uw->u, co_uw);
611 BKE_mask_point_normal(spline, point, uw->u, customdata->no);
613 madd_v2_v2v2fl(customdata->feather, co_uw, customdata->no, uw->w * weight_scalar);
616 BezTriple *bezt = &point->bezt;
618 customdata->weight = bezt->weight;
619 customdata->weight_scalar = 1.0f;
620 BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
622 madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
625 if (customdata->action == SLIDE_ACTION_FEATHER)
626 customdata->initial_feather = slide_point_check_initial_feather(spline);
628 copy_m3_m3(customdata->vec, point->bezt.vec);
629 if (which_handle != MASK_WHICH_HANDLE_NONE) {
630 BKE_mask_point_handle(point, which_handle, customdata->handle);
632 customdata->which_handle = which_handle;
633 ED_mask_mouse_pos(sa, ar, event->mval, customdata->co);
639 static int slide_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
641 Mask *mask = CTX_data_edit_mask(C);
642 SlidePointData *slidedata;
645 return OPERATOR_CANCELLED;
648 slidedata = slide_point_customdata(C, op, event);
651 op->customdata = slidedata;
653 WM_event_add_modal_handler(C, op);
657 if ((slidedata->uw->flag & SELECT) == 0) {
658 ED_mask_select_toggle_all(mask, SEL_DESELECT);
660 slidedata->uw->flag |= SELECT;
662 ED_mask_select_flush_all(mask);
665 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
666 ED_mask_select_toggle_all(mask, SEL_DESELECT);
668 BKE_mask_point_select_set(slidedata->point, TRUE);
670 ED_mask_select_flush_all(mask);
674 slidedata->masklay->act_spline = slidedata->spline;
675 slidedata->masklay->act_point = slidedata->point;
677 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
679 return OPERATOR_RUNNING_MODAL;
682 return OPERATOR_PASS_THROUGH;
685 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
689 for (i = 0; i < data->spline->tot_point; i++) {
690 MaskSplinePoint *point = &data->spline->points[i];
691 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
693 point->bezt.weight = orig_point->bezt.weight + delta;
694 if (point->bezt.weight < 0.0f)
695 point->bezt.weight = 0.0f;
697 /* not needed anymore */
700 for (j = 0; j < point->tot_uw; j++) {
701 point->uw[j].w = orig_point->uw[j].w + delta;
702 if (point->uw[j].w < 0.0f)
703 point->uw[j].w = 0.0f;
709 static void slide_point_restore_spline(SlidePointData *data)
713 for (i = 0; i < data->spline->tot_point; i++) {
714 MaskSplinePoint *point = &data->spline->points[i];
715 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
718 point->bezt = orig_point->bezt;
720 for (j = 0; j < point->tot_uw; j++)
721 point->uw[j] = orig_point->uw[j];
725 static void cancel_slide_point(SlidePointData *data)
729 if (data->orig_spline) {
730 slide_point_restore_spline(data);
733 if (data->action == SLIDE_ACTION_FEATHER) {
735 data->uw->w = data->weight;
737 data->point->bezt.weight = data->weight;
740 copy_m3_m3(data->point->bezt.vec, data->vec);
741 data->point->bezt.h1 = data->old_h1;
742 data->point->bezt.h2 = data->old_h2;
747 static void free_slide_point_data(SlidePointData *data)
749 if (data->orig_spline)
750 BKE_mask_spline_free(data->orig_spline);
755 static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
757 SlidePointData *data = (SlidePointData *)op->customdata;
758 BezTriple *bezt = &data->point->bezt;
761 switch (event->type) {
766 if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) {
767 if (data->action == SLIDE_ACTION_FEATHER)
768 data->overall_feather = (event->val == KM_PRESS);
770 data->curvature_only = (event->val == KM_PRESS);
773 if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
774 data->accurate = (event->val == KM_PRESS);
776 /* fall-through */ /* update CV position */
779 ScrArea *sa = CTX_wm_area(C);
780 ARegion *ar = CTX_wm_region(C);
782 ED_mask_mouse_pos(sa, ar, event->mval, co);
783 sub_v2_v2v2(dco, co, data->co);
785 if (data->action == SLIDE_ACTION_HANDLE) {
786 float delta[2], offco[2];
788 sub_v2_v2v2(delta, data->handle, data->co);
790 sub_v2_v2v2(offco, co, data->co);
792 mul_v2_fl(offco, 0.2f);
794 if (data->is_sliding_new_point && data->which_handle == MASK_WHICH_HANDLE_STICK) {
795 if (ELEM(data->point, &data->spline->points[0],
796 &data->spline->points[data->spline->tot_point - 1]))
798 SWAP(float, offco[0], offco[1]);
803 add_v2_v2(offco, data->co);
804 add_v2_v2(offco, delta);
806 BKE_mask_point_set_handle(data->point, data->which_handle,
807 offco, data->curvature_only,
808 data->handle, data->vec);
810 if (data->is_sliding_new_point) {
811 if (ELEM(data->which_handle, MASK_WHICH_HANDLE_LEFT, MASK_WHICH_HANDLE_RIGHT)) {
812 BezTriple *bezt = &data->point->bezt;
814 short self_handle = (data->which_handle == MASK_WHICH_HANDLE_LEFT) ? 0 : 2;
815 short other_handle = (data->which_handle == MASK_WHICH_HANDLE_LEFT) ? 2 : 0;
817 sub_v2_v2v2(vec, bezt->vec[1], bezt->vec[self_handle]);
818 add_v2_v2v2(bezt->vec[other_handle], bezt->vec[1], vec);
822 else if (data->action == SLIDE_ACTION_POINT) {
825 copy_v2_v2(delta, dco);
827 mul_v2_fl(delta, 0.2f);
829 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
830 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
831 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
833 else if (data->action == SLIDE_ACTION_FEATHER) {
834 float vec[2], no[2], p[2], c[2], w, offco[2];
835 float *weight = NULL;
836 float weight_scalar = 1.0f;
837 int overall_feather = data->overall_feather || data->initial_feather;
839 add_v2_v2v2(offco, data->feather, dco);
842 /* project on both sides and find the closest one,
843 * prevents flickering when projecting onto both sides can happen */
844 const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
845 data->uw->u, offco, MASK_PROJ_NEG);
846 const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
847 data->uw->u, offco, MASK_PROJ_POS);
848 float dist_pos = FLT_MAX;
849 float dist_neg = FLT_MAX;
854 if (u_pos > 0.0f && u_pos < 1.0f) {
855 BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
856 dist_pos = len_squared_v2v2(offco, co_pos);
859 if (u_neg > 0.0f && u_neg < 1.0f) {
860 BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
861 dist_neg = len_squared_v2v2(offco, co_neg);
864 u = dist_pos < dist_neg ? u_pos : u_neg;
866 if (u > 0.0f && u < 1.0f) {
869 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
870 weight = &data->uw->w;
871 weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
872 if (weight_scalar != 0.0f) {
873 weight_scalar = 1.0f / weight_scalar;
876 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
877 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
881 weight = &bezt->weight;
882 /* weight_scalar = 1.0f; keep as is */
883 copy_v2_v2(no, data->no);
884 copy_v2_v2(p, bezt->vec[1]);
888 sub_v2_v2v2(c, offco, p);
889 project_v2_v2v2(vec, c, no);
893 if (overall_feather) {
896 if (dot_v2v2(no, vec) <= 0.0f)
899 delta = w - data->weight * data->weight_scalar;
901 if (data->orig_spline == NULL) {
902 /* restore weight for currently sliding point, so orig_spline would be created
903 * with original weights used
905 *weight = data->weight;
907 data->orig_spline = BKE_mask_spline_copy(data->spline);
910 slide_point_delta_all_feather(data, delta);
913 if (dot_v2v2(no, vec) <= 0.0f)
916 if (data->orig_spline) {
917 /* restore possible overall feather changes */
918 slide_point_restore_spline(data);
920 BKE_mask_spline_free(data->orig_spline);
921 data->orig_spline = NULL;
924 if (weight_scalar != 0.0f) {
925 *weight = w * weight_scalar;
931 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
932 DAG_id_tag_update(&data->mask->id, 0);
938 if (event->val == KM_RELEASE) {
939 Scene *scene = CTX_data_scene(C);
941 /* dont key sliding feather uw's */
942 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
943 if (IS_AUTOKEY_ON(scene)) {
944 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
948 if (data->is_sliding_new_point) {
949 BezTriple *bezt = &data->point->bezt;
950 if (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) < FLT_EPSILON) {
953 if (len_squared_v2v2(bezt->vec[2], bezt->vec[1]) < FLT_EPSILON) {
958 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
959 DAG_id_tag_update(&data->mask->id, 0);
961 free_slide_point_data(op->customdata); /* keep this last! */
962 return OPERATOR_FINISHED;
968 cancel_slide_point(op->customdata);
970 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
971 DAG_id_tag_update(&data->mask->id, 0);
973 free_slide_point_data(op->customdata); /* keep this last! */
974 return OPERATOR_CANCELLED;
977 return OPERATOR_RUNNING_MODAL;
980 void MASK_OT_slide_point(wmOperatorType *ot)
985 ot->name = "Slide Point";
986 ot->description = "Slide control points";
987 ot->idname = "MASK_OT_slide_point";
990 ot->invoke = slide_point_invoke;
991 ot->modal = slide_point_modal;
992 ot->poll = ED_operator_mask;
995 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
997 RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
999 prop = RNA_def_boolean(ot->srna, "is_new_point", 0, "Slide New Point", "Newly created vertex is being slided");
1000 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1003 /******************** toggle cyclic *********************/
1005 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1007 Mask *mask = CTX_data_edit_mask(C);
1010 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1013 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1017 for (spline = masklay->splines.first; spline; spline = spline->next) {
1018 if (ED_mask_spline_select_check(spline)) {
1019 spline->flag ^= MASK_SPLINE_CYCLIC;
1024 DAG_id_tag_update(&mask->id, 0);
1025 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1027 return OPERATOR_FINISHED;
1030 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
1033 ot->name = "Toggle Cyclic";
1034 ot->description = "Toggle cyclic for selected splines";
1035 ot->idname = "MASK_OT_cyclic_toggle";
1038 ot->exec = cyclic_toggle_exec;
1039 ot->poll = ED_maskedit_mask_poll;
1042 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1045 /******************** delete *********************/
1047 static void delete_feather_points(MaskSplinePoint *point)
1054 for (i = 0; i < point->tot_uw; i++) {
1055 if ((point->uw[i].flag & SELECT) == 0)
1060 MEM_freeN(point->uw);
1065 MaskSplinePointUW *new_uw;
1068 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
1070 for (i = 0; i < point->tot_uw; i++) {
1071 if ((point->uw[i].flag & SELECT) == 0) {
1072 new_uw[j++] = point->uw[i];
1076 MEM_freeN(point->uw);
1079 point->tot_uw = count;
1083 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
1085 Scene *scene = CTX_data_scene(C);
1086 Mask *mask = CTX_data_edit_mask(C);
1088 bool changed = false;
1090 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1092 int mask_layer_shape_ofs = 0;
1094 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1098 spline = masklay->splines.first;
1101 const int tot_point_orig = spline->tot_point;
1103 MaskSpline *next_spline = spline->next;
1105 /* count unselected points */
1106 for (i = 0; i < spline->tot_point; i++) {
1107 MaskSplinePoint *point = &spline->points[i];
1109 if (!MASKPOINT_ISSEL_ANY(point))
1114 /* delete the whole spline */
1115 BLI_remlink(&masklay->splines, spline);
1116 BKE_mask_spline_free(spline);
1118 if (spline == masklay->act_spline) {
1119 masklay->act_spline = NULL;
1120 masklay->act_point = NULL;
1123 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
1126 MaskSplinePoint *new_points;
1129 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
1131 for (i = 0, j = 0; i < tot_point_orig; i++) {
1132 MaskSplinePoint *point = &spline->points[i];
1134 if (!MASKPOINT_ISSEL_ANY(point)) {
1135 if (point == masklay->act_point)
1136 masklay->act_point = &new_points[j];
1138 delete_feather_points(point);
1140 new_points[j] = *point;
1144 if (point == masklay->act_point)
1145 masklay->act_point = NULL;
1147 BKE_mask_point_free(point);
1148 spline->tot_point--;
1150 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
1154 mask_layer_shape_ofs += spline->tot_point;
1156 MEM_freeN(spline->points);
1157 spline->points = new_points;
1159 ED_mask_select_flush_all(mask);
1163 spline = next_spline;
1166 /* not essential but confuses users when there are keys with no data!
1167 * assume if they delete all data from the layer they also dont care about keys */
1168 if (BLI_listbase_is_empty(&masklay->splines)) {
1169 BKE_mask_layer_free_shapes(masklay);
1174 return OPERATOR_CANCELLED;
1177 /* TODO: only update edited splines */
1178 BKE_mask_update_display(mask, CFRA);
1180 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1182 return OPERATOR_FINISHED;
1185 void MASK_OT_delete(wmOperatorType *ot)
1188 ot->name = "Delete";
1189 ot->description = "Delete selected control points or splines";
1190 ot->idname = "MASK_OT_delete";
1193 ot->invoke = WM_operator_confirm;
1194 ot->exec = delete_exec;
1195 ot->poll = ED_maskedit_mask_poll;
1198 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1201 /* *** switch direction *** */
1202 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
1204 Scene *scene = CTX_data_scene(C);
1205 Mask *mask = CTX_data_edit_mask(C);
1208 bool changed = false;
1210 /* do actual selection */
1211 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1213 bool changed_layer = false;
1215 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1219 for (spline = masklay->splines.first; spline; spline = spline->next) {
1220 if (ED_mask_spline_select_check(spline)) {
1221 BKE_mask_spline_direction_switch(masklay, spline);
1223 changed_layer = true;
1227 if (changed_layer) {
1228 if (IS_AUTOKEY_ON(scene)) {
1229 ED_mask_layer_shape_auto_key(masklay, CFRA);
1235 /* TODO: only update this spline */
1236 BKE_mask_update_display(mask, CFRA);
1238 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1239 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1241 return OPERATOR_FINISHED;
1244 return OPERATOR_CANCELLED;
1247 void MASK_OT_switch_direction(wmOperatorType *ot)
1250 ot->name = "Switch Direction";
1251 ot->description = "Switch direction of selected splines";
1252 ot->idname = "MASK_OT_switch_direction";
1255 ot->exec = mask_switch_direction_exec;
1256 ot->poll = ED_maskedit_mask_poll;
1259 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1263 /* *** recalc normals *** */
1264 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1266 Scene *scene = CTX_data_scene(C);
1267 Mask *mask = CTX_data_edit_mask(C);
1271 bool changed = false;
1273 /* do actual selection */
1274 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1276 bool changed_layer = false;
1278 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1282 for (spline = masklay->splines.first; spline; spline = spline->next) {
1283 for (i = 0; i < spline->tot_point; i++) {
1284 MaskSplinePoint *point = &spline->points[i];
1286 if (MASKPOINT_ISSEL_ANY(point)) {
1287 BKE_mask_calc_handle_point_auto(spline, point, FALSE);
1289 changed_layer = true;
1294 if (changed_layer) {
1295 if (IS_AUTOKEY_ON(scene)) {
1296 ED_mask_layer_shape_auto_key(masklay, CFRA);
1302 /* TODO: only update this spline */
1303 BKE_mask_update_display(mask, CFRA);
1305 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1306 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1308 return OPERATOR_FINISHED;
1311 return OPERATOR_CANCELLED;
1314 /* named to match mesh recalc normals */
1315 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1318 ot->name = "Recalc Normals";
1319 ot->description = "Re-calculate the direction of selected handles";
1320 ot->idname = "MASK_OT_normals_make_consistent";
1323 ot->exec = mask_normals_make_consistent_exec;
1324 ot->poll = ED_maskedit_mask_poll;
1327 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1331 /******************** set handle type *********************/
1333 static int set_handle_type_exec(bContext *C, wmOperator *op)
1335 Mask *mask = CTX_data_edit_mask(C);
1337 int handle_type = RNA_enum_get(op->ptr, "type");
1339 bool changed = false;
1341 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1345 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1349 for (spline = masklay->splines.first; spline; spline = spline->next) {
1350 for (i = 0; i < spline->tot_point; i++) {
1351 MaskSplinePoint *point = &spline->points[i];
1353 if (MASKPOINT_ISSEL_ANY(point)) {
1354 BezTriple *bezt = &point->bezt;
1356 if (bezt->f2 & SELECT) {
1357 bezt->h1 = handle_type;
1358 bezt->h2 = handle_type;
1361 if (bezt->f1 & SELECT) {
1362 bezt->h1 = handle_type;
1364 if (bezt->f3 & SELECT) {
1365 bezt->h2 = handle_type;
1369 if (handle_type == HD_ALIGN) {
1371 sub_v3_v3v3(vec, bezt->vec[0], bezt->vec[1]);
1372 add_v3_v3v3(bezt->vec[2], bezt->vec[1], vec);
1382 WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1383 DAG_id_tag_update(&mask->id, 0);
1385 return OPERATOR_FINISHED;
1387 return OPERATOR_CANCELLED;
1390 void MASK_OT_handle_type_set(wmOperatorType *ot)
1392 static EnumPropertyItem editcurve_handle_type_items[] = {
1393 {HD_AUTO, "AUTO", 0, "Auto", ""},
1394 {HD_VECT, "VECTOR", 0, "Vector", ""},
1395 {HD_ALIGN, "ALIGNED", 0, "Aligned Single", ""},
1396 {HD_ALIGN_DOUBLESIDE, "ALIGNED_DOUBLESIDE", 0, "Aligned", ""},
1397 {HD_FREE, "FREE", 0, "Free", ""},
1398 {0, NULL, 0, NULL, NULL}
1402 ot->name = "Set Handle Type";
1403 ot->description = "Set type of handles for selected control points";
1404 ot->idname = "MASK_OT_handle_type_set";
1407 ot->invoke = WM_menu_invoke;
1408 ot->exec = set_handle_type_exec;
1409 ot->poll = ED_maskedit_mask_poll;
1412 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1415 ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1419 /* ********* clear/set restrict view *********/
1420 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1422 Mask *mask = CTX_data_edit_mask(C);
1424 bool changed = false;
1426 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1428 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1429 ED_mask_layer_select_set(masklay, TRUE);
1430 masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1436 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1437 DAG_id_tag_update(&mask->id, 0);
1439 return OPERATOR_FINISHED;
1442 return OPERATOR_CANCELLED;
1446 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1450 ot->name = "Clear Restrict View";
1451 ot->description = "Reveal the layer by setting the hide flag";
1452 ot->idname = "MASK_OT_hide_view_clear";
1455 ot->exec = mask_hide_view_clear_exec;
1456 ot->poll = ED_maskedit_mask_poll;
1459 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1462 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1464 Mask *mask = CTX_data_edit_mask(C);
1466 const int unselected = RNA_boolean_get(op->ptr, "unselected");
1467 bool changed = false;
1469 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1471 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1476 if (ED_mask_layer_select_check(masklay)) {
1477 ED_mask_layer_select_set(masklay, FALSE);
1479 masklay->restrictflag |= OB_RESTRICT_VIEW;
1481 if (masklay == BKE_mask_layer_active(mask)) {
1482 BKE_mask_layer_active_set(mask, NULL);
1487 if (!ED_mask_layer_select_check(masklay)) {
1488 masklay->restrictflag |= OB_RESTRICT_VIEW;
1490 if (masklay == BKE_mask_layer_active(mask)) {
1491 BKE_mask_layer_active_set(mask, NULL);
1498 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1499 DAG_id_tag_update(&mask->id, 0);
1501 return OPERATOR_FINISHED;
1504 return OPERATOR_CANCELLED;
1508 void MASK_OT_hide_view_set(wmOperatorType *ot)
1511 ot->name = "Set Restrict View";
1512 ot->description = "Hide the layer by setting the hide flag";
1513 ot->idname = "MASK_OT_hide_view_set";
1516 ot->exec = mask_hide_view_set_exec;
1517 ot->poll = ED_maskedit_mask_poll;
1520 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1522 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1526 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1528 Scene *scene = CTX_data_scene(C);
1529 Mask *mask = CTX_data_edit_mask(C);
1531 bool changed = false;
1534 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1537 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1541 for (spline = masklay->splines.first; spline; spline = spline->next) {
1542 for (i = 0; i < spline->tot_point; i++) {
1543 MaskSplinePoint *point = &spline->points[i];
1545 if (MASKPOINT_ISSEL_ANY(point)) {
1546 BezTriple *bezt = &point->bezt;
1547 bezt->weight = 0.0f;
1555 /* TODO: only update edited splines */
1556 BKE_mask_update_display(mask, CFRA);
1558 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1559 DAG_id_tag_update(&mask->id, 0);
1561 return OPERATOR_FINISHED;
1564 return OPERATOR_CANCELLED;
1568 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1571 ot->name = "Clear Feather Weight";
1572 ot->description = "Reset the feather weight to zero";
1573 ot->idname = "MASK_OT_feather_weight_clear";
1576 ot->exec = mask_feather_weight_clear_exec;
1577 ot->poll = ED_maskedit_mask_poll;
1580 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1583 /******************** move mask layer operator *********************/
1585 static int mask_layer_move_poll(bContext *C)
1587 if (ED_maskedit_mask_poll(C)) {
1588 Mask *mask = CTX_data_edit_mask(C);
1590 return mask->masklay_tot > 0;
1596 static int mask_layer_move_exec(bContext *C, wmOperator *op)
1598 Mask *mask = CTX_data_edit_mask(C);
1599 MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
1600 MaskLayer *mask_layer_other;
1601 int direction = RNA_enum_get(op->ptr, "direction");
1604 return OPERATOR_CANCELLED;
1606 if (direction == -1) {
1607 mask_layer_other = mask_layer->prev;
1609 if (!mask_layer_other)
1610 return OPERATOR_CANCELLED;
1612 BLI_remlink(&mask->masklayers, mask_layer);
1613 BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
1614 mask->masklay_act--;
1616 else if (direction == 1) {
1617 mask_layer_other = mask_layer->next;
1619 if (!mask_layer_other)
1620 return OPERATOR_CANCELLED;
1622 BLI_remlink(&mask->masklayers, mask_layer);
1623 BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
1624 mask->masklay_act++;
1627 return OPERATOR_FINISHED;
1630 void MASK_OT_layer_move(wmOperatorType *ot)
1632 static EnumPropertyItem direction_items[] = {
1633 {-1, "UP", 0, "Up", ""},
1634 {1, "DOWN", 0, "Down", ""},
1635 {0, NULL, 0, NULL, NULL}
1639 ot->name = "Move Layer";
1640 ot->description = "Move the active layer up/down in the list";
1641 ot->idname = "MASK_OT_layer_move";
1644 ot->exec = mask_layer_move_exec;
1645 ot->poll = mask_layer_move_poll;
1648 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1651 RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move the active layer");
1654 /******************** duplicate *********************/
1656 static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
1658 Scene *scene = CTX_data_scene(C);
1659 Mask *mask = CTX_data_edit_mask(C);
1660 MaskLayer *mask_layer;
1662 for (mask_layer = mask->masklayers.first;
1664 mask_layer = mask_layer->next)
1668 for (spline = mask_layer->splines.last;
1670 spline = spline->prev)
1672 MaskSplinePoint *point = spline->points;
1674 while (i < spline->tot_point) {
1675 int start = i, end = -1;
1676 /* Find next selected segment. */
1677 while (MASKPOINT_ISSEL_ANY(point)) {
1678 BKE_mask_point_select_set(point, false);
1680 if (i >= spline->tot_point - 1) {
1687 MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
1688 MaskSplinePoint *new_point;
1691 /* BKE_mask_spline_add might allocate the points, need to free them in this case. */
1692 if (new_spline->points) {
1693 MEM_freeN(new_spline->points);
1696 /* Copy options from old spline. */
1697 new_spline->flag = spline->flag;
1698 new_spline->offset_mode = spline->offset_mode;
1699 new_spline->weight_interp = spline->weight_interp;
1700 new_spline->parent = spline->parent;
1702 /* Allocate new points and copy them from old spline. */
1703 new_spline->tot_point = end - start + 1;
1704 new_spline->points = MEM_mallocN(sizeof(MaskSplinePoint) * new_spline->tot_point,
1705 "duplicated mask points");
1707 memcpy(new_spline->points, spline->points + start,
1708 new_spline->tot_point * sizeof(MaskSplinePoint));
1710 /* Select points and duplicate their UWs (if needed). */
1711 for (b = 0, new_point = new_spline->points;
1712 b < new_spline->tot_point;
1715 if (new_point->uw) {
1716 new_point->uw = MEM_dupallocN(new_point->uw);
1718 BKE_mask_point_select_set(new_point, true);
1721 /* Clear cyclic flag if we didn't copy the whole spline. */
1722 if (new_spline->flag & MASK_SPLINE_CYCLIC) {
1723 if (start != 0 || end != spline->tot_point - 1) {
1724 new_spline->flag &= ~MASK_SPLINE_CYCLIC;
1728 /* Flush selection to splines. */
1729 new_spline->flag |= SELECT;
1730 spline->flag &= ~SELECT;
1732 mask_layer->act_spline = new_spline;
1740 /* TODO: only update edited splines */
1741 BKE_mask_update_display(mask, CFRA);
1743 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1745 return OPERATOR_FINISHED;
1748 void MASK_OT_duplicate(wmOperatorType *ot)
1751 ot->name = "Duplicate Mask";
1752 ot->description = "Duplicate selected control points and segments between them";
1753 ot->idname = "MASK_OT_duplicate";
1756 ot->exec = mask_duplicate_exec;
1757 ot->poll = ED_maskedit_mask_poll;
1760 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1763 /********************** copy splines to clipboard operator *********************/
1765 static int copy_splines_exec(bContext *C, wmOperator *UNUSED(op))
1767 Mask *mask = CTX_data_edit_mask(C);
1768 MaskLayer *mask_layer = BKE_mask_layer_active(mask);
1770 BKE_mask_clipboard_copy_from_layer(mask_layer);
1772 return OPERATOR_FINISHED;
1775 void MASK_OT_copy_splines(wmOperatorType *ot)
1778 ot->name = "Copy Splines";
1779 ot->description = "Copy selected splines to clipboard";
1780 ot->idname = "MASK_OT_copy_splines";
1783 ot->exec = copy_splines_exec;
1784 ot->poll = ED_maskedit_mask_poll;
1787 ot->flag = OPTYPE_REGISTER;
1790 /********************** paste tracks from clipboard operator *********************/
1792 static int paste_splines_poll(bContext *C)
1794 if (ED_maskedit_mask_poll(C)) {
1795 return BKE_mask_clipboard_is_empty() == false;
1801 static int paste_splines_exec(bContext *C, wmOperator *UNUSED(op))
1803 Scene *scene = CTX_data_scene(C);
1804 Mask *mask = CTX_data_edit_mask(C);
1805 MaskLayer *mask_layer = BKE_mask_layer_active(mask);
1807 BKE_mask_clipboard_paste_to_layer(CTX_data_main(C), mask_layer);
1809 /* TODO: only update edited splines */
1810 BKE_mask_update_display(mask, CFRA);
1812 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1814 return OPERATOR_FINISHED;
1817 void MASK_OT_paste_splines(wmOperatorType *ot)
1820 ot->name = "Paste Splines";
1821 ot->description = "Paste splines from clipboard";
1822 ot->idname = "MASK_OT_paste_splines";
1825 ot->exec = paste_splines_exec;
1826 ot->poll = paste_splines_poll;
1829 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;