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"
41 #include "DNA_scene_types.h"
42 #include "DNA_mask_types.h"
43 #include "DNA_object_types.h" /* SELECT */
48 #include "ED_screen.h"
51 #include "ED_keyframing.h"
53 #include "RNA_access.h"
54 #include "RNA_define.h"
56 #include "mask_intern.h" /* own include */
58 /******************** utility functions *********************/
60 MaskSplinePoint *ED_mask_point_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
61 MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
65 MaskLayer *point_masklay = NULL;
66 MaskSpline *point_spline = NULL;
67 MaskSplinePoint *point = NULL;
68 float co[2], aspx, aspy;
69 float len = FLT_MAX, scalex, scaley;
70 int is_handle = FALSE, width, height;
72 ED_mask_size(C, &width, &height);
73 ED_mask_aspect(C, &aspx, &aspy);
74 ED_mask_pixelspace_factor(C, &scalex, &scaley);
76 co[0] = normal_co[0] * scalex;
77 co[1] = normal_co[1] * scaley;
79 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
82 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
86 for (spline = masklay->splines.first; spline; spline = spline->next) {
87 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
91 for (i = 0; i < spline->tot_point; i++) {
92 MaskSplinePoint *cur_point = &spline->points[i];
93 MaskSplinePoint *cur_point_deform = &points_array[i];
94 float cur_len, vec[2], handle[2];
96 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
97 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
99 if (BKE_mask_point_has_handle(cur_point)) {
100 BKE_mask_point_handle(cur_point_deform, handle);
104 cur_len = len_v2v2(co, handle);
107 point_masklay = masklay;
108 point_spline = spline;
115 cur_len = len_v2v2(co, vec);
118 point_spline = spline;
119 point_masklay = masklay;
128 if (len < threshold) {
130 *masklay_r = point_masklay;
133 *spline_r = point_spline;
136 *is_handle_r = is_handle;
151 *is_handle_r = FALSE;
156 int ED_mask_feather_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
157 MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
158 MaskSplinePointUW **uw_r, float *score)
160 MaskLayer *masklay, *point_masklay = NULL;
161 MaskSpline *point_spline = NULL;
162 MaskSplinePoint *point = NULL;
163 MaskSplinePointUW *uw = NULL;
164 float len = FLT_MAX, co[2];
165 float scalex, scaley, aspx, aspy;
168 ED_mask_size(C, &width, &height);
169 ED_mask_aspect(C, &aspx, &aspy);
170 ED_mask_pixelspace_factor(C, &scalex, &scaley);
172 co[0] = normal_co[0] * scalex;
173 co[1] = normal_co[1] * scaley;
175 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
178 for (spline = masklay->splines.first; spline; spline = spline->next) {
179 //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
181 int i, tot_feather_point;
182 float (*feather_points)[2], (*fp)[2];
184 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
188 feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
190 for (i = 0; i < spline->tot_point; i++) {
192 MaskSplinePoint *cur_point = &spline->points[i];
194 for (j = 0; j < cur_point->tot_uw + 1; j++) {
195 float cur_len, vec[2];
197 vec[0] = (*fp)[0] * scalex;
198 vec[1] = (*fp)[1] * scaley;
200 cur_len = len_v2v2(vec, co);
202 if (point == NULL || cur_len < len) {
206 uw = &cur_point->uw[j - 1];
208 point_masklay = masklay;
209 point_spline = spline;
218 MEM_freeN(feather_points);
222 if (len < threshold) {
224 *masklay_r = point_masklay;
227 *spline_r = point_spline;
254 /******************** create new mask *********************/
256 static int mask_new_exec(bContext *C, wmOperator *op)
258 SpaceClip *sc = CTX_wm_space_clip(C);
260 char name[MAX_ID_NAME - 2];
262 RNA_string_get(op->ptr, "name", name);
264 mask = BKE_mask_new(name);
267 ED_space_clip_set_mask(C, sc, mask);
269 return OPERATOR_FINISHED;
272 void MASK_OT_new(wmOperatorType *ot)
275 ot->name = "New Mask";
276 ot->description = "Create new mask";
277 ot->idname = "MASK_OT_new";
280 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
283 ot->exec = mask_new_exec;
284 ot->poll = ED_operator_mask;
287 RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
290 /******************** create new masklay *********************/
292 static int masklay_new_exec(bContext *C, wmOperator *op)
294 Mask *mask = CTX_data_edit_mask(C);
295 char name[MAX_ID_NAME - 2];
297 RNA_string_get(op->ptr, "name", name);
299 BKE_mask_layer_new(mask, name);
300 mask->masklay_act = mask->masklay_tot - 1;
302 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
304 return OPERATOR_FINISHED;
307 void MASK_OT_layer_new(wmOperatorType *ot)
310 ot->name = "Add Mask Layer";
311 ot->description = "Add new mask layer for masking";
312 ot->idname = "MASK_OT_layer_new";
315 ot->exec = masklay_new_exec;
316 ot->poll = ED_maskedit_poll;
319 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
322 RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
325 /******************** remove mask layer *********************/
327 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
329 Mask *mask = CTX_data_edit_mask(C);
330 MaskLayer *masklay = BKE_mask_layer_active(mask);
333 BKE_mask_layer_remove(mask, masklay);
335 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
338 return OPERATOR_FINISHED;
341 void MASK_OT_layer_remove(wmOperatorType *ot)
344 ot->name = "Remove Mask Layer";
345 ot->description = "Remove mask layer";
346 ot->idname = "MASK_OT_layer_remove";
349 ot->exec = masklay_remove_exec;
350 ot->poll = ED_maskedit_poll;
353 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
356 /******************** slide *********************/
359 SLIDE_ACTION_NONE = 0,
360 SLIDE_ACTION_POINT = 1,
361 SLIDE_ACTION_HANDLE = 2,
362 SLIDE_ACTION_FEATHER = 3
365 typedef struct SlidePointData {
373 MaskSpline *spline, *orig_spline;
374 MaskSplinePoint *point;
375 MaskSplinePointUW *uw;
376 float handle[2], no[2], feather[2];
380 short curvature_only, accurate;
381 short initial_feather, overall_feather;
384 static int slide_point_check_initial_feather(MaskSpline *spline)
388 for (i = 0; i < spline->tot_point; i++) {
389 MaskSplinePoint *point = &spline->points[i];
391 if (point->bezt.weight != 0.0f)
394 /* comment for now. if all bezt weights are zero - this is as good-as initial */
397 for (j = 0; j < point->tot_uw; j++) {
398 if (point->uw[j].w != 0.0f)
407 static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
409 Mask *mask = CTX_data_edit_mask(C);
410 SlidePointData *customdata = NULL;
411 MaskLayer *masklay, *cv_masklay, *feather_masklay;
412 MaskSpline *spline, *cv_spline, *feather_spline;
413 MaskSplinePoint *point, *cv_point, *feather_point;
414 MaskSplinePointUW *uw = NULL;
415 int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
416 int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
417 float co[2], cv_score, feather_score;
418 const float threshold = 19;
420 ED_mask_mouse_pos(C, event, co);
421 ED_mask_size(C, &width, &height);
423 cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
425 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
426 if (slide_feather || !cv_point || feather_score < cv_score) {
427 action = SLIDE_ACTION_FEATHER;
429 masklay = feather_masklay;
430 spline = feather_spline;
431 point = feather_point;
435 if (cv_point && action == SLIDE_ACTION_NONE) {
437 action = SLIDE_ACTION_HANDLE;
439 action = SLIDE_ACTION_POINT;
441 masklay = cv_masklay;
446 if (action != SLIDE_ACTION_NONE) {
447 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
449 customdata->mask = mask;
450 customdata->masklay = masklay;
451 customdata->spline = spline;
452 customdata->point = point;
453 customdata->width = width;
454 customdata->height = height;
455 customdata->action = action;
460 float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
462 customdata->weight = uw->w;
463 BKE_mask_point_segment_co(spline, point, uw->u, co);
464 BKE_mask_point_normal(spline, point, uw->u, customdata->no);
466 madd_v2_v2v2fl(customdata->feather, co, customdata->no, uw->w * weight_scalar);
469 BezTriple *bezt = &point->bezt;
471 customdata->weight = bezt->weight;
472 BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
474 madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
477 if (customdata->action == SLIDE_ACTION_FEATHER)
478 customdata->initial_feather = slide_point_check_initial_feather(spline);
480 copy_m3_m3(customdata->vec, point->bezt.vec);
481 if (BKE_mask_point_has_handle(point))
482 BKE_mask_point_handle(point, customdata->handle);
483 ED_mask_mouse_pos(C, event, customdata->co);
489 static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
491 SlidePointData *slidedata = slide_point_customdata(C, op, event);
494 Mask *mask = CTX_data_edit_mask(C);
496 op->customdata = slidedata;
498 WM_event_add_modal_handler(C, op);
501 if ((slidedata->uw->flag & SELECT) == 0) {
502 ED_mask_select_toggle_all(mask, SEL_DESELECT);
504 slidedata->uw->flag |= SELECT;
506 ED_mask_select_flush_all(mask);
509 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
510 ED_mask_select_toggle_all(mask, SEL_DESELECT);
512 BKE_mask_point_select_set(slidedata->point, TRUE);
514 ED_mask_select_flush_all(mask);
517 slidedata->masklay->act_spline = slidedata->spline;
518 slidedata->masklay->act_point = slidedata->point;
520 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
522 return OPERATOR_RUNNING_MODAL;
525 return OPERATOR_PASS_THROUGH;
528 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
532 for (i = 0; i < data->spline->tot_point; i++) {
533 MaskSplinePoint *point = &data->spline->points[i];
534 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
536 point->bezt.weight = orig_point->bezt.weight + delta;
537 if (point->bezt.weight < 0.0f)
538 point->bezt.weight = 0.0f;
540 /* not needed anymore */
543 for (j = 0; j < point->tot_uw; j++) {
544 point->uw[j].w = orig_point->uw[j].w + delta;
545 if (point->uw[j].w < 0.0f)
546 point->uw[j].w = 0.0f;
552 static void slide_point_restore_spline(SlidePointData *data)
556 for (i = 0; i < data->spline->tot_point; i++) {
557 MaskSplinePoint *point = &data->spline->points[i];
558 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
561 point->bezt = orig_point->bezt;
563 for (j = 0; j < point->tot_uw; j++)
564 point->uw[j] = orig_point->uw[j];
568 static void cancel_slide_point(SlidePointData *data)
572 if (data->orig_spline) {
573 slide_point_restore_spline(data);
576 if (data->action == SLIDE_ACTION_FEATHER) {
578 data->uw->w = data->weight;
580 data->point->bezt.weight = data->weight;
583 copy_m3_m3(data->point->bezt.vec, data->vec);
588 static void free_slide_point_data(SlidePointData *data)
590 if (data->orig_spline)
591 BKE_mask_spline_free(data->orig_spline);
596 static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
598 SlidePointData *data = (SlidePointData *)op->customdata;
599 BezTriple *bezt = &data->point->bezt;
602 switch (event->type) {
607 if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
608 if (data->action == SLIDE_ACTION_FEATHER)
609 data->overall_feather = event->val == KM_PRESS;
611 data->curvature_only = event->val == KM_PRESS;
614 if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
615 data->accurate = event->val == KM_PRESS;
617 /* no break! update CV position */
620 ED_mask_mouse_pos(C, event, co);
621 sub_v2_v2v2(dco, co, data->co);
623 if (data->action == SLIDE_ACTION_HANDLE) {
624 float delta[2], offco[2];
626 sub_v2_v2v2(delta, data->handle, data->co);
628 sub_v2_v2v2(offco, co, data->co);
630 mul_v2_fl(offco, 0.2f);
631 add_v2_v2(offco, data->co);
632 add_v2_v2(offco, delta);
634 BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
636 else if (data->action == SLIDE_ACTION_POINT) {
639 copy_v2_v2(delta, dco);
641 mul_v2_fl(delta, 0.2f);
643 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
644 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
645 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
647 else if (data->action == SLIDE_ACTION_FEATHER) {
648 float vec[2], no[2], p[2], c[2], w, offco[2];
649 float *weight = NULL;
650 float weight_scalar = 1.0f;
651 int overall_feather = data->overall_feather || data->initial_feather;
653 add_v2_v2v2(offco, data->feather, dco);
656 /* project on both sides and find the closest one,
657 * prevents flickering when projecting onto both sides can happen */
658 const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
659 data->uw->u, offco, MASK_PROJ_NEG);
660 const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
661 data->uw->u, offco, MASK_PROJ_POS);
662 float dist_pos = FLT_MAX;
663 float dist_neg = FLT_MAX;
668 if (u_pos > 0.0f && u_pos < 1.0f) {
669 BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
670 dist_pos = len_squared_v2v2(offco, co_pos);
673 if (u_neg > 0.0f && u_neg < 1.0f) {
674 BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
675 dist_neg = len_squared_v2v2(offco, co_neg);
678 u = dist_pos < dist_neg ? u_pos : u_neg;
680 if (u > 0.0f && u < 1.0f) {
683 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
684 weight = &data->uw->w;
685 weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
686 if (weight_scalar != 0.0f) {
687 weight_scalar = 1.0f / weight_scalar;
690 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
691 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
695 weight = &bezt->weight;
696 /* weight_scalar = 1.0f; keep as is */
697 copy_v2_v2(no, data->no);
698 copy_v2_v2(p, bezt->vec[1]);
702 sub_v2_v2v2(c, offco, p);
703 project_v2_v2v2(vec, c, no);
707 if (overall_feather) {
710 if (dot_v2v2(no, vec) <= 0.0f)
713 delta = w - data->weight;
715 if (data->orig_spline == NULL) {
716 /* restore weight for currently sliding point, so orig_spline would be created
717 * with original weights used
719 *weight = data->weight * weight_scalar;
721 data->orig_spline = BKE_mask_spline_copy(data->spline);
724 slide_point_delta_all_feather(data, delta);
727 if (dot_v2v2(no, vec) <= 0.0f)
730 if (data->orig_spline) {
731 /* restore possible overall feather changes */
732 slide_point_restore_spline(data);
734 BKE_mask_spline_free(data->orig_spline);
735 data->orig_spline = NULL;
738 if (weight_scalar != 0.0f) {
739 *weight = w * weight_scalar;
745 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
746 DAG_id_tag_update(&data->mask->id, 0);
751 if (event->val == KM_RELEASE) {
752 Scene *scene = CTX_data_scene(C);
754 /* dont key sliding feather uw's */
755 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
756 if (IS_AUTOKEY_ON(scene)) {
757 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
761 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
762 DAG_id_tag_update(&data->mask->id, 0);
764 free_slide_point_data(op->customdata); /* keep this last! */
765 return OPERATOR_FINISHED;
771 cancel_slide_point(op->customdata);
773 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
774 DAG_id_tag_update(&data->mask->id, 0);
776 free_slide_point_data(op->customdata); /* keep this last! */
777 return OPERATOR_CANCELLED;
780 return OPERATOR_RUNNING_MODAL;
783 void MASK_OT_slide_point(wmOperatorType *ot)
786 ot->name = "Slide Point";
787 ot->description = "Slide control points";
788 ot->idname = "MASK_OT_slide_point";
791 ot->invoke = slide_point_invoke;
792 ot->modal = slide_point_modal;
793 ot->poll = ED_maskedit_mask_poll;
796 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
798 RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
801 /******************** toggle cyclic *********************/
803 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
805 Mask *mask = CTX_data_edit_mask(C);
808 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
811 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
815 for (spline = masklay->splines.first; spline; spline = spline->next) {
816 if (ED_mask_spline_select_check(spline)) {
817 spline->flag ^= MASK_SPLINE_CYCLIC;
822 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
824 return OPERATOR_FINISHED;
827 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
830 ot->name = "Toggle Cyclic";
831 ot->description = "Toggle cyclic for selected splines";
832 ot->idname = "MASK_OT_cyclic_toggle";
835 ot->exec = cyclic_toggle_exec;
836 ot->poll = ED_maskedit_mask_poll;
839 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
842 /******************** delete *********************/
844 static void delete_feather_points(MaskSplinePoint *point)
851 for (i = 0; i < point->tot_uw; i++) {
852 if ((point->uw[i].flag & SELECT) == 0)
857 MEM_freeN(point->uw);
862 MaskSplinePointUW *new_uw;
865 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
867 for (i = 0; i < point->tot_uw; i++) {
868 if ((point->uw[i].flag & SELECT) == 0) {
869 new_uw[j++] = point->uw[i];
873 MEM_freeN(point->uw);
876 point->tot_uw = count;
880 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
882 Mask *mask = CTX_data_edit_mask(C);
885 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
887 int mask_layer_shape_ofs = 0;
889 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
893 spline = masklay->splines.first;
896 const int tot_point_orig = spline->tot_point;
898 MaskSpline *next_spline = spline->next;
900 /* count unselected points */
901 for (i = 0; i < spline->tot_point; i++) {
902 MaskSplinePoint *point = &spline->points[i];
904 if (!MASKPOINT_ISSEL_ANY(point))
910 /* delete the whole spline */
911 BLI_remlink(&masklay->splines, spline);
912 BKE_mask_spline_free(spline);
914 if (spline == masklay->act_spline) {
915 masklay->act_spline = NULL;
916 masklay->act_point = NULL;
919 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
922 MaskSplinePoint *new_points;
925 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
927 for (i = 0, j = 0; i < tot_point_orig; i++) {
928 MaskSplinePoint *point = &spline->points[i];
930 if (!MASKPOINT_ISSEL_ANY(point)) {
931 if (point == masklay->act_point)
932 masklay->act_point = &new_points[j];
934 delete_feather_points(point);
936 new_points[j] = *point;
940 if (point == masklay->act_point)
941 masklay->act_point = NULL;
943 BKE_mask_point_free(point);
946 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
950 mask_layer_shape_ofs += spline->tot_point;
952 MEM_freeN(spline->points);
953 spline->points = new_points;
955 ED_mask_select_flush_all(mask);
958 spline = next_spline;
961 /* not essential but confuses users when there are keys with no data!
962 * assume if they delete all data from the layer they also dont care about keys */
963 if (masklay->splines.first == NULL) {
964 BKE_mask_layer_free_shapes(masklay);
968 /* TODO: only update edited splines */
969 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
971 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
973 return OPERATOR_FINISHED;
976 void MASK_OT_delete(wmOperatorType *ot)
980 ot->description = "Delete selected control points or splines";
981 ot->idname = "MASK_OT_delete";
984 ot->invoke = WM_operator_confirm;
985 ot->exec = delete_exec;
986 ot->poll = ED_maskedit_mask_poll;
989 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
992 /* *** switch direction *** */
993 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
995 Scene *scene = CTX_data_scene(C);
996 Mask *mask = CTX_data_edit_mask(C);
1001 /* do actual selection */
1002 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1004 int change_layer = FALSE;
1006 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1010 for (spline = masklay->splines.first; spline; spline = spline->next) {
1011 if (ED_mask_spline_select_check(spline)) {
1012 BKE_mask_spline_direction_switch(masklay, spline);
1014 change_layer = TRUE;
1019 if (IS_AUTOKEY_ON(scene)) {
1020 ED_mask_layer_shape_auto_key(masklay, CFRA);
1026 /* TODO: only update this spline */
1027 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1029 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1031 return OPERATOR_FINISHED;
1034 return OPERATOR_CANCELLED;
1037 void MASK_OT_switch_direction(wmOperatorType *ot)
1040 ot->name = "Switch Direction";
1041 ot->description = "Switch direction of selected splines";
1042 ot->idname = "MASK_OT_switch_direction";
1045 ot->exec = mask_switch_direction_exec;
1046 ot->poll = ED_maskedit_mask_poll;
1049 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1053 /* *** recalc normals *** */
1054 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1056 Scene *scene = CTX_data_scene(C);
1057 Mask *mask = CTX_data_edit_mask(C);
1063 /* do actual selection */
1064 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1066 int change_layer = FALSE;
1068 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1072 for (spline = masklay->splines.first; spline; spline = spline->next) {
1073 for (i = 0; i < spline->tot_point; i++) {
1074 MaskSplinePoint *point = &spline->points[i];
1076 if (MASKPOINT_ISSEL_ANY(point)) {
1077 BKE_mask_calc_handle_point_auto(spline, point, FALSE);
1079 change_layer = TRUE;
1085 if (IS_AUTOKEY_ON(scene)) {
1086 ED_mask_layer_shape_auto_key(masklay, CFRA);
1092 /* TODO: only update this spline */
1093 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1095 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1097 return OPERATOR_FINISHED;
1100 return OPERATOR_CANCELLED;
1103 /* named to match mesh recalc normals */
1104 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1107 ot->name = "Recalc Normals";
1108 ot->description = "Re-calculate the direction of selected handles";
1109 ot->idname = "MASK_OT_normals_make_consistent";
1112 ot->exec = mask_normals_make_consistent_exec;
1113 ot->poll = ED_maskedit_mask_poll;
1116 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1120 /******************** set handle type *********************/
1122 static int set_handle_type_exec(bContext *C, wmOperator *op)
1124 Mask *mask = CTX_data_edit_mask(C);
1126 int handle_type = RNA_enum_get(op->ptr, "type");
1128 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1132 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1136 for (spline = masklay->splines.first; spline; spline = spline->next) {
1137 for (i = 0; i < spline->tot_point; i++) {
1138 MaskSplinePoint *point = &spline->points[i];
1140 if (MASKPOINT_ISSEL_ANY(point)) {
1141 BezTriple *bezt = &point->bezt;
1143 bezt->h1 = bezt->h2 = handle_type;
1149 WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1150 DAG_id_tag_update(&mask->id, 0);
1152 return OPERATOR_FINISHED;
1155 void MASK_OT_handle_type_set(wmOperatorType *ot)
1157 static EnumPropertyItem editcurve_handle_type_items[] = {
1158 {HD_AUTO, "AUTO", 0, "Auto", ""},
1159 {HD_VECT, "VECTOR", 0, "Vector", ""},
1160 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1161 {0, NULL, 0, NULL, NULL}
1165 ot->name = "Set Handle Type";
1166 ot->description = "Set type of handles for selected control points";
1167 ot->idname = "MASK_OT_handle_type_set";
1170 ot->invoke = WM_menu_invoke;
1171 ot->exec = set_handle_type_exec;
1172 ot->poll = ED_maskedit_mask_poll;
1175 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1178 ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1182 /* ********* clear/set restrict view *********/
1183 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1185 Mask *mask = CTX_data_edit_mask(C);
1187 int changed = FALSE;
1189 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1191 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1192 ED_mask_layer_select_set(masklay, TRUE);
1193 masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1199 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1200 DAG_id_tag_update(&mask->id, 0);
1202 return OPERATOR_FINISHED;
1205 return OPERATOR_CANCELLED;
1209 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1213 ot->name = "Clear Restrict View";
1214 ot->description = "Reveal the layer by setting the hide flag";
1215 ot->idname = "MASK_OT_hide_view_clear";
1218 ot->exec = mask_hide_view_clear_exec;
1219 ot->poll = ED_maskedit_mask_poll;
1222 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1225 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1227 Mask *mask = CTX_data_edit_mask(C);
1229 const int unselected = RNA_boolean_get(op->ptr, "unselected");
1230 int changed = FALSE;
1232 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1234 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1239 if (ED_mask_layer_select_check(masklay)) {
1240 ED_mask_layer_select_set(masklay, FALSE);
1242 masklay->restrictflag |= OB_RESTRICT_VIEW;
1244 if (masklay == BKE_mask_layer_active(mask)) {
1245 BKE_mask_layer_active_set(mask, NULL);
1250 if (!ED_mask_layer_select_check(masklay)) {
1251 masklay->restrictflag |= OB_RESTRICT_VIEW;
1253 if (masklay == BKE_mask_layer_active(mask)) {
1254 BKE_mask_layer_active_set(mask, NULL);
1261 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1262 DAG_id_tag_update(&mask->id, 0);
1264 return OPERATOR_FINISHED;
1267 return OPERATOR_CANCELLED;
1271 void MASK_OT_hide_view_set(wmOperatorType *ot)
1274 ot->name = "Set Restrict View";
1275 ot->description = "Hide the layer by setting the hide flag";
1276 ot->idname = "MASK_OT_hide_view_set";
1279 ot->exec = mask_hide_view_set_exec;
1280 ot->poll = ED_maskedit_mask_poll;
1283 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1285 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1289 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1291 Mask *mask = CTX_data_edit_mask(C);
1293 int changed = FALSE;
1296 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1299 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1303 for (spline = masklay->splines.first; spline; spline = spline->next) {
1304 for (i = 0; i < spline->tot_point; i++) {
1305 MaskSplinePoint *point = &spline->points[i];
1307 if (MASKPOINT_ISSEL_ANY(point)) {
1308 BezTriple *bezt = &point->bezt;
1309 bezt->weight = 0.0f;
1317 /* TODO: only update edited splines */
1318 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1320 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1321 DAG_id_tag_update(&mask->id, 0);
1323 return OPERATOR_FINISHED;
1326 return OPERATOR_CANCELLED;
1330 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1333 ot->name = "Clear Feather Weight";
1334 ot->description = "Reset the feather weight to zero";
1335 ot->idname = "MASK_OT_feather_weight_clear";
1338 ot->exec = mask_feather_weight_clear_exec;
1339 ot->poll = ED_maskedit_mask_poll;
1342 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;