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"
41 #include "DEG_depsgraph.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_mask_types.h"
45 #include "DNA_object_types.h" /* SELECT */
52 #include "ED_keyframing.h"
54 #include "ED_screen.h"
56 #include "RNA_access.h"
57 #include "RNA_define.h"
59 #include "mask_intern.h" /* own include */
61 /******************** utility functions *********************/
63 static void mask_point_scaled_handle(/*const*/ MaskSplinePoint *point, /*const*/ eMaskWhichHandle which_handle,
64 const float scalex, const float scaley, float handle[2])
66 BKE_mask_point_handle(point, which_handle, handle);
71 MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask, const float normal_co[2], const float threshold,
72 MaskLayer **masklay_r, MaskSpline **spline_r,
73 eMaskWhichHandle *which_handle_r, float *score)
75 ScrArea *sa = CTX_wm_area(C);
76 ARegion *ar = CTX_wm_region(C);
79 MaskLayer *point_masklay = NULL;
80 MaskSpline *point_spline = NULL;
81 MaskSplinePoint *point = NULL;
83 const float threshold_sq = threshold * threshold;
84 float len_sq = FLT_MAX, scalex, scaley;
85 eMaskWhichHandle which_handle = MASK_WHICH_HANDLE_NONE;
88 ED_mask_get_size(sa, &width, &height);
89 ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
91 co[0] = normal_co[0] * scalex;
92 co[1] = normal_co[1] * scaley;
94 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
97 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
101 for (spline = masklay->splines.first; spline; spline = spline->next) {
102 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
106 for (i = 0; i < spline->tot_point; i++) {
107 MaskSplinePoint *cur_point = &spline->points[i];
108 MaskSplinePoint *cur_point_deform = &points_array[i];
109 eMaskWhichHandle cur_which_handle = MASK_WHICH_HANDLE_NONE;
110 BezTriple *bezt = &cur_point_deform->bezt;
111 float cur_len_sq, vec[2];
113 vec[0] = bezt->vec[1][0] * scalex;
114 vec[1] = bezt->vec[1][1] * scaley;
116 cur_len_sq = len_squared_v2v2(co, vec);
118 if (cur_len_sq < len_sq) {
119 point_spline = spline;
120 point_masklay = masklay;
123 which_handle = MASK_WHICH_HANDLE_NONE;
126 if (BKE_mask_point_handles_mode_get(cur_point_deform) == MASK_HANDLE_MODE_STICK) {
128 mask_point_scaled_handle(cur_point_deform, MASK_WHICH_HANDLE_STICK, scalex, scaley, handle);
129 cur_len_sq = len_squared_v2v2(co, handle);
130 cur_which_handle = MASK_WHICH_HANDLE_STICK;
133 float handle_left[2], handle_right[2];
134 float len_left_sq, len_right_sq;
135 mask_point_scaled_handle(cur_point_deform, MASK_WHICH_HANDLE_LEFT, scalex, scaley, handle_left);
136 mask_point_scaled_handle(cur_point_deform, MASK_WHICH_HANDLE_RIGHT, scalex, scaley, handle_right);
138 len_left_sq = len_squared_v2v2(co, handle_left);
139 len_right_sq = len_squared_v2v2(co, handle_right);
141 if (len_left_sq <= len_right_sq) {
142 if (bezt->h1 != HD_VECT) {
143 cur_which_handle = MASK_WHICH_HANDLE_LEFT;
144 cur_len_sq = len_left_sq;
147 else if (bezt->h2 != HD_VECT) {
148 cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
149 cur_len_sq = len_right_sq;
153 if (len_right_sq <= len_left_sq) {
154 if (bezt->h2 != HD_VECT) {
155 cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
156 cur_len_sq = len_right_sq;
159 else if (bezt->h1 != HD_VECT) {
160 cur_which_handle = MASK_WHICH_HANDLE_LEFT;
161 cur_len_sq = len_left_sq;
166 if (cur_len_sq <= len_sq && cur_which_handle != MASK_WHICH_HANDLE_NONE) {
167 point_masklay = masklay;
168 point_spline = spline;
171 which_handle = cur_which_handle;
177 if (len_sq < threshold_sq) {
179 *masklay_r = point_masklay;
182 *spline_r = point_spline;
185 *which_handle_r = which_handle;
188 *score = sqrtf(len_sq);
200 *which_handle_r = MASK_WHICH_HANDLE_NONE;
205 bool ED_mask_feather_find_nearest(const bContext *C, Mask *mask, const float normal_co[2], const float threshold,
206 MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
207 MaskSplinePointUW **uw_r, float *score)
209 ScrArea *sa = CTX_wm_area(C);
210 ARegion *ar = CTX_wm_region(C);
212 MaskLayer *masklay, *point_masklay = NULL;
213 MaskSpline *point_spline = NULL;
214 MaskSplinePoint *point = NULL;
215 MaskSplinePointUW *uw = NULL;
216 const float threshold_sq = threshold * threshold;
217 float len = FLT_MAX, co[2];
218 float scalex, scaley;
221 ED_mask_get_size(sa, &width, &height);
222 ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
224 co[0] = normal_co[0] * scalex;
225 co[1] = normal_co[1] * scaley;
227 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
230 for (spline = masklay->splines.first; spline; spline = spline->next) {
231 //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
233 int i, tot_feather_point;
234 float (*feather_points)[2], (*fp)[2];
236 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
240 feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
242 for (i = 0; i < spline->tot_point; i++) {
244 MaskSplinePoint *cur_point = &spline->points[i];
246 for (j = 0; j <= cur_point->tot_uw; j++) {
247 float cur_len_sq, vec[2];
249 vec[0] = (*fp)[0] * scalex;
250 vec[1] = (*fp)[1] * scaley;
252 cur_len_sq = len_squared_v2v2(vec, co);
254 if (point == NULL || cur_len_sq < len) {
258 uw = &cur_point->uw[j - 1];
260 point_masklay = masklay;
261 point_spline = spline;
270 MEM_freeN(feather_points);
274 if (len < threshold_sq) {
276 *masklay_r = point_masklay;
279 *spline_r = point_spline;
306 /******************** create new mask *********************/
308 Mask *ED_mask_new(bContext *C, const char *name)
310 ScrArea *sa = CTX_wm_area(C);
311 Main *bmain = CTX_data_main(C);
314 mask = BKE_mask_new(bmain, name);
316 if (sa && sa->spacedata.first) {
317 switch (sa->spacetype) {
320 SpaceClip *sc = sa->spacedata.first;
321 ED_space_clip_set_mask(C, sc, mask);
331 SpaceImage *sima = sa->spacedata.first;
332 ED_space_image_set_mask(C, sima, mask);
341 /* Get ative layer. Will create mask/layer to be sure there's an active layer. */
342 MaskLayer *ED_mask_layer_ensure(bContext *C)
344 Mask *mask = CTX_data_edit_mask(C);
345 MaskLayer *mask_layer;
348 /* If there's no active mask, create one. */
349 mask = ED_mask_new(C, NULL);
352 mask_layer = BKE_mask_layer_active(mask);
353 if (mask_layer == NULL) {
354 /* If there's no active mask layer, create one. */
355 mask_layer = BKE_mask_layer_new(mask, "");
361 static int mask_new_exec(bContext *C, wmOperator *op)
363 char name[MAX_ID_NAME - 2];
365 RNA_string_get(op->ptr, "name", name);
367 ED_mask_new(C, name);
369 return OPERATOR_FINISHED;
372 void MASK_OT_new(wmOperatorType *ot)
375 ot->name = "New Mask";
376 ot->description = "Create new mask";
377 ot->idname = "MASK_OT_new";
380 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
383 ot->exec = mask_new_exec;
384 ot->poll = ED_operator_mask;
387 RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask");
390 /******************** create new masklay *********************/
392 static int masklay_new_exec(bContext *C, wmOperator *op)
394 Mask *mask = CTX_data_edit_mask(C);
395 char name[MAX_ID_NAME - 2];
397 RNA_string_get(op->ptr, "name", name);
399 BKE_mask_layer_new(mask, name);
400 mask->masklay_act = mask->masklay_tot - 1;
402 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
404 return OPERATOR_FINISHED;
407 void MASK_OT_layer_new(wmOperatorType *ot)
410 ot->name = "Add Mask Layer";
411 ot->description = "Add new mask layer for masking";
412 ot->idname = "MASK_OT_layer_new";
415 ot->exec = masklay_new_exec;
416 ot->poll = ED_maskedit_poll;
419 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
422 RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask layer");
425 /******************** remove mask layer *********************/
427 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
429 Mask *mask = CTX_data_edit_mask(C);
430 MaskLayer *masklay = BKE_mask_layer_active(mask);
433 BKE_mask_layer_remove(mask, masklay);
435 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
438 return OPERATOR_FINISHED;
441 void MASK_OT_layer_remove(wmOperatorType *ot)
444 ot->name = "Remove Mask Layer";
445 ot->description = "Remove mask layer";
446 ot->idname = "MASK_OT_layer_remove";
449 ot->exec = masklay_remove_exec;
450 ot->poll = ED_maskedit_poll;
453 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
456 /******************** slide *********************/
459 SLIDE_ACTION_NONE = 0,
460 SLIDE_ACTION_POINT = 1,
461 SLIDE_ACTION_HANDLE = 2,
462 SLIDE_ACTION_FEATHER = 3,
463 SLIDE_ACTION_SPLINE = 4
466 typedef struct SlidePointData {
467 /* Generic fields. */
468 short event_invoke_type;
472 MaskSpline *spline, *orig_spline;
473 MaskSplinePoint *point;
474 MaskSplinePointUW *uw;
475 eMaskWhichHandle which_handle;
478 float prev_mouse_coord[2];
481 bool is_curvature_only,
486 bool is_sliding_new_point;
488 /* Data needed to restre the state. */
494 /* Handle sliding. */
495 float orig_handle_coord[2], prev_handle_coord[2];
497 /* Feather sliding. */
498 float prev_feather_coord[2];
499 float weight, weight_scalar;
502 static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float co[2])
504 BKE_mask_coord_to_movieclip(sc->clip, &sc->user, r_co, co);
505 ED_clip_point_undistorted_pos(sc, r_co, r_co);
506 BKE_mask_coord_from_movieclip(sc->clip, &sc->user, r_co, r_co);
509 static bool spline_under_mouse_get(const bContext *C,
510 Mask *mask, const float co[2],
511 MaskLayer **mask_layer_r,
512 MaskSpline **mask_spline_r)
514 const float threshold = 19.0f;
515 ScrArea *sa = CTX_wm_area(C);
516 SpaceClip *sc = CTX_wm_space_clip(C);
517 MaskLayer *mask_layer;
520 float closest_dist_squared = 0.0f;
521 MaskLayer *closest_layer = NULL;
522 MaskSpline *closest_spline = NULL;
523 bool undistort = false;
524 *mask_layer_r = NULL;
525 *mask_spline_r = NULL;
526 ED_mask_get_size(sa, &width, &height);
527 pixel_co[0] = co[0] * width;
528 pixel_co[1] = co[1] * height;
530 undistort = (sc->clip != NULL) &&
531 (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0;
533 for (mask_layer = mask->masklayers.first;
535 mask_layer = mask_layer->next)
538 if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
542 for (spline = mask_layer->splines.first;
544 spline = spline->next)
546 MaskSplinePoint *points_array;
547 float min[2], max[2], center[2];
551 if ((spline->flag & SELECT) == 0) {
555 points_array = BKE_mask_spline_point_array(spline);
556 INIT_MINMAX2(min, max);
557 for (i = 0; i < spline->tot_point; i++) {
558 MaskSplinePoint *point_deform = &points_array[i];
559 BezTriple *bezt = &point_deform->bezt;
563 copy_v2_v2(vert, bezt->vec[1]);
566 mask_point_undistort_pos(sc, vert, vert);
569 minmax_v2v2_v2(min, max, vert);
572 center[0] = (min[0] + max[0]) / 2.0f * width;
573 center[1] = (min[1] + max[1]) / 2.0f * height;
574 dist_squared = len_squared_v2v2(pixel_co, center);
575 max_bb_side = min_ff((max[0] - min[0]) * width, (max[1] - min[1]) * height);
576 if (dist_squared <= max_bb_side * max_bb_side * 0.5f &&
577 (closest_spline == NULL || dist_squared < closest_dist_squared))
579 closest_layer = mask_layer;
580 closest_spline = spline;
581 closest_dist_squared = dist_squared;
585 if (closest_dist_squared < SQUARE(threshold) && closest_spline != NULL) {
587 if (ED_mask_find_nearest_diff_point(C, mask, co, threshold,
588 false, NULL, true, false,
589 NULL, NULL, NULL, NULL,
592 if (SQUARE(diff_score) < closest_dist_squared) {
597 *mask_layer_r = closest_layer;
598 *mask_spline_r = closest_spline;
604 static bool slide_point_check_initial_feather(MaskSpline *spline)
608 for (i = 0; i < spline->tot_point; i++) {
609 MaskSplinePoint *point = &spline->points[i];
611 if (point->bezt.weight != 0.0f) {
619 static void select_sliding_point(Mask *mask, MaskLayer *mask_layer, MaskSpline *spline,
620 MaskSplinePoint *point, eMaskWhichHandle which_handle)
622 ED_mask_select_toggle_all(mask, SEL_DESELECT);
624 switch (which_handle) {
625 case MASK_WHICH_HANDLE_NONE:
626 BKE_mask_point_select_set(point, true);
628 case MASK_WHICH_HANDLE_LEFT:
629 point->bezt.f1 |= SELECT;
631 case MASK_WHICH_HANDLE_RIGHT:
632 point->bezt.f3 |= SELECT;
634 case MASK_WHICH_HANDLE_STICK:
635 point->bezt.f1 |= SELECT;
636 point->bezt.f3 |= SELECT;
639 BLI_assert(!"Unexpected situation in select_sliding_point()");
642 mask_layer->act_spline = spline;
643 mask_layer->act_point = point;
644 ED_mask_select_flush_all(mask);
647 static void check_sliding_handle_type(MaskSplinePoint *point, eMaskWhichHandle which_handle)
649 BezTriple *bezt = &point->bezt;
651 if (which_handle == MASK_WHICH_HANDLE_LEFT) {
652 if (bezt->h1 == HD_VECT) {
655 else if (bezt->h1 == HD_AUTO) {
656 bezt->h1 = HD_ALIGN_DOUBLESIDE;
657 bezt->h2 = HD_ALIGN_DOUBLESIDE;
660 else if (which_handle == MASK_WHICH_HANDLE_RIGHT) {
661 if (bezt->h2 == HD_VECT) {
664 else if (bezt->h2 == HD_AUTO) {
665 bezt->h1 = HD_ALIGN_DOUBLESIDE;
666 bezt->h2 = HD_ALIGN_DOUBLESIDE;
671 static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *event)
673 ScrArea *sa = CTX_wm_area(C);
674 ARegion *ar = CTX_wm_region(C);
676 Mask *mask = CTX_data_edit_mask(C);
677 SlidePointData *customdata = NULL;
678 MaskLayer *masklay, *cv_masklay, *feather_masklay;
679 MaskSpline *spline, *cv_spline, *feather_spline;
680 MaskSplinePoint *point, *cv_point, *feather_point;
681 MaskSplinePointUW *uw = NULL;
682 int width, height, action = SLIDE_ACTION_NONE;
683 const bool slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
684 float co[2], cv_score, feather_score;
685 const float threshold = 19;
686 eMaskWhichHandle which_handle;
688 ED_mask_mouse_pos(sa, ar, event->mval, co);
689 ED_mask_get_size(sa, &width, &height);
691 cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &which_handle, &cv_score);
693 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
694 if (slide_feather || !cv_point || feather_score < cv_score) {
695 action = SLIDE_ACTION_FEATHER;
697 masklay = feather_masklay;
698 spline = feather_spline;
699 point = feather_point;
703 if (cv_point && action == SLIDE_ACTION_NONE) {
704 if (which_handle != MASK_WHICH_HANDLE_NONE)
705 action = SLIDE_ACTION_HANDLE;
707 action = SLIDE_ACTION_POINT;
709 masklay = cv_masklay;
714 if (action == SLIDE_ACTION_NONE) {
715 if (spline_under_mouse_get(C, mask, co, &masklay, &spline)) {
716 action = SLIDE_ACTION_SPLINE;
721 if (action != SLIDE_ACTION_NONE) {
722 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
723 customdata->event_invoke_type = event->type;
724 customdata->mask = mask;
725 customdata->masklay = masklay;
726 customdata->spline = spline;
727 customdata->point = point;
728 customdata->width = width;
729 customdata->height = height;
730 customdata->action = action;
733 customdata->is_sliding_new_point = RNA_boolean_get(op->ptr, "is_new_point");
735 if (customdata->action != SLIDE_ACTION_SPLINE) {
736 customdata->old_h1 = point->bezt.h1;
737 customdata->old_h2 = point->bezt.h2;
738 select_sliding_point(mask, masklay, spline, point, which_handle);
739 check_sliding_handle_type(point, which_handle);
744 float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
746 customdata->weight = uw->w;
747 customdata->weight_scalar = weight_scalar;
748 BKE_mask_point_segment_co(spline, point, uw->u, co_uw);
749 BKE_mask_point_normal(spline, point, uw->u, customdata->no);
751 madd_v2_v2v2fl(customdata->prev_feather_coord, co_uw, customdata->no, uw->w * weight_scalar);
753 else if (customdata->action != SLIDE_ACTION_SPLINE) {
754 BezTriple *bezt = &point->bezt;
756 customdata->weight = bezt->weight;
757 customdata->weight_scalar = 1.0f;
758 BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
760 madd_v2_v2v2fl(customdata->prev_feather_coord, bezt->vec[1], customdata->no, bezt->weight);
763 if (customdata->action == SLIDE_ACTION_FEATHER) {
764 customdata->is_initial_feather = slide_point_check_initial_feather(spline);
767 if (customdata->action != SLIDE_ACTION_SPLINE) {
768 copy_m3_m3(customdata->vec, point->bezt.vec);
769 if (which_handle != MASK_WHICH_HANDLE_NONE) {
770 BKE_mask_point_handle(point, which_handle, customdata->orig_handle_coord);
771 copy_v2_v2(customdata->prev_handle_coord, customdata->orig_handle_coord);
774 customdata->which_handle = which_handle;
776 ED_mask_mouse_pos(sa, ar, event->mval, customdata->prev_mouse_coord);
782 static int slide_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
784 Mask *mask = CTX_data_edit_mask(C);
785 SlidePointData *slidedata;
788 return OPERATOR_PASS_THROUGH;
791 slidedata = slide_point_customdata(C, op, event);
794 op->customdata = slidedata;
796 WM_event_add_modal_handler(C, op);
798 slidedata->masklay->act_spline = slidedata->spline;
799 slidedata->masklay->act_point = slidedata->point;
801 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
803 return OPERATOR_RUNNING_MODAL;
806 return OPERATOR_PASS_THROUGH;
809 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
813 for (i = 0; i < data->spline->tot_point; i++) {
814 MaskSplinePoint *point = &data->spline->points[i];
815 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
817 point->bezt.weight = orig_point->bezt.weight + delta;
818 if (point->bezt.weight < 0.0f) {
819 point->bezt.weight = 0.0f;
824 static void slide_point_restore_spline(SlidePointData *data)
828 for (i = 0; i < data->spline->tot_point; i++) {
829 MaskSplinePoint *point = &data->spline->points[i];
830 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
833 point->bezt = orig_point->bezt;
835 for (j = 0; j < point->tot_uw; j++)
836 point->uw[j] = orig_point->uw[j];
840 static void cancel_slide_point(SlidePointData *data)
844 if (data->orig_spline) {
845 slide_point_restore_spline(data);
848 if (data->action == SLIDE_ACTION_FEATHER) {
850 data->uw->w = data->weight;
852 data->point->bezt.weight = data->weight;
854 else if (data->action != SLIDE_ACTION_SPLINE) {
855 copy_m3_m3(data->point->bezt.vec, data->vec);
856 data->point->bezt.h1 = data->old_h1;
857 data->point->bezt.h2 = data->old_h2;
862 static void free_slide_point_data(SlidePointData *data)
864 if (data->orig_spline)
865 BKE_mask_spline_free(data->orig_spline);
870 static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
872 SlidePointData *data = (SlidePointData *)op->customdata;
873 BezTriple *bezt = &data->point->bezt;
876 switch (event->type) {
881 if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) {
882 if (data->action == SLIDE_ACTION_FEATHER) {
883 data->is_overall_feather = (event->val == KM_PRESS);
886 data->is_curvature_only = (event->val == KM_PRESS);
890 if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
891 data->is_accurate = (event->val == KM_PRESS);
893 ATTR_FALLTHROUGH; /* update CV position */
896 ScrArea *sa = CTX_wm_area(C);
897 ARegion *ar = CTX_wm_region(C);
900 ED_mask_mouse_pos(sa, ar, event->mval, co);
901 sub_v2_v2v2(delta, co, data->prev_mouse_coord);
902 if (data->is_accurate) {
903 mul_v2_fl(delta, 0.2f);
905 copy_v2_v2(data->prev_mouse_coord, co);
907 if (data->action == SLIDE_ACTION_HANDLE) {
910 if (data->is_sliding_new_point && data->which_handle == MASK_WHICH_HANDLE_STICK) {
911 if (ELEM(data->point, &data->spline->points[0],
912 &data->spline->points[data->spline->tot_point - 1]))
914 SWAP(float, delta[0], delta[1]);
917 /* flip last point */
918 if (data->point != &data->spline->points[0]) {
924 add_v2_v2v2(new_handle, data->prev_handle_coord, delta);
926 BKE_mask_point_set_handle(data->point, data->which_handle,
927 new_handle, data->is_curvature_only,
928 data->orig_handle_coord, data->vec);
929 BKE_mask_point_handle(data->point, data->which_handle, data->prev_handle_coord);
931 if (data->is_sliding_new_point) {
932 if (ELEM(data->which_handle, MASK_WHICH_HANDLE_LEFT, MASK_WHICH_HANDLE_RIGHT)) {
934 short self_handle = (data->which_handle == MASK_WHICH_HANDLE_LEFT) ? 0 : 2;
935 short other_handle = (data->which_handle == MASK_WHICH_HANDLE_LEFT) ? 2 : 0;
937 sub_v2_v2v2(vec, bezt->vec[1], bezt->vec[self_handle]);
938 add_v2_v2v2(bezt->vec[other_handle], bezt->vec[1], vec);
942 else if (data->action == SLIDE_ACTION_POINT) {
943 add_v2_v2(bezt->vec[0], delta);
944 add_v2_v2(bezt->vec[1], delta);
945 add_v2_v2(bezt->vec[2], delta);
947 else if (data->action == SLIDE_ACTION_FEATHER) {
948 float vec[2], no[2], p[2], c[2], w, offco[2];
949 float *weight = NULL;
950 float weight_scalar = 1.0f;
951 bool is_overall_feather = data->is_overall_feather || data->is_initial_feather;
953 add_v2_v2v2(offco, data->prev_feather_coord, delta);
956 /* project on both sides and find the closest one,
957 * prevents flickering when projecting onto both sides can happen */
958 const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
959 data->uw->u, offco, MASK_PROJ_NEG);
960 const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
961 data->uw->u, offco, MASK_PROJ_POS);
962 float dist_pos = FLT_MAX;
963 float dist_neg = FLT_MAX;
968 if (u_pos > 0.0f && u_pos < 1.0f) {
969 BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
970 dist_pos = len_squared_v2v2(offco, co_pos);
973 if (u_neg > 0.0f && u_neg < 1.0f) {
974 BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
975 dist_neg = len_squared_v2v2(offco, co_neg);
978 u = dist_pos < dist_neg ? u_pos : u_neg;
980 if (u > 0.0f && u < 1.0f) {
983 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
984 weight = &data->uw->w;
985 weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
986 if (weight_scalar != 0.0f) {
987 weight_scalar = 1.0f / weight_scalar;
990 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
991 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
995 weight = &bezt->weight;
996 /* weight_scalar = 1.0f; keep as is */
997 copy_v2_v2(no, data->no);
998 copy_v2_v2(p, bezt->vec[1]);
1002 sub_v2_v2v2(c, offco, p);
1003 project_v2_v2v2(vec, c, no);
1007 if (is_overall_feather) {
1010 if (dot_v2v2(no, vec) <= 0.0f)
1013 w_delta = w - data->weight * data->weight_scalar;
1015 if (data->orig_spline == NULL) {
1016 /* restore weight for currently sliding point, so orig_spline would be created
1017 * with original weights used
1019 *weight = data->weight;
1021 data->orig_spline = BKE_mask_spline_copy(data->spline);
1024 if (data->is_initial_feather) {
1025 *weight = w * weight_scalar;
1028 slide_point_delta_all_feather(data, w_delta);
1031 if (dot_v2v2(no, vec) <= 0.0f)
1034 if (data->orig_spline) {
1035 /* restore possible overall feather changes */
1036 slide_point_restore_spline(data);
1038 BKE_mask_spline_free(data->orig_spline);
1039 data->orig_spline = NULL;
1042 if (weight_scalar != 0.0f) {
1043 *weight = w * weight_scalar;
1047 copy_v2_v2(data->prev_feather_coord, offco);
1050 else if (data->action == SLIDE_ACTION_SPLINE) {
1053 if (data->orig_spline == NULL) {
1054 data->orig_spline = BKE_mask_spline_copy(data->spline);
1057 for (i = 0; i < data->spline->tot_point; i++) {
1058 MaskSplinePoint *point = &data->spline->points[i];
1059 add_v2_v2(point->bezt.vec[0], delta);
1060 add_v2_v2(point->bezt.vec[1], delta);
1061 add_v2_v2(point->bezt.vec[2], delta);
1065 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
1066 DEG_id_tag_update(&data->mask->id, 0);
1073 if (event->type == data->event_invoke_type && event->val == KM_RELEASE) {
1074 Scene *scene = CTX_data_scene(C);
1076 /* dont key sliding feather uw's */
1077 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == false) {
1078 if (IS_AUTOKEY_ON(scene)) {
1079 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
1083 if (data->is_sliding_new_point) {
1084 if (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) < FLT_EPSILON) {
1087 if (len_squared_v2v2(bezt->vec[2], bezt->vec[1]) < FLT_EPSILON) {
1092 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
1093 DEG_id_tag_update(&data->mask->id, 0);
1095 free_slide_point_data(op->customdata); /* keep this last! */
1096 return OPERATOR_FINISHED;
1098 else if (event->type != data->event_invoke_type && event->val == KM_PRESS) {
1099 /* pass to ESCKEY */
1106 cancel_slide_point(op->customdata);
1108 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
1109 DEG_id_tag_update(&data->mask->id, 0);
1111 free_slide_point_data(op->customdata); /* keep this last! */
1112 return OPERATOR_CANCELLED;
1115 return OPERATOR_RUNNING_MODAL;
1118 void MASK_OT_slide_point(wmOperatorType *ot)
1123 ot->name = "Slide Point";
1124 ot->description = "Slide control points";
1125 ot->idname = "MASK_OT_slide_point";
1128 ot->invoke = slide_point_invoke;
1129 ot->modal = slide_point_modal;
1130 ot->poll = ED_operator_mask;
1133 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1135 RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
1137 prop = RNA_def_boolean(ot->srna, "is_new_point", 0, "Slide New Point", "Newly created vertex is being slid");
1138 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1141 /******************** slide spline curvature *********************/
1143 typedef struct SlideSplineCurvatureData {
1144 short event_invoke_type;
1147 MaskLayer *mask_layer;
1149 MaskSplinePoint *point;
1153 BezTriple *adjust_bezt, *other_bezt;
1154 BezTriple bezt_backup, other_bezt_backup;
1156 float prev_mouse_coord[2];
1157 float prev_spline_coord[2];
1159 float P0[2], P1[2], P2[2], P3[3];
1160 } SlideSplineCurvatureData;
1162 static void cancel_slide_spline_curvature(SlideSplineCurvatureData *slide_data)
1164 *slide_data->adjust_bezt = slide_data->bezt_backup;
1165 *slide_data->other_bezt = slide_data->other_bezt_backup;
1169 static void free_slide_spline_curvature_data(SlideSplineCurvatureData *slide_data)
1171 MEM_freeN(slide_data);
1174 static bool slide_spline_curvature_check(bContext *C, const wmEvent *event)
1176 Mask *mask = CTX_data_edit_mask(C);
1178 const float threshold = 19.0f;
1180 ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co);
1182 if (ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL)) {
1186 if (ED_mask_feather_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL, NULL)) {
1193 static SlideSplineCurvatureData *slide_spline_curvature_customdata(
1194 bContext *C, const wmEvent *event)
1196 const float threshold = 19.0f;
1198 Mask *mask = CTX_data_edit_mask(C);
1199 SlideSplineCurvatureData *slide_data;
1200 MaskLayer *mask_layer;
1202 MaskSplinePoint *point;
1204 BezTriple *next_bezt;
1206 ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co);
1208 if (!ED_mask_find_nearest_diff_point(C, mask, co, threshold, false,
1210 &mask_layer, &spline, &point, &u,
1216 next_bezt = BKE_mask_spline_point_next_bezt(spline, spline->points, point);
1217 if (next_bezt == NULL) {
1221 slide_data = MEM_callocN(sizeof(SlideSplineCurvatureData), "slide curvature slide");
1222 slide_data->event_invoke_type = event->type;
1223 slide_data->mask = mask;
1224 slide_data->mask_layer = mask_layer;
1225 slide_data->spline = spline;
1226 slide_data->point = point;
1229 copy_v2_v2(slide_data->prev_mouse_coord, co);
1230 BKE_mask_point_segment_co(spline, point, u, slide_data->prev_spline_coord);
1232 copy_v2_v2(slide_data->P0, point->bezt.vec[1]);
1233 copy_v2_v2(slide_data->P1, point->bezt.vec[2]);
1234 copy_v2_v2(slide_data->P2, next_bezt->vec[0]);
1235 copy_v2_v2(slide_data->P3, next_bezt->vec[1]);
1237 /* Depending to which end we're closer to adjust either left or right side of the spline. */
1239 slide_data->adjust_bezt = &point->bezt;
1240 slide_data->other_bezt = next_bezt;
1243 slide_data->adjust_bezt = next_bezt;
1244 slide_data->other_bezt = &point->bezt;
1247 /* Data needed for restoring state. */
1248 slide_data->bezt_backup = *slide_data->adjust_bezt;
1249 slide_data->other_bezt_backup = *slide_data->other_bezt;
1251 /* Let's dont touch other side of the point for now, so set handle to FREE. */
1253 if (slide_data->adjust_bezt->h2 <= HD_VECT) {
1254 slide_data->adjust_bezt->h2 = HD_FREE;
1258 if (slide_data->adjust_bezt->h1 <= HD_VECT) {
1259 slide_data->adjust_bezt->h1 = HD_FREE;
1263 /* Change selection */
1264 ED_mask_select_toggle_all(mask, SEL_DESELECT);
1265 slide_data->adjust_bezt->f2 |= SELECT;
1266 slide_data->other_bezt->f2 |= SELECT;
1268 slide_data->adjust_bezt->f3 |= SELECT;
1269 slide_data->other_bezt->f1 |= SELECT;
1272 slide_data->adjust_bezt->f1 |= SELECT;
1273 slide_data->other_bezt->f3 |= SELECT;
1275 mask_layer->act_spline = spline;
1276 mask_layer->act_point = point;
1277 ED_mask_select_flush_all(mask);
1282 static int slide_spline_curvature_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1284 Mask *mask = CTX_data_edit_mask(C);
1285 SlideSplineCurvatureData *slide_data;
1288 return OPERATOR_PASS_THROUGH;
1291 /* Be sure we don't conflict with point slide here. */
1292 if (!slide_spline_curvature_check(C, event)) {
1293 return OPERATOR_PASS_THROUGH;
1296 slide_data = slide_spline_curvature_customdata(C, event);
1297 if (slide_data != NULL) {
1298 op->customdata = slide_data;
1299 WM_event_add_modal_handler(C, op);
1300 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1301 return OPERATOR_RUNNING_MODAL;
1304 return OPERATOR_PASS_THROUGH;
1307 static void slide_spline_solve_P1(const float u,
1314 const float u2 = u * u, u3 = u * u * u;
1315 const float v = 1.0f - u;
1316 const float v2 = v * v, v3 = v * v * v;
1317 const float inv_divider = 1.0f / (3.0f * v2 * u);
1318 const float t = 3.0f * v * u2;
1319 solution[0] = -(v3 * P0[0] + t * P2[0] + u3 * P3[0] - B[0]) * inv_divider;
1320 solution[1] = -(v3 * P0[1] + t * P2[1] + u3 * P3[1] - B[1]) * inv_divider;
1323 static void slide_spline_solve_P2(const float u,
1330 const float u2 = u * u, u3 = u * u * u;
1331 const float v = 1.0f - u;
1332 const float v2 = v * v, v3 = v * v * v;
1333 const float inv_divider = 1.0f / (3.0f * v * u2);
1334 const float t = 3.0f * v2 * u;
1335 solution[0] = -(v3 * P0[0] + t * P1[0] + u3 * P3[0] - B[0]) * inv_divider;
1336 solution[1] = -(v3 * P0[1] + t * P1[1] + u3 * P3[1] - B[1]) * inv_divider;
1339 static int slide_spline_curvature_modal(bContext *C, wmOperator *op, const wmEvent *event)
1341 Scene *scene = CTX_data_scene(C);
1342 const float margin = 0.2f;
1343 SlideSplineCurvatureData *slide_data = (SlideSplineCurvatureData *) op->customdata;
1344 float u = slide_data->u;
1346 switch (event->type) {
1351 if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) {
1352 slide_data->accurate = (event->val == KM_PRESS);
1355 if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
1356 if (event->val == KM_PRESS) {
1357 slide_data->adjust_bezt->h1 = slide_data->adjust_bezt->h2 = HD_FREE;
1358 if ((u > margin && u < 0.5f) || (u >= 0.5f && u < 1.0f - margin)) {
1359 slide_data->other_bezt->h1 = slide_data->other_bezt->h2 = HD_FREE;
1362 else if (event->val == KM_RELEASE) {
1363 slide_data->adjust_bezt->h1 = slide_data->bezt_backup.h1;
1364 slide_data->adjust_bezt->h2 = slide_data->bezt_backup.h2;
1365 slide_data->other_bezt->h1 = slide_data->other_bezt_backup.h1;
1366 slide_data->other_bezt->h2 = slide_data->other_bezt_backup.h2;
1370 copy_v2_v2(slide_data->adjust_bezt->vec[0], slide_data->bezt_backup.vec[0]);
1371 copy_v2_v2(slide_data->other_bezt->vec[2], slide_data->other_bezt_backup.vec[2]);
1374 copy_v2_v2(slide_data->adjust_bezt->vec[2], slide_data->bezt_backup.vec[2]);
1375 copy_v2_v2(slide_data->other_bezt->vec[0], slide_data->other_bezt_backup.vec[0]);
1380 ATTR_FALLTHROUGH; /* update CV position */
1383 float B[2], mouse_coord[2], delta[2];
1385 /* Get coordinate spline is expected to go through. */
1386 ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, mouse_coord);
1387 sub_v2_v2v2(delta, mouse_coord, slide_data->prev_mouse_coord);
1388 if (slide_data->accurate) {
1389 mul_v2_fl(delta, 0.2f);
1391 add_v2_v2v2(B, slide_data->prev_spline_coord, delta);
1392 copy_v2_v2(slide_data->prev_spline_coord, B);
1393 copy_v2_v2(slide_data->prev_mouse_coord, mouse_coord);
1397 bool need_restore_P2 = false;
1401 float x = (u - margin) * 0.5f / (0.5f - margin);
1402 float weight = (3 * x * x - 2 * x * x * x);
1404 slide_spline_solve_P2(u, B,
1410 copy_v2_v2(oldP2, slide_data->P2);
1411 interp_v2_v2v2(slide_data->P2, slide_data->P2, solution, weight);
1412 copy_v2_v2(slide_data->other_bezt->vec[0], slide_data->P2);
1413 need_restore_P2 = true;
1415 /* Tweak handle type in order to be able to apply the delta. */
1416 if (weight > 0.0f) {
1417 if (slide_data->other_bezt->h1 <= HD_VECT) {
1418 slide_data->other_bezt->h1 = HD_FREE;
1423 slide_spline_solve_P1(u, B,
1427 slide_data->adjust_bezt->vec[2]);
1429 if (need_restore_P2) {
1430 copy_v2_v2(slide_data->P2, oldP2);
1435 bool need_restore_P1 = false;
1437 if (u < 1.0f - margin) {
1439 float x = ((1.0f - u) - margin) * 0.5f / (0.5f - margin);
1440 float weight = 3 * x * x - 2 * x * x * x;
1442 slide_spline_solve_P1(u, B,
1448 copy_v2_v2(oldP1, slide_data->P1);
1449 interp_v2_v2v2(slide_data->P1, slide_data->P1, solution, weight);
1450 copy_v2_v2(slide_data->other_bezt->vec[2], slide_data->P1);
1451 need_restore_P1 = true;
1453 /* Tweak handle type in order to be able to apply the delta. */
1454 if (weight > 0.0f) {
1455 if (slide_data->other_bezt->h2 <= HD_VECT) {
1456 slide_data->other_bezt->h2 = HD_FREE;
1461 slide_spline_solve_P2(u, B,
1465 slide_data->adjust_bezt->vec[0]);
1467 if (need_restore_P1) {
1468 copy_v2_v2(slide_data->P1, oldP1);
1472 WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
1473 DEG_id_tag_update(&slide_data->mask->id, 0);
1480 if (event->type == slide_data->event_invoke_type && event->val == KM_RELEASE) {
1481 /* dont key sliding feather uw's */
1482 if (IS_AUTOKEY_ON(scene)) {
1483 ED_mask_layer_shape_auto_key(slide_data->mask_layer, CFRA);
1486 WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
1487 DEG_id_tag_update(&slide_data->mask->id, 0);
1489 free_slide_spline_curvature_data(slide_data); /* keep this last! */
1490 return OPERATOR_FINISHED;
1496 cancel_slide_spline_curvature(slide_data);
1498 WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
1499 DEG_id_tag_update(&slide_data->mask->id, 0);
1501 free_slide_spline_curvature_data(op->customdata); /* keep this last! */
1502 return OPERATOR_CANCELLED;
1505 return OPERATOR_RUNNING_MODAL;
1508 void MASK_OT_slide_spline_curvature(wmOperatorType *ot)
1511 ot->name = "Slide Spline Curvature";
1512 ot->description = "Slide a point on the spline to define it's curvature";
1513 ot->idname = "MASK_OT_slide_spline_curvature";
1516 ot->invoke = slide_spline_curvature_invoke;
1517 ot->modal = slide_spline_curvature_modal;
1518 ot->poll = ED_operator_mask;
1521 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1524 /******************** toggle cyclic *********************/
1526 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1528 Mask *mask = CTX_data_edit_mask(C);
1531 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1534 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1538 for (spline = masklay->splines.first; spline; spline = spline->next) {
1539 if (ED_mask_spline_select_check(spline)) {
1540 spline->flag ^= MASK_SPLINE_CYCLIC;
1545 DEG_id_tag_update(&mask->id, 0);
1546 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1548 return OPERATOR_FINISHED;
1551 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
1554 ot->name = "Toggle Cyclic";
1555 ot->description = "Toggle cyclic for selected splines";
1556 ot->idname = "MASK_OT_cyclic_toggle";
1559 ot->exec = cyclic_toggle_exec;
1560 ot->poll = ED_maskedit_mask_poll;
1563 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1566 /******************** delete *********************/
1568 static void delete_feather_points(MaskSplinePoint *point)
1575 for (i = 0; i < point->tot_uw; i++) {
1576 if ((point->uw[i].flag & SELECT) == 0)
1581 MEM_freeN(point->uw);
1586 MaskSplinePointUW *new_uw;
1589 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
1591 for (i = 0; i < point->tot_uw; i++) {
1592 if ((point->uw[i].flag & SELECT) == 0) {
1593 new_uw[j++] = point->uw[i];
1597 MEM_freeN(point->uw);
1600 point->tot_uw = count;
1604 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
1606 Scene *scene = CTX_data_scene(C);
1607 Mask *mask = CTX_data_edit_mask(C);
1609 bool changed = false;
1611 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1613 int mask_layer_shape_ofs = 0;
1615 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1619 spline = masklay->splines.first;
1622 const int tot_point_orig = spline->tot_point;
1624 MaskSpline *next_spline = spline->next;
1626 /* count unselected points */
1627 for (i = 0; i < spline->tot_point; i++) {
1628 MaskSplinePoint *point = &spline->points[i];
1630 if (!MASKPOINT_ISSEL_ANY(point))
1635 /* delete the whole spline */
1636 BLI_remlink(&masklay->splines, spline);
1637 BKE_mask_spline_free(spline);
1639 if (spline == masklay->act_spline) {
1640 masklay->act_spline = NULL;
1641 masklay->act_point = NULL;
1644 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
1647 MaskSplinePoint *new_points;
1650 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
1652 for (i = 0, j = 0; i < tot_point_orig; i++) {
1653 MaskSplinePoint *point = &spline->points[i];
1655 if (!MASKPOINT_ISSEL_ANY(point)) {
1656 if (point == masklay->act_point)
1657 masklay->act_point = &new_points[j];
1659 delete_feather_points(point);
1661 new_points[j] = *point;
1665 if (point == masklay->act_point)
1666 masklay->act_point = NULL;
1668 BKE_mask_point_free(point);
1669 spline->tot_point--;
1671 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
1675 mask_layer_shape_ofs += spline->tot_point;
1677 MEM_freeN(spline->points);
1678 spline->points = new_points;
1680 ED_mask_select_flush_all(mask);
1684 spline = next_spline;
1687 /* not essential but confuses users when there are keys with no data!
1688 * assume if they delete all data from the layer they also dont care about keys */
1689 if (BLI_listbase_is_empty(&masklay->splines)) {
1690 BKE_mask_layer_free_shapes(masklay);
1695 return OPERATOR_CANCELLED;
1698 /* TODO: only update edited splines */
1699 BKE_mask_update_display(mask, CFRA);
1701 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1703 return OPERATOR_FINISHED;
1706 void MASK_OT_delete(wmOperatorType *ot)
1709 ot->name = "Delete";
1710 ot->description = "Delete selected control points or splines";
1711 ot->idname = "MASK_OT_delete";
1714 ot->invoke = WM_operator_confirm;
1715 ot->exec = delete_exec;
1716 ot->poll = ED_maskedit_mask_poll;
1719 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1722 /* *** switch direction *** */
1723 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
1725 Scene *scene = CTX_data_scene(C);
1726 Mask *mask = CTX_data_edit_mask(C);
1729 bool changed = false;
1731 /* do actual selection */
1732 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1734 bool changed_layer = false;
1736 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1740 for (spline = masklay->splines.first; spline; spline = spline->next) {
1741 if (ED_mask_spline_select_check(spline)) {
1742 BKE_mask_spline_direction_switch(masklay, spline);
1744 changed_layer = true;
1748 if (changed_layer) {
1749 if (IS_AUTOKEY_ON(scene)) {
1750 ED_mask_layer_shape_auto_key(masklay, CFRA);
1756 /* TODO: only update this spline */
1757 BKE_mask_update_display(mask, CFRA);
1759 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1760 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1762 return OPERATOR_FINISHED;
1765 return OPERATOR_CANCELLED;
1768 void MASK_OT_switch_direction(wmOperatorType *ot)
1771 ot->name = "Switch Direction";
1772 ot->description = "Switch direction of selected splines";
1773 ot->idname = "MASK_OT_switch_direction";
1776 ot->exec = mask_switch_direction_exec;
1777 ot->poll = ED_maskedit_mask_poll;
1780 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1784 /* *** recalc normals *** */
1785 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1787 Scene *scene = CTX_data_scene(C);
1788 Mask *mask = CTX_data_edit_mask(C);
1792 bool changed = false;
1794 /* do actual selection */
1795 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1797 bool changed_layer = false;
1799 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1803 for (spline = masklay->splines.first; spline; spline = spline->next) {
1804 for (i = 0; i < spline->tot_point; i++) {
1805 MaskSplinePoint *point = &spline->points[i];
1807 if (MASKPOINT_ISSEL_ANY(point)) {
1808 BKE_mask_calc_handle_point_auto(spline, point, false);
1810 changed_layer = true;
1815 if (changed_layer) {
1816 if (IS_AUTOKEY_ON(scene)) {
1817 ED_mask_layer_shape_auto_key(masklay, CFRA);
1823 /* TODO: only update this spline */
1824 BKE_mask_update_display(mask, CFRA);
1826 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1827 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1829 return OPERATOR_FINISHED;
1832 return OPERATOR_CANCELLED;
1835 /* named to match mesh recalc normals */
1836 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1839 ot->name = "Recalc Normals";
1840 ot->description = "Re-calculate the direction of selected handles";
1841 ot->idname = "MASK_OT_normals_make_consistent";
1844 ot->exec = mask_normals_make_consistent_exec;
1845 ot->poll = ED_maskedit_mask_poll;
1848 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1852 /******************** set handle type *********************/
1854 static int set_handle_type_exec(bContext *C, wmOperator *op)
1856 Mask *mask = CTX_data_edit_mask(C);
1858 int handle_type = RNA_enum_get(op->ptr, "type");
1860 bool changed = false;
1862 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1866 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1870 for (spline = masklay->splines.first; spline; spline = spline->next) {
1871 for (i = 0; i < spline->tot_point; i++) {
1872 MaskSplinePoint *point = &spline->points[i];
1874 if (MASKPOINT_ISSEL_ANY(point)) {
1875 BezTriple *bezt = &point->bezt;
1877 if (bezt->f2 & SELECT) {
1878 bezt->h1 = handle_type;
1879 bezt->h2 = handle_type;
1882 if (bezt->f1 & SELECT) {
1883 bezt->h1 = handle_type;
1885 if (bezt->f3 & SELECT) {
1886 bezt->h2 = handle_type;
1890 if (handle_type == HD_ALIGN) {
1892 sub_v3_v3v3(vec, bezt->vec[0], bezt->vec[1]);
1893 add_v3_v3v3(bezt->vec[2], bezt->vec[1], vec);
1903 WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1904 DEG_id_tag_update(&mask->id, 0);
1906 return OPERATOR_FINISHED;
1908 return OPERATOR_CANCELLED;
1911 void MASK_OT_handle_type_set(wmOperatorType *ot)
1913 static EnumPropertyItem editcurve_handle_type_items[] = {
1914 {HD_AUTO, "AUTO", 0, "Auto", ""},
1915 {HD_VECT, "VECTOR", 0, "Vector", ""},
1916 {HD_ALIGN, "ALIGNED", 0, "Aligned Single", ""},
1917 {HD_ALIGN_DOUBLESIDE, "ALIGNED_DOUBLESIDE", 0, "Aligned", ""},
1918 {HD_FREE, "FREE", 0, "Free", ""},
1919 {0, NULL, 0, NULL, NULL}
1923 ot->name = "Set Handle Type";
1924 ot->description = "Set type of handles for selected control points";
1925 ot->idname = "MASK_OT_handle_type_set";
1928 ot->invoke = WM_menu_invoke;
1929 ot->exec = set_handle_type_exec;
1930 ot->poll = ED_maskedit_mask_poll;
1933 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1936 ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1940 /* ********* clear/set restrict view *********/
1941 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1943 Mask *mask = CTX_data_edit_mask(C);
1945 bool changed = false;
1947 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1949 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1950 ED_mask_layer_select_set(masklay, true);
1951 masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1957 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1958 DEG_id_tag_update(&mask->id, 0);
1960 return OPERATOR_FINISHED;
1963 return OPERATOR_CANCELLED;
1967 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1971 ot->name = "Clear Restrict View";
1972 ot->description = "Reveal the layer by setting the hide flag";
1973 ot->idname = "MASK_OT_hide_view_clear";
1976 ot->exec = mask_hide_view_clear_exec;
1977 ot->poll = ED_maskedit_mask_poll;
1980 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1983 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1985 Mask *mask = CTX_data_edit_mask(C);
1987 const bool unselected = RNA_boolean_get(op->ptr, "unselected");
1988 bool changed = false;
1990 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1992 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1997 if (ED_mask_layer_select_check(masklay)) {
1998 ED_mask_layer_select_set(masklay, false);
2000 masklay->restrictflag |= OB_RESTRICT_VIEW;
2002 if (masklay == BKE_mask_layer_active(mask)) {
2003 BKE_mask_layer_active_set(mask, NULL);
2008 if (!ED_mask_layer_select_check(masklay)) {
2009 masklay->restrictflag |= OB_RESTRICT_VIEW;
2011 if (masklay == BKE_mask_layer_active(mask)) {
2012 BKE_mask_layer_active_set(mask, NULL);
2019 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
2020 DEG_id_tag_update(&mask->id, 0);
2022 return OPERATOR_FINISHED;
2025 return OPERATOR_CANCELLED;
2029 void MASK_OT_hide_view_set(wmOperatorType *ot)
2032 ot->name = "Set Restrict View";
2033 ot->description = "Hide the layer by setting the hide flag";
2034 ot->idname = "MASK_OT_hide_view_set";
2037 ot->exec = mask_hide_view_set_exec;
2038 ot->poll = ED_maskedit_mask_poll;
2041 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2043 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
2047 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
2049 Scene *scene = CTX_data_scene(C);
2050 Mask *mask = CTX_data_edit_mask(C);
2052 bool changed = false;
2055 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
2058 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
2062 for (spline = masklay->splines.first; spline; spline = spline->next) {
2063 for (i = 0; i < spline->tot_point; i++) {
2064 MaskSplinePoint *point = &spline->points[i];
2066 if (MASKPOINT_ISSEL_ANY(point)) {
2067 BezTriple *bezt = &point->bezt;
2068 bezt->weight = 0.0f;
2076 /* TODO: only update edited splines */
2077 BKE_mask_update_display(mask, CFRA);
2079 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
2080 DEG_id_tag_update(&mask->id, 0);
2082 return OPERATOR_FINISHED;
2085 return OPERATOR_CANCELLED;
2089 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
2092 ot->name = "Clear Feather Weight";
2093 ot->description = "Reset the feather weight to zero";
2094 ot->idname = "MASK_OT_feather_weight_clear";
2097 ot->exec = mask_feather_weight_clear_exec;
2098 ot->poll = ED_maskedit_mask_poll;
2101 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2104 /******************** move mask layer operator *********************/
2106 static int mask_layer_move_poll(bContext *C)
2108 if (ED_maskedit_mask_poll(C)) {
2109 Mask *mask = CTX_data_edit_mask(C);
2111 return mask->masklay_tot > 0;
2117 static int mask_layer_move_exec(bContext *C, wmOperator *op)
2119 Mask *mask = CTX_data_edit_mask(C);
2120 MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
2121 MaskLayer *mask_layer_other;
2122 int direction = RNA_enum_get(op->ptr, "direction");
2125 return OPERATOR_CANCELLED;
2127 if (direction == -1) {
2128 mask_layer_other = mask_layer->prev;
2130 if (!mask_layer_other)
2131 return OPERATOR_CANCELLED;
2133 BLI_remlink(&mask->masklayers, mask_layer);
2134 BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
2135 mask->masklay_act--;
2137 else if (direction == 1) {
2138 mask_layer_other = mask_layer->next;
2140 if (!mask_layer_other)
2141 return OPERATOR_CANCELLED;
2143 BLI_remlink(&mask->masklayers, mask_layer);
2144 BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
2145 mask->masklay_act++;
2148 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
2150 return OPERATOR_FINISHED;
2153 void MASK_OT_layer_move(wmOperatorType *ot)
2155 static EnumPropertyItem direction_items[] = {
2156 {-1, "UP", 0, "Up", ""},
2157 {1, "DOWN", 0, "Down", ""},
2158 {0, NULL, 0, NULL, NULL}
2162 ot->name = "Move Layer";
2163 ot->description = "Move the active layer up/down in the list";
2164 ot->idname = "MASK_OT_layer_move";
2167 ot->exec = mask_layer_move_exec;
2168 ot->poll = mask_layer_move_poll;
2171 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2174 RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move the active layer");
2177 /******************** duplicate *********************/
2179 static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
2181 Scene *scene = CTX_data_scene(C);
2182 Mask *mask = CTX_data_edit_mask(C);
2183 MaskLayer *mask_layer;
2185 for (mask_layer = mask->masklayers.first;
2187 mask_layer = mask_layer->next)
2191 for (spline = mask_layer->splines.last;
2193 spline = spline->prev)
2195 MaskSplinePoint *point = spline->points;
2197 while (i < spline->tot_point) {
2198 int start = i, end = -1;
2199 /* Find next selected segment. */
2200 while (MASKPOINT_ISSEL_ANY(point)) {
2201 BKE_mask_point_select_set(point, false);
2203 if (i >= spline->tot_point - 1) {
2211 int tot_point_shape_start = 0;
2212 MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
2213 MaskSplinePoint *new_point;
2216 /* BKE_mask_spline_add might allocate the points, need to free them in this case. */
2217 if (new_spline->points) {
2218 MEM_freeN(new_spline->points);
2221 /* Copy options from old spline. */
2222 new_spline->flag = spline->flag;
2223 new_spline->offset_mode = spline->offset_mode;
2224 new_spline->weight_interp = spline->weight_interp;
2225 new_spline->parent = spline->parent;
2227 /* Allocate new points and copy them from old spline. */
2228 new_spline->tot_point = end - start + 1;
2229 new_spline->points = MEM_mallocN(sizeof(MaskSplinePoint) * new_spline->tot_point,
2230 "duplicated mask points");
2232 memcpy(new_spline->points, spline->points + start,
2233 new_spline->tot_point * sizeof(MaskSplinePoint));
2235 tot_point = new_spline->tot_point;
2237 /* animation requires points added one by one */
2238 if (mask_layer->splines_shapes.first) {
2239 new_spline->tot_point = 0;
2240 tot_point_shape_start = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
2243 /* Select points and duplicate their UWs (if needed). */
2244 for (b = 0, new_point = new_spline->points;
2248 if (new_point->uw) {
2249 new_point->uw = MEM_dupallocN(new_point->uw);
2251 BKE_mask_point_select_set(new_point, true);
2254 if (mask_layer->splines_shapes.first) {
2255 new_spline->tot_point++;
2256 BKE_mask_layer_shape_changed_add(mask_layer, tot_point_shape_start + b, true, false);
2260 /* Clear cyclic flag if we didn't copy the whole spline. */
2261 if (new_spline->flag & MASK_SPLINE_CYCLIC) {
2262 if (start != 0 || end != spline->tot_point - 1) {
2263 new_spline->flag &= ~MASK_SPLINE_CYCLIC;
2267 /* Flush selection to splines. */
2268 new_spline->flag |= SELECT;
2269 spline->flag &= ~SELECT;
2271 mask_layer->act_spline = new_spline;
2279 /* TODO: only update edited splines */
2280 BKE_mask_update_display(mask, CFRA);
2282 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
2284 return OPERATOR_FINISHED;
2287 void MASK_OT_duplicate(wmOperatorType *ot)
2290 ot->name = "Duplicate Mask";
2291 ot->description = "Duplicate selected control points and segments between them";
2292 ot->idname = "MASK_OT_duplicate";
2295 ot->exec = mask_duplicate_exec;
2296 ot->poll = ED_maskedit_mask_poll;
2299 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2302 /********************** copy splines to clipboard operator *********************/
2304 static int copy_splines_exec(bContext *C, wmOperator *UNUSED(op))
2306 Mask *mask = CTX_data_edit_mask(C);
2307 MaskLayer *mask_layer = BKE_mask_layer_active(mask);
2309 if (mask_layer == NULL) {
2310 return OPERATOR_CANCELLED;
2313 BKE_mask_clipboard_copy_from_layer(mask_layer);
2315 return OPERATOR_FINISHED;
2318 void MASK_OT_copy_splines(wmOperatorType *ot)
2321 ot->name = "Copy Splines";
2322 ot->description = "Copy selected splines to clipboard";
2323 ot->idname = "MASK_OT_copy_splines";
2326 ot->exec = copy_splines_exec;
2327 ot->poll = ED_maskedit_mask_poll;
2330 ot->flag = OPTYPE_REGISTER;
2333 /********************** paste tracks from clipboard operator *********************/
2335 static int paste_splines_poll(bContext *C)
2337 if (ED_maskedit_mask_poll(C)) {
2338 return BKE_mask_clipboard_is_empty() == false;
2344 static int paste_splines_exec(bContext *C, wmOperator *UNUSED(op))
2346 Scene *scene = CTX_data_scene(C);
2347 Mask *mask = CTX_data_edit_mask(C);
2348 MaskLayer *mask_layer = BKE_mask_layer_active(mask);
2350 if (mask_layer == NULL) {
2351 mask_layer = BKE_mask_layer_new(mask, "");
2354 BKE_mask_clipboard_paste_to_layer(CTX_data_main(C), mask_layer);
2356 /* TODO: only update edited splines */
2357 BKE_mask_update_display(mask, CFRA);
2359 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
2361 return OPERATOR_FINISHED;
2364 void MASK_OT_paste_splines(wmOperatorType *ot)
2367 ot->name = "Paste Splines";
2368 ot->description = "Paste splines from clipboard";
2369 ot->idname = "MASK_OT_paste_splines";
2372 ot->exec = paste_splines_exec;
2373 ot->poll = paste_splines_poll;
2376 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;