Code cleanup: use bool for static methods
[blender.git] / source / blender / editors / mask / mask_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation,
23  *                 Sergey Sharybin
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mask/mask_ops.c
29  *  \ingroup edmask
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_listbase.h"
35 #include "BLI_math.h"
36
37 #include "BKE_context.h"
38 #include "BKE_depsgraph.h"
39 #include "BKE_main.h"
40 #include "BKE_mask.h"
41
42 #include "DNA_scene_types.h"
43 #include "DNA_mask_types.h"
44 #include "DNA_object_types.h"  /* SELECT */
45
46 #include "WM_api.h"
47 #include "WM_types.h"
48
49 #include "ED_clip.h"
50 #include "ED_image.h"
51 #include "ED_keyframing.h"
52 #include "ED_mask.h"
53 #include "ED_screen.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "mask_intern.h"  /* own include */
59
60 /******************** utility functions *********************/
61
62 MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask, const float normal_co[2], const float threshold,
63                                             MaskLayer **masklay_r, MaskSpline **spline_r, bool *is_handle_r,
64                                             float *score)
65 {
66         ScrArea *sa = CTX_wm_area(C);
67         ARegion *ar = CTX_wm_region(C);
68
69         MaskLayer *masklay;
70         MaskLayer *point_masklay = NULL;
71         MaskSpline *point_spline = NULL;
72         MaskSplinePoint *point = NULL;
73         float co[2];
74         const float threshold_sq = threshold * threshold;
75         float len_sq = FLT_MAX, scalex, scaley;
76         int is_handle = FALSE, width, height;
77
78         ED_mask_get_size(sa, &width, &height);
79         ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
80
81         co[0] = normal_co[0] * scalex;
82         co[1] = normal_co[1] * scaley;
83
84         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
85                 MaskSpline *spline;
86
87                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
88                         continue;
89                 }
90
91                 for (spline = masklay->splines.first; spline; spline = spline->next) {
92                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
93
94                         int i;
95
96                         for (i = 0; i < spline->tot_point; i++) {
97                                 MaskSplinePoint *cur_point = &spline->points[i];
98                                 MaskSplinePoint *cur_point_deform = &points_array[i];
99                                 float cur_len_sq, vec[2], handle[2];
100
101                                 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
102                                 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
103
104                                 if (BKE_mask_point_has_handle(cur_point)) {
105                                         BKE_mask_point_handle(cur_point_deform, handle);
106                                         handle[0] *= scalex;
107                                         handle[1] *= scaley;
108
109                                         cur_len_sq = len_squared_v2v2(co, handle);
110
111                                         if (cur_len_sq < len_sq) {
112                                                 point_masklay = masklay;
113                                                 point_spline = spline;
114                                                 point = cur_point;
115                                                 len_sq = cur_len_sq;
116                                                 is_handle = TRUE;
117                                         }
118                                 }
119
120                                 cur_len_sq = len_squared_v2v2(co, vec);
121
122                                 if (cur_len_sq < len_sq) {
123                                         point_spline = spline;
124                                         point_masklay = masklay;
125                                         point = cur_point;
126                                         len_sq = cur_len_sq;
127                                         is_handle = FALSE;
128                                 }
129                         }
130                 }
131         }
132
133         if (len_sq < threshold_sq) {
134                 if (masklay_r)
135                         *masklay_r = point_masklay;
136
137                 if (spline_r)
138                         *spline_r = point_spline;
139
140                 if (is_handle_r)
141                         *is_handle_r = is_handle;
142
143                 if (score)
144                         *score = sqrtf(len_sq);
145
146                 return point;
147         }
148
149         if (masklay_r)
150                 *masklay_r = NULL;
151
152         if (spline_r)
153                 *spline_r = NULL;
154
155         if (is_handle_r)
156                 *is_handle_r = FALSE;
157
158         return NULL;
159 }
160
161 bool ED_mask_feather_find_nearest(const bContext *C, Mask *mask, const float normal_co[2], const float threshold,
162                                   MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
163                                   MaskSplinePointUW **uw_r, float *score)
164 {
165         ScrArea *sa = CTX_wm_area(C);
166         ARegion *ar = CTX_wm_region(C);
167
168         MaskLayer *masklay, *point_masklay = NULL;
169         MaskSpline *point_spline = NULL;
170         MaskSplinePoint *point = NULL;
171         MaskSplinePointUW *uw = NULL;
172         const float threshold_sq = threshold * threshold;
173         float len = FLT_MAX, co[2];
174         float scalex, scaley;
175         int width, height;
176
177         ED_mask_get_size(sa, &width, &height);
178         ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
179
180         co[0] = normal_co[0] * scalex;
181         co[1] = normal_co[1] * scaley;
182
183         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
184                 MaskSpline *spline;
185
186                 for (spline = masklay->splines.first; spline; spline = spline->next) {
187                         //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
188
189                         int i, tot_feather_point;
190                         float (*feather_points)[2], (*fp)[2];
191
192                         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
193                                 continue;
194                         }
195
196                         feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
197
198                         for (i = 0; i < spline->tot_point; i++) {
199                                 int j;
200                                 MaskSplinePoint *cur_point = &spline->points[i];
201
202                                 for (j = 0; j <= cur_point->tot_uw; j++) {
203                                         float cur_len_sq, vec[2];
204
205                                         vec[0] = (*fp)[0] * scalex;
206                                         vec[1] = (*fp)[1] * scaley;
207
208                                         cur_len_sq = len_squared_v2v2(vec, co);
209
210                                         if (point == NULL || cur_len_sq < len) {
211                                                 if (j == 0)
212                                                         uw = NULL;
213                                                 else
214                                                         uw = &cur_point->uw[j - 1];
215
216                                                 point_masklay = masklay;
217                                                 point_spline = spline;
218                                                 point = cur_point;
219                                                 len = cur_len_sq;
220                                         }
221
222                                         fp++;
223                                 }
224                         }
225
226                         MEM_freeN(feather_points);
227                 }
228         }
229
230         if (len < threshold_sq) {
231                 if (masklay_r)
232                         *masklay_r = point_masklay;
233
234                 if (spline_r)
235                         *spline_r = point_spline;
236
237                 if (point_r)
238                         *point_r = point;
239
240                 if (uw_r)
241                         *uw_r = uw;
242
243                 if (score)
244                         *score = sqrtf(len);
245
246                 return TRUE;
247         }
248
249         if (masklay_r)
250                 *masklay_r = NULL;
251
252         if (spline_r)
253                 *spline_r = NULL;
254
255         if (point_r)
256                 *point_r = NULL;
257
258         return FALSE;
259 }
260
261
262 /******************** create new mask *********************/
263
264 Mask *ED_mask_new(bContext *C, const char *name)
265 {
266         ScrArea *sa = CTX_wm_area(C);
267         Main *bmain = CTX_data_main(C);
268         Mask *mask;
269
270         mask = BKE_mask_new(bmain, name);
271
272         if (sa && sa->spacedata.first) {
273                 switch (sa->spacetype) {
274                         case SPACE_CLIP:
275                         {
276                                 SpaceClip *sc = sa->spacedata.first;
277                                 ED_space_clip_set_mask(C, sc, mask);
278                                 break;
279                         }
280                         case SPACE_SEQ:
281                         {
282                                 /* do nothing */
283                                 break;
284                         }
285                         case SPACE_IMAGE:
286                         {
287                                 SpaceImage *sima = sa->spacedata.first;
288                                 ED_space_image_set_mask(C, sima, mask);
289                                 break;
290                         }
291                 }
292         }
293
294         return mask;
295 }
296
297 /* Get ative layer. Will create mask/layer to be sure there's an active layer.  */
298 MaskLayer *ED_mask_layer_ensure(bContext *C)
299 {
300         Mask *mask = CTX_data_edit_mask(C);
301         MaskLayer *mask_layer;
302
303         if (mask == NULL) {
304                 /* If there's no active mask, create one. */
305                 mask = ED_mask_new(C, NULL);
306         }
307
308         mask_layer = BKE_mask_layer_active(mask);
309         if (mask_layer == NULL) {
310                 /* If there's no active mask layer, create one. */
311                 mask_layer = BKE_mask_layer_new(mask, "");
312         }
313
314         return mask_layer;
315 }
316
317 static int mask_new_exec(bContext *C, wmOperator *op)
318 {
319         char name[MAX_ID_NAME - 2];
320
321         RNA_string_get(op->ptr, "name", name);
322
323         ED_mask_new(C, name);
324
325         return OPERATOR_FINISHED;
326 }
327
328 void MASK_OT_new(wmOperatorType *ot)
329 {
330         /* identifiers */
331         ot->name = "New Mask";
332         ot->description = "Create new mask";
333         ot->idname = "MASK_OT_new";
334
335         /* flags */
336         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
337
338         /* api callbacks */
339         ot->exec = mask_new_exec;
340         ot->poll = ED_operator_mask;
341
342         /* properties */
343         RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask");
344 }
345
346 /******************** create new masklay *********************/
347
348 static int masklay_new_exec(bContext *C, wmOperator *op)
349 {
350         Mask *mask = CTX_data_edit_mask(C);
351         char name[MAX_ID_NAME - 2];
352
353         RNA_string_get(op->ptr, "name", name);
354
355         BKE_mask_layer_new(mask, name);
356         mask->masklay_act = mask->masklay_tot - 1;
357
358         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
359
360         return OPERATOR_FINISHED;
361 }
362
363 void MASK_OT_layer_new(wmOperatorType *ot)
364 {
365         /* identifiers */
366         ot->name = "Add Mask Layer";
367         ot->description = "Add new mask layer for masking";
368         ot->idname = "MASK_OT_layer_new";
369
370         /* api callbacks */
371         ot->exec = masklay_new_exec;
372         ot->poll = ED_maskedit_poll;
373
374         /* flags */
375         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
376
377         /* properties */
378         RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask layer");
379 }
380
381 /******************** remove mask layer *********************/
382
383 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
384 {
385         Mask *mask = CTX_data_edit_mask(C);
386         MaskLayer *masklay = BKE_mask_layer_active(mask);
387
388         if (masklay) {
389                 BKE_mask_layer_remove(mask, masklay);
390
391                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
392         }
393
394         return OPERATOR_FINISHED;
395 }
396
397 void MASK_OT_layer_remove(wmOperatorType *ot)
398 {
399         /* identifiers */
400         ot->name = "Remove Mask Layer";
401         ot->description = "Remove mask layer";
402         ot->idname = "MASK_OT_layer_remove";
403
404         /* api callbacks */
405         ot->exec = masklay_remove_exec;
406         ot->poll = ED_maskedit_poll;
407
408         /* flags */
409         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
410 }
411
412 /******************** slide *********************/
413
414 enum {
415         SLIDE_ACTION_NONE    = 0,
416         SLIDE_ACTION_POINT   = 1,
417         SLIDE_ACTION_HANDLE  = 2,
418         SLIDE_ACTION_FEATHER = 3
419 };
420
421 typedef struct SlidePointData {
422         int action;
423
424         float co[2];
425         float vec[3][3];
426
427         Mask *mask;
428         MaskLayer *masklay;
429         MaskSpline *spline, *orig_spline;
430         MaskSplinePoint *point;
431         MaskSplinePointUW *uw;
432         float handle[2], no[2], feather[2];
433         int width, height;
434         float weight, weight_scalar;
435
436         short curvature_only, accurate;
437         short initial_feather, overall_feather;
438 } SlidePointData;
439
440 static bool slide_point_check_initial_feather(MaskSpline *spline)
441 {
442         int i;
443
444         for (i = 0; i < spline->tot_point; i++) {
445                 MaskSplinePoint *point = &spline->points[i];
446
447                 if (point->bezt.weight != 0.0f)
448                         return FALSE;
449
450                 /* comment for now. if all bezt weights are zero - this is as good-as initial */
451 #if 0
452                 int j;
453                 for (j = 0; j < point->tot_uw; j++) {
454                         if (point->uw[j].w != 0.0f)
455                                 return FALSE;
456                 }
457 #endif
458         }
459
460         return TRUE;
461 }
462
463 static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *event)
464 {
465         ScrArea *sa = CTX_wm_area(C);
466         ARegion *ar = CTX_wm_region(C);
467
468         Mask *mask = CTX_data_edit_mask(C);
469         SlidePointData *customdata = NULL;
470         MaskLayer *masklay, *cv_masklay, *feather_masklay;
471         MaskSpline *spline, *cv_spline, *feather_spline;
472         MaskSplinePoint *point, *cv_point, *feather_point;
473         MaskSplinePointUW *uw = NULL;
474         int width, height, action = SLIDE_ACTION_NONE;
475         bool is_handle = false;
476         const bool slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
477         float co[2], cv_score, feather_score;
478         const float threshold = 19;
479
480         ED_mask_mouse_pos(sa, ar, event->mval, co);
481         ED_mask_get_size(sa, &width, &height);
482
483         cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
484
485         if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
486                 if (slide_feather || !cv_point || feather_score < cv_score) {
487                         action = SLIDE_ACTION_FEATHER;
488
489                         masklay = feather_masklay;
490                         spline = feather_spline;
491                         point = feather_point;
492                 }
493         }
494
495         if (cv_point && action == SLIDE_ACTION_NONE) {
496                 if (is_handle)
497                         action = SLIDE_ACTION_HANDLE;
498                 else
499                         action = SLIDE_ACTION_POINT;
500
501                 masklay = cv_masklay;
502                 spline = cv_spline;
503                 point = cv_point;
504         }
505
506         if (action != SLIDE_ACTION_NONE) {
507                 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
508
509                 customdata->mask = mask;
510                 customdata->masklay = masklay;
511                 customdata->spline = spline;
512                 customdata->point = point;
513                 customdata->width = width;
514                 customdata->height = height;
515                 customdata->action = action;
516                 customdata->uw = uw;
517
518                 if (uw) {
519                         float co_uw[2];
520                         float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
521
522                         customdata->weight = uw->w;
523                         customdata->weight_scalar = weight_scalar;
524                         BKE_mask_point_segment_co(spline, point, uw->u, co_uw);
525                         BKE_mask_point_normal(spline, point, uw->u, customdata->no);
526
527                         madd_v2_v2v2fl(customdata->feather, co_uw, customdata->no, uw->w * weight_scalar);
528                 }
529                 else {
530                         BezTriple *bezt = &point->bezt;
531
532                         customdata->weight = bezt->weight;
533                         customdata->weight_scalar = 1.0f;
534                         BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
535
536                         madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
537                 }
538
539                 if (customdata->action == SLIDE_ACTION_FEATHER)
540                         customdata->initial_feather = slide_point_check_initial_feather(spline);
541
542                 copy_m3_m3(customdata->vec, point->bezt.vec);
543                 if (BKE_mask_point_has_handle(point))
544                         BKE_mask_point_handle(point, customdata->handle);
545                 ED_mask_mouse_pos(sa, ar, event->mval, customdata->co);
546         }
547
548         return customdata;
549 }
550
551 static int slide_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
552 {
553         SlidePointData *slidedata = slide_point_customdata(C, op, event);
554
555         if (slidedata) {
556                 Mask *mask = CTX_data_edit_mask(C);
557
558                 op->customdata = slidedata;
559
560                 WM_event_add_modal_handler(C, op);
561
562 #if 0
563                 if (slidedata->uw) {
564                         if ((slidedata->uw->flag & SELECT) == 0) {
565                                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
566
567                                 slidedata->uw->flag |= SELECT;
568
569                                 ED_mask_select_flush_all(mask);
570                         }
571                 }
572                 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
573                         ED_mask_select_toggle_all(mask, SEL_DESELECT);
574
575                         BKE_mask_point_select_set(slidedata->point, TRUE);
576
577                         ED_mask_select_flush_all(mask);
578                 }
579 #endif
580
581                 slidedata->masklay->act_spline = slidedata->spline;
582                 slidedata->masklay->act_point = slidedata->point;
583
584                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
585
586                 return OPERATOR_RUNNING_MODAL;
587         }
588
589         return OPERATOR_PASS_THROUGH;
590 }
591
592 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
593 {
594         int i;
595
596         for (i = 0; i < data->spline->tot_point; i++) {
597                 MaskSplinePoint *point = &data->spline->points[i];
598                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
599
600                 point->bezt.weight = orig_point->bezt.weight + delta;
601                 if (point->bezt.weight < 0.0f)
602                         point->bezt.weight = 0.0f;
603
604                 /* not needed anymore */
605 #if 0
606                 int j;
607                 for (j = 0; j < point->tot_uw; j++) {
608                         point->uw[j].w = orig_point->uw[j].w + delta;
609                         if (point->uw[j].w < 0.0f)
610                                 point->uw[j].w = 0.0f;
611                 }
612 #endif
613         }
614 }
615
616 static void slide_point_restore_spline(SlidePointData *data)
617 {
618         int i;
619
620         for (i = 0; i < data->spline->tot_point; i++) {
621                 MaskSplinePoint *point = &data->spline->points[i];
622                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
623                 int j;
624
625                 point->bezt = orig_point->bezt;
626
627                 for (j = 0; j < point->tot_uw; j++)
628                         point->uw[j] = orig_point->uw[j];
629         }
630 }
631
632 static void cancel_slide_point(SlidePointData *data)
633 {
634         /* cancel sliding */
635
636         if (data->orig_spline) {
637                 slide_point_restore_spline(data);
638         }
639         else {
640                 if (data->action == SLIDE_ACTION_FEATHER) {
641                         if (data->uw)
642                                 data->uw->w = data->weight;
643                         else
644                                 data->point->bezt.weight = data->weight;
645                 }
646                 else {
647                         copy_m3_m3(data->point->bezt.vec, data->vec);
648                 }
649         }
650 }
651
652 static void free_slide_point_data(SlidePointData *data)
653 {
654         if (data->orig_spline)
655                 BKE_mask_spline_free(data->orig_spline);
656
657         MEM_freeN(data);
658 }
659
660 static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
661 {
662         SlidePointData *data = (SlidePointData *)op->customdata;
663         BezTriple *bezt = &data->point->bezt;
664         float co[2], dco[2];
665
666         switch (event->type) {
667                 case LEFTALTKEY:
668                 case RIGHTALTKEY:
669                 case LEFTSHIFTKEY:
670                 case RIGHTSHIFTKEY:
671                         if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) {
672                                 if (data->action == SLIDE_ACTION_FEATHER)
673                                         data->overall_feather = (event->val == KM_PRESS);
674                                 else
675                                         data->curvature_only = (event->val == KM_PRESS);
676                         }
677
678                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
679                                 data->accurate = (event->val == KM_PRESS);
680
681                         /* fall-through */  /* update CV position */
682                 case MOUSEMOVE:
683                 {
684                         ScrArea *sa = CTX_wm_area(C);
685                         ARegion *ar = CTX_wm_region(C);
686
687                         ED_mask_mouse_pos(sa, ar, event->mval, co);
688                         sub_v2_v2v2(dco, co, data->co);
689
690                         if (data->action == SLIDE_ACTION_HANDLE) {
691                                 float delta[2], offco[2];
692
693                                 sub_v2_v2v2(delta, data->handle, data->co);
694
695                                 sub_v2_v2v2(offco, co, data->co);
696                                 if (data->accurate)
697                                         mul_v2_fl(offco, 0.2f);
698                                 add_v2_v2(offco, data->co);
699                                 add_v2_v2(offco, delta);
700
701                                 BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
702                         }
703                         else if (data->action == SLIDE_ACTION_POINT) {
704                                 float delta[2];
705
706                                 copy_v2_v2(delta, dco);
707                                 if (data->accurate)
708                                         mul_v2_fl(delta, 0.2f);
709
710                                 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
711                                 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
712                                 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
713                         }
714                         else if (data->action == SLIDE_ACTION_FEATHER) {
715                                 float vec[2], no[2], p[2], c[2], w, offco[2];
716                                 float *weight = NULL;
717                                 float weight_scalar = 1.0f;
718                                 int overall_feather = data->overall_feather || data->initial_feather;
719
720                                 add_v2_v2v2(offco, data->feather, dco);
721
722                                 if (data->uw) {
723                                         /* project on both sides and find the closest one,
724                                          * prevents flickering when projecting onto both sides can happen */
725                                         const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
726                                                                                        data->uw->u, offco, MASK_PROJ_NEG);
727                                         const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
728                                                                                        data->uw->u, offco, MASK_PROJ_POS);
729                                         float dist_pos = FLT_MAX;
730                                         float dist_neg = FLT_MAX;
731                                         float co_pos[2];
732                                         float co_neg[2];
733                                         float u;
734
735                                         if (u_pos > 0.0f && u_pos < 1.0f) {
736                                                 BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
737                                                 dist_pos = len_squared_v2v2(offco, co_pos);
738                                         }
739
740                                         if (u_neg > 0.0f && u_neg < 1.0f) {
741                                                 BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
742                                                 dist_neg = len_squared_v2v2(offco, co_neg);
743                                         }
744
745                                         u = dist_pos < dist_neg ? u_pos : u_neg;
746
747                                         if (u > 0.0f && u < 1.0f) {
748                                                 data->uw->u = u;
749
750                                                 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
751                                                 weight = &data->uw->w;
752                                                 weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
753                                                 if (weight_scalar != 0.0f) {
754                                                         weight_scalar = 1.0f / weight_scalar;
755                                                 }
756
757                                                 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
758                                                 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
759                                         }
760                                 }
761                                 else {
762                                         weight = &bezt->weight;
763                                         /* weight_scalar = 1.0f; keep as is */
764                                         copy_v2_v2(no, data->no);
765                                         copy_v2_v2(p, bezt->vec[1]);
766                                 }
767
768                                 if (weight) {
769                                         sub_v2_v2v2(c, offco, p);
770                                         project_v2_v2v2(vec, c, no);
771
772                                         w = len_v2(vec);
773
774                                         if (overall_feather) {
775                                                 float delta;
776
777                                                 if (dot_v2v2(no, vec) <= 0.0f)
778                                                         w = -w;
779
780                                                 delta = w - data->weight * data->weight_scalar;
781
782                                                 if (data->orig_spline == NULL) {
783                                                         /* restore weight for currently sliding point, so orig_spline would be created
784                                                          * with original weights used
785                                                          */
786                                                         *weight = data->weight;
787
788                                                         data->orig_spline = BKE_mask_spline_copy(data->spline);
789                                                 }
790
791                                                 slide_point_delta_all_feather(data, delta);
792                                         }
793                                         else {
794                                                 if (dot_v2v2(no, vec) <= 0.0f)
795                                                         w = 0.0f;
796
797                                                 if (data->orig_spline) {
798                                                         /* restore possible overall feather changes */
799                                                         slide_point_restore_spline(data);
800
801                                                         BKE_mask_spline_free(data->orig_spline);
802                                                         data->orig_spline = NULL;
803                                                 }
804
805                                                 if (weight_scalar != 0.0f) {
806                                                         *weight = w * weight_scalar;
807                                                 }
808                                         }
809                                 }
810                         }
811
812                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
813                         DAG_id_tag_update(&data->mask->id, 0);
814
815                         break;
816                 }
817
818                 case LEFTMOUSE:
819                         if (event->val == KM_RELEASE) {
820                                 Scene *scene = CTX_data_scene(C);
821
822                                 /* dont key sliding feather uw's */
823                                 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
824                                         if (IS_AUTOKEY_ON(scene)) {
825                                                 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
826                                         }
827                                 }
828
829                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
830                                 DAG_id_tag_update(&data->mask->id, 0);
831
832                                 free_slide_point_data(op->customdata); /* keep this last! */
833                                 return OPERATOR_FINISHED;
834                         }
835
836                         break;
837
838                 case ESCKEY:
839                         cancel_slide_point(op->customdata);
840
841                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
842                         DAG_id_tag_update(&data->mask->id, 0);
843
844                         free_slide_point_data(op->customdata); /* keep this last! */
845                         return OPERATOR_CANCELLED;
846         }
847
848         return OPERATOR_RUNNING_MODAL;
849 }
850
851 void MASK_OT_slide_point(wmOperatorType *ot)
852 {
853         /* identifiers */
854         ot->name = "Slide Point";
855         ot->description = "Slide control points";
856         ot->idname = "MASK_OT_slide_point";
857
858         /* api callbacks */
859         ot->invoke = slide_point_invoke;
860         ot->modal = slide_point_modal;
861         ot->poll = ED_maskedit_mask_poll;
862
863         /* flags */
864         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
865
866         RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
867 }
868
869 /******************** toggle cyclic *********************/
870
871 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
872 {
873         Mask *mask = CTX_data_edit_mask(C);
874         MaskLayer *masklay;
875
876         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
877                 MaskSpline *spline;
878
879                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
880                         continue;
881                 }
882
883                 for (spline = masklay->splines.first; spline; spline = spline->next) {
884                         if (ED_mask_spline_select_check(spline)) {
885                                 spline->flag ^= MASK_SPLINE_CYCLIC;
886                         }
887                 }
888         }
889
890         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
891
892         return OPERATOR_FINISHED;
893 }
894
895 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
896 {
897         /* identifiers */
898         ot->name = "Toggle Cyclic";
899         ot->description = "Toggle cyclic for selected splines";
900         ot->idname = "MASK_OT_cyclic_toggle";
901
902         /* api callbacks */
903         ot->exec = cyclic_toggle_exec;
904         ot->poll = ED_maskedit_mask_poll;
905
906         /* flags */
907         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
908 }
909
910 /******************** delete *********************/
911
912 static void delete_feather_points(MaskSplinePoint *point)
913 {
914         int i, count = 0;
915
916         if (!point->tot_uw)
917                 return;
918
919         for (i = 0; i < point->tot_uw; i++) {
920                 if ((point->uw[i].flag & SELECT) == 0)
921                         count++;
922         }
923
924         if (count == 0) {
925                 MEM_freeN(point->uw);
926                 point->uw = NULL;
927                 point->tot_uw = 0;
928         }
929         else {
930                 MaskSplinePointUW *new_uw;
931                 int j = 0;
932
933                 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
934
935                 for (i = 0; i < point->tot_uw; i++) {
936                         if ((point->uw[i].flag & SELECT) == 0) {
937                                 new_uw[j++] = point->uw[i];
938                         }
939                 }
940
941                 MEM_freeN(point->uw);
942
943                 point->uw = new_uw;
944                 point->tot_uw = count;
945         }
946 }
947
948 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
949 {
950         Scene *scene = CTX_data_scene(C);
951         Mask *mask = CTX_data_edit_mask(C);
952         MaskLayer *masklay;
953         bool changed = false;
954
955         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
956                 MaskSpline *spline;
957                 int mask_layer_shape_ofs = 0;
958
959                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
960                         continue;
961                 }
962
963                 spline = masklay->splines.first;
964
965                 while (spline) {
966                         const int tot_point_orig = spline->tot_point;
967                         int i, count = 0;
968                         MaskSpline *next_spline = spline->next;
969
970                         /* count unselected points */
971                         for (i = 0; i < spline->tot_point; i++) {
972                                 MaskSplinePoint *point = &spline->points[i];
973
974                                 if (!MASKPOINT_ISSEL_ANY(point))
975                                         count++;
976                         }
977
978                         if (count == 0) {
979                                 /* delete the whole spline */
980                                 BLI_remlink(&masklay->splines, spline);
981                                 BKE_mask_spline_free(spline);
982
983                                 if (spline == masklay->act_spline) {
984                                         masklay->act_spline = NULL;
985                                         masklay->act_point = NULL;
986                                 }
987
988                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
989                         }
990                         else {
991                                 MaskSplinePoint *new_points;
992                                 int j;
993
994                                 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
995
996                                 for (i = 0, j = 0; i < tot_point_orig; i++) {
997                                         MaskSplinePoint *point = &spline->points[i];
998
999                                         if (!MASKPOINT_ISSEL_ANY(point)) {
1000                                                 if (point == masklay->act_point)
1001                                                         masklay->act_point = &new_points[j];
1002
1003                                                 delete_feather_points(point);
1004
1005                                                 new_points[j] = *point;
1006                                                 j++;
1007                                         }
1008                                         else {
1009                                                 if (point == masklay->act_point)
1010                                                         masklay->act_point = NULL;
1011
1012                                                 BKE_mask_point_free(point);
1013                                                 spline->tot_point--;
1014
1015                                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
1016                                         }
1017                                 }
1018
1019                                 mask_layer_shape_ofs += spline->tot_point;
1020
1021                                 MEM_freeN(spline->points);
1022                                 spline->points = new_points;
1023
1024                                 ED_mask_select_flush_all(mask);
1025                         }
1026
1027                         changed = true;
1028                         spline = next_spline;
1029                 }
1030
1031                 /* not essential but confuses users when there are keys with no data!
1032                  * assume if they delete all data from the layer they also dont care about keys */
1033                 if (masklay->splines.first == NULL) {
1034                         BKE_mask_layer_free_shapes(masklay);
1035                 }
1036         }
1037
1038         if (!changed) {
1039                 return OPERATOR_CANCELLED;
1040         }
1041
1042         /* TODO: only update edited splines */
1043         BKE_mask_update_display(mask, CFRA);
1044
1045         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1046
1047         return OPERATOR_FINISHED;
1048 }
1049
1050 void MASK_OT_delete(wmOperatorType *ot)
1051 {
1052         /* identifiers */
1053         ot->name = "Delete";
1054         ot->description = "Delete selected control points or splines";
1055         ot->idname = "MASK_OT_delete";
1056
1057         /* api callbacks */
1058         ot->invoke = WM_operator_confirm;
1059         ot->exec = delete_exec;
1060         ot->poll = ED_maskedit_mask_poll;
1061
1062         /* flags */
1063         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1064 }
1065
1066 /* *** switch direction *** */
1067 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
1068 {
1069         Scene *scene = CTX_data_scene(C);
1070         Mask *mask = CTX_data_edit_mask(C);
1071         MaskLayer *masklay;
1072
1073         bool changed = false;
1074
1075         /* do actual selection */
1076         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1077                 MaskSpline *spline;
1078                 bool changed_layer = false;
1079
1080                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1081                         continue;
1082                 }
1083
1084                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1085                         if (ED_mask_spline_select_check(spline)) {
1086                                 BKE_mask_spline_direction_switch(masklay, spline);
1087                                 changed = true;
1088                                 changed_layer = true;
1089                         }
1090                 }
1091
1092                 if (changed_layer) {
1093                         if (IS_AUTOKEY_ON(scene)) {
1094                                 ED_mask_layer_shape_auto_key(masklay, CFRA);
1095                         }
1096                 }
1097         }
1098
1099         if (changed) {
1100                 /* TODO: only update this spline */
1101                 BKE_mask_update_display(mask, CFRA);
1102
1103                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1104                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1105
1106                 return OPERATOR_FINISHED;
1107         }
1108
1109         return OPERATOR_CANCELLED;
1110 }
1111
1112 void MASK_OT_switch_direction(wmOperatorType *ot)
1113 {
1114         /* identifiers */
1115         ot->name = "Switch Direction";
1116         ot->description = "Switch direction of selected splines";
1117         ot->idname = "MASK_OT_switch_direction";
1118
1119         /* api callbacks */
1120         ot->exec = mask_switch_direction_exec;
1121         ot->poll = ED_maskedit_mask_poll;
1122
1123         /* flags */
1124         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1125 }
1126
1127
1128 /* *** recalc normals *** */
1129 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1130 {
1131         Scene *scene = CTX_data_scene(C);
1132         Mask *mask = CTX_data_edit_mask(C);
1133         MaskLayer *masklay;
1134         int i;
1135
1136         bool changed = false;
1137
1138         /* do actual selection */
1139         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1140                 MaskSpline *spline;
1141                 bool changed_layer = false;
1142
1143                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1144                         continue;
1145                 }
1146
1147                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1148                         for (i = 0; i < spline->tot_point; i++) {
1149                                 MaskSplinePoint *point = &spline->points[i];
1150
1151                                 if (MASKPOINT_ISSEL_ANY(point)) {
1152                                         BKE_mask_calc_handle_point_auto(spline, point, FALSE);
1153                                         changed = true;
1154                                         changed_layer = true;
1155                                 }
1156                         }
1157                 }
1158
1159                 if (changed_layer) {
1160                         if (IS_AUTOKEY_ON(scene)) {
1161                                 ED_mask_layer_shape_auto_key(masklay, CFRA);
1162                         }
1163                 }
1164         }
1165
1166         if (changed) {
1167                 /* TODO: only update this spline */
1168                 BKE_mask_update_display(mask, CFRA);
1169
1170                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1171                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1172
1173                 return OPERATOR_FINISHED;
1174         }
1175
1176         return OPERATOR_CANCELLED;
1177 }
1178
1179 /* named to match mesh recalc normals */
1180 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1181 {
1182         /* identifiers */
1183         ot->name = "Recalc Normals";
1184         ot->description = "Re-calculate the direction of selected handles";
1185         ot->idname = "MASK_OT_normals_make_consistent";
1186
1187         /* api callbacks */
1188         ot->exec = mask_normals_make_consistent_exec;
1189         ot->poll = ED_maskedit_mask_poll;
1190
1191         /* flags */
1192         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1193 }
1194
1195
1196 /******************** set handle type *********************/
1197
1198 static int set_handle_type_exec(bContext *C, wmOperator *op)
1199 {
1200         Mask *mask = CTX_data_edit_mask(C);
1201         MaskLayer *masklay;
1202         int handle_type = RNA_enum_get(op->ptr, "type");
1203
1204         bool changed = false;
1205
1206         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1207                 MaskSpline *spline;
1208                 int i;
1209
1210                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1211                         continue;
1212                 }
1213
1214                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1215                         for (i = 0; i < spline->tot_point; i++) {
1216                                 MaskSplinePoint *point = &spline->points[i];
1217
1218                                 if (MASKPOINT_ISSEL_ANY(point)) {
1219                                         BezTriple *bezt = &point->bezt;
1220
1221                                         bezt->h1 = bezt->h2 = handle_type;
1222                                         changed = true;
1223                                 }
1224                         }
1225                 }
1226         }
1227
1228         if (changed) {
1229                 WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1230                 DAG_id_tag_update(&mask->id, 0);
1231
1232                 return OPERATOR_FINISHED;
1233         }
1234         return OPERATOR_CANCELLED;
1235 }
1236
1237 void MASK_OT_handle_type_set(wmOperatorType *ot)
1238 {
1239         static EnumPropertyItem editcurve_handle_type_items[] = {
1240                 {HD_AUTO, "AUTO", 0, "Auto", ""},
1241                 {HD_VECT, "VECTOR", 0, "Vector", ""},
1242                 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1243                 {0, NULL, 0, NULL, NULL}
1244         };
1245
1246         /* identifiers */
1247         ot->name = "Set Handle Type";
1248         ot->description = "Set type of handles for selected control points";
1249         ot->idname = "MASK_OT_handle_type_set";
1250
1251         /* api callbacks */
1252         ot->invoke = WM_menu_invoke;
1253         ot->exec = set_handle_type_exec;
1254         ot->poll = ED_maskedit_mask_poll;
1255
1256         /* flags */
1257         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1258
1259         /* properties */
1260         ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1261 }
1262
1263
1264 /* ********* clear/set restrict view *********/
1265 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1266 {
1267         Mask *mask = CTX_data_edit_mask(C);
1268         MaskLayer *masklay;
1269         bool changed = false;
1270
1271         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1272
1273                 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1274                         ED_mask_layer_select_set(masklay, TRUE);
1275                         masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1276                         changed = true;
1277                 }
1278         }
1279
1280         if (changed) {
1281                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1282                 DAG_id_tag_update(&mask->id, 0);
1283
1284                 return OPERATOR_FINISHED;
1285         }
1286         else {
1287                 return OPERATOR_CANCELLED;
1288         }
1289 }
1290
1291 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1292 {
1293
1294         /* identifiers */
1295         ot->name = "Clear Restrict View";
1296         ot->description = "Reveal the layer by setting the hide flag";
1297         ot->idname = "MASK_OT_hide_view_clear";
1298
1299         /* api callbacks */
1300         ot->exec = mask_hide_view_clear_exec;
1301         ot->poll = ED_maskedit_mask_poll;
1302
1303         /* flags */
1304         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1305 }
1306
1307 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1308 {
1309         Mask *mask = CTX_data_edit_mask(C);
1310         MaskLayer *masklay;
1311         const int unselected = RNA_boolean_get(op->ptr, "unselected");
1312         bool changed = false;
1313
1314         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1315
1316                 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1317                         continue;
1318                 }
1319
1320                 if (!unselected) {
1321                         if (ED_mask_layer_select_check(masklay)) {
1322                                 ED_mask_layer_select_set(masklay, FALSE);
1323
1324                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1325                                 changed = true;
1326                                 if (masklay == BKE_mask_layer_active(mask)) {
1327                                         BKE_mask_layer_active_set(mask, NULL);
1328                                 }
1329                         }
1330                 }
1331                 else {
1332                         if (!ED_mask_layer_select_check(masklay)) {
1333                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1334                                 changed = true;
1335                                 if (masklay == BKE_mask_layer_active(mask)) {
1336                                         BKE_mask_layer_active_set(mask, NULL);
1337                                 }
1338                         }
1339                 }
1340         }
1341
1342         if (changed) {
1343                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1344                 DAG_id_tag_update(&mask->id, 0);
1345
1346                 return OPERATOR_FINISHED;
1347         }
1348         else {
1349                 return OPERATOR_CANCELLED;
1350         }
1351 }
1352
1353 void MASK_OT_hide_view_set(wmOperatorType *ot)
1354 {
1355         /* identifiers */
1356         ot->name = "Set Restrict View";
1357         ot->description = "Hide the layer by setting the hide flag";
1358         ot->idname = "MASK_OT_hide_view_set";
1359
1360         /* api callbacks */
1361         ot->exec = mask_hide_view_set_exec;
1362         ot->poll = ED_maskedit_mask_poll;
1363
1364         /* flags */
1365         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1366
1367         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1368 }
1369
1370
1371 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1372 {
1373         Scene *scene = CTX_data_scene(C);
1374         Mask *mask = CTX_data_edit_mask(C);
1375         MaskLayer *masklay;
1376         bool changed = false;
1377         int i;
1378
1379         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1380                 MaskSpline *spline;
1381
1382                 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1383                         continue;
1384                 }
1385
1386                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1387                         for (i = 0; i < spline->tot_point; i++) {
1388                                 MaskSplinePoint *point = &spline->points[i];
1389
1390                                 if (MASKPOINT_ISSEL_ANY(point)) {
1391                                         BezTriple *bezt = &point->bezt;
1392                                         bezt->weight = 0.0f;
1393                                         changed = true;
1394                                 }
1395                         }
1396                 }
1397         }
1398
1399         if (changed) {
1400                 /* TODO: only update edited splines */
1401                 BKE_mask_update_display(mask, CFRA);
1402
1403                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1404                 DAG_id_tag_update(&mask->id, 0);
1405
1406                 return OPERATOR_FINISHED;
1407         }
1408         else {
1409                 return OPERATOR_CANCELLED;
1410         }
1411 }
1412
1413 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1414 {
1415         /* identifiers */
1416         ot->name = "Clear Feather Weight";
1417         ot->description = "Reset the feather weight to zero";
1418         ot->idname = "MASK_OT_feather_weight_clear";
1419
1420         /* api callbacks */
1421         ot->exec = mask_feather_weight_clear_exec;
1422         ot->poll = ED_maskedit_mask_poll;
1423
1424         /* flags */
1425         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1426 }
1427
1428 /******************** move mask layer operator *********************/
1429
1430 static int mask_layer_move_poll(bContext *C)
1431 {
1432         if (ED_maskedit_mask_poll(C)) {
1433                 Mask *mask = CTX_data_edit_mask(C);
1434
1435                 return mask->masklay_tot > 0;
1436         }
1437
1438         return FALSE;
1439 }
1440
1441 static int mask_layer_move_exec(bContext *C, wmOperator *op)
1442 {
1443         Mask *mask = CTX_data_edit_mask(C);
1444         MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
1445         MaskLayer *mask_layer_other;
1446         int direction = RNA_enum_get(op->ptr, "direction");
1447
1448         if (!mask_layer)
1449                 return OPERATOR_CANCELLED;
1450
1451         if (direction == -1) {
1452                 mask_layer_other = mask_layer->prev;
1453
1454                 if (!mask_layer_other)
1455                         return OPERATOR_CANCELLED;
1456
1457                 BLI_remlink(&mask->masklayers, mask_layer);
1458                 BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
1459                 mask->masklay_act--;
1460         }
1461         else if (direction == 1) {
1462                 mask_layer_other = mask_layer->next;
1463
1464                 if (!mask_layer_other)
1465                         return OPERATOR_CANCELLED;
1466
1467                 BLI_remlink(&mask->masklayers, mask_layer);
1468                 BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
1469                 mask->masklay_act++;
1470         }
1471
1472         return OPERATOR_FINISHED;
1473 }
1474
1475 void MASK_OT_layer_move(wmOperatorType *ot)
1476 {
1477         static EnumPropertyItem direction_items[] = {
1478                 {-1, "UP", 0, "Up", ""},
1479                 {1, "DOWN", 0, "Down", ""},
1480                 {0, NULL, 0, NULL, NULL}
1481         };
1482
1483         /* identifiers */
1484         ot->name = "Move Layer";
1485         ot->description = "Move the active layer up/down in the list";
1486         ot->idname = "MASK_OT_layer_move";
1487
1488         /* api callbacks */
1489         ot->exec = mask_layer_move_exec;
1490         ot->poll = mask_layer_move_poll;
1491
1492         /* flags */
1493         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1494
1495         /* properties */
1496         RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move the active layer");
1497 }
1498
1499 /******************** duplicate *********************/
1500
1501 static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
1502 {
1503         Scene *scene = CTX_data_scene(C);
1504         Mask *mask = CTX_data_edit_mask(C);
1505         MaskLayer *mask_layer = BKE_mask_layer_active(mask);
1506         MaskSpline *spline;
1507
1508         if (mask_layer == NULL) {
1509                 return OPERATOR_CANCELLED;
1510         }
1511
1512         for (spline = mask_layer->splines.last;
1513              spline;
1514              spline = spline->prev)
1515         {
1516                 MaskSplinePoint *point = spline->points;
1517                 int i = 0;
1518                 while (i < spline->tot_point) {
1519                         int start = i, end = -1;
1520                         /* Find next selected segment. */
1521                         while (MASKPOINT_ISSEL_ANY(point)) {
1522                                 BKE_mask_point_select_set(point, false);
1523                                 end = i;
1524                                 if (i >= spline->tot_point - 1) {
1525                                         break;
1526                                 }
1527                                 i++;
1528                                 point++;
1529                         }
1530                         if (end >= start) {
1531                                 MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
1532                                 MaskSplinePoint *new_point;
1533                                 int b;
1534
1535                                 /* BKE_mask_spline_add might allocate the points, need to free them in this case. */
1536                                 if (new_spline->points) {
1537                                         MEM_freeN(new_spline->points);
1538                                 }
1539
1540                                 /* Copy options from old spline. */
1541                                 new_spline->flag = spline->flag;
1542                                 new_spline->offset_mode = spline->offset_mode;
1543                                 new_spline->weight_interp = spline->weight_interp;
1544                                 new_spline->parent = spline->parent;
1545
1546                                 /* Allocate new points and copy them from old spline. */
1547                                 new_spline->tot_point = end - start + 1;
1548                                 new_spline->points = MEM_mallocN(sizeof(MaskSplinePoint) * new_spline->tot_point,
1549                                                                  "duplicated mask points");
1550
1551                                 memcpy(new_spline->points, spline->points + start,
1552                                        new_spline->tot_point * sizeof(MaskSplinePoint));
1553
1554                                 /* Select points and duplicate their UWs (if needed). */
1555                                 for (b = 0, new_point = new_spline->points;
1556                                      b < new_spline->tot_point;
1557                                      b++, new_point++)
1558                                 {
1559                                         if (new_point->uw) {
1560                                                 new_point->uw = MEM_dupallocN(new_point->uw);
1561                                         }
1562                                         BKE_mask_point_select_set(new_point, true);
1563                                 }
1564
1565                                 /* Clear cyclic flag if we didn't copy the whole spline. */
1566                                 if (new_spline->flag & MASK_SPLINE_CYCLIC) {
1567                                         if (start != 0 || end != spline->tot_point - 1) {
1568                                                 new_spline->flag &= ~MASK_SPLINE_CYCLIC;
1569                                         }
1570                                 }
1571
1572                                 /* Flush selection to splines. */
1573                                 new_spline->flag |= SELECT;
1574                                 spline->flag &= ~SELECT;
1575
1576                                 mask_layer->act_spline = new_spline;
1577                         }
1578                         i++;
1579                         point++;
1580                 }
1581         }
1582
1583         /* TODO: only update edited splines */
1584         BKE_mask_update_display(mask, CFRA);
1585
1586         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1587
1588         return OPERATOR_FINISHED;
1589 }
1590
1591 void MASK_OT_duplicate(wmOperatorType *ot)
1592 {
1593         /* identifiers */
1594         ot->name = "Duplicate Mask";
1595         ot->description = "Duplicate selected control points and segments between them";
1596         ot->idname = "MASK_OT_duplicate";
1597
1598         /* api callbacks */
1599         ot->exec = mask_duplicate_exec;
1600         ot->poll = ED_maskedit_mask_poll;
1601
1602         /* flags */
1603         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1604 }
1605
1606 /********************** copy splines to clipboard operator *********************/
1607
1608 static int copy_splines_exec(bContext *C, wmOperator *UNUSED(op))
1609 {
1610         Mask *mask = CTX_data_edit_mask(C);
1611         MaskLayer *mask_layer = BKE_mask_layer_active(mask);
1612
1613         BKE_mask_clipboard_copy_from_layer(mask_layer);
1614
1615         return OPERATOR_FINISHED;
1616 }
1617
1618 void MASK_OT_copy_splines(wmOperatorType *ot)
1619 {
1620         /* identifiers */
1621         ot->name = "Copy Splines";
1622         ot->description = "Copy selected splines to clipboard";
1623         ot->idname = "MASK_OT_copy_splines";
1624
1625         /* api callbacks */
1626         ot->exec = copy_splines_exec;
1627         ot->poll = ED_maskedit_mask_poll;
1628
1629         /* flags */
1630         ot->flag = OPTYPE_REGISTER;
1631 }
1632
1633 /********************** paste tracks from clipboard operator *********************/
1634
1635 static int paste_splines_poll(bContext *C)
1636 {
1637         if (ED_maskedit_mask_poll(C)) {
1638                 return BKE_mask_clipboard_is_empty() == false;
1639         }
1640
1641         return 0;
1642 }
1643
1644 static int paste_splines_exec(bContext *C, wmOperator *UNUSED(op))
1645 {
1646         Scene *scene = CTX_data_scene(C);
1647         Mask *mask = CTX_data_edit_mask(C);
1648         MaskLayer *mask_layer = BKE_mask_layer_active(mask);
1649
1650         BKE_mask_clipboard_paste_to_layer(CTX_data_main(C), mask_layer);
1651
1652         /* TODO: only update edited splines */
1653         BKE_mask_update_display(mask, CFRA);
1654
1655         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1656
1657         return OPERATOR_FINISHED;
1658 }
1659
1660 void MASK_OT_paste_splines(wmOperatorType *ot)
1661 {
1662         /* identifiers */
1663         ot->name = "Paste Splines";
1664         ot->description = "Paste splines from clipboard";
1665         ot->idname = "MASK_OT_paste_splines";
1666
1667         /* api callbacks */
1668         ot->exec = paste_splines_exec;
1669         ot->poll = paste_splines_poll;
1670
1671         /* flags */
1672         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1673 }