Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / mask / mask_ops.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edmask
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_listbase.h"
27 #include "BLI_math.h"
28
29 #include "BKE_context.h"
30 #include "BKE_main.h"
31 #include "BKE_mask.h"
32
33 #include "DEG_depsgraph.h"
34
35 #include "DNA_scene_types.h"
36 #include "DNA_mask_types.h"
37 #include "DNA_object_types.h" /* SELECT */
38
39 #include "WM_api.h"
40 #include "WM_types.h"
41
42 #include "ED_clip.h"
43 #include "ED_image.h"
44 #include "ED_keyframing.h"
45 #include "ED_mask.h"
46 #include "ED_screen.h"
47 #include "ED_select_utils.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51
52 #include "mask_intern.h" /* own include */
53
54 /******************** utility functions *********************/
55
56 static void mask_point_scaled_handle(/*const*/ MaskSplinePoint *point,
57                                      /*const*/ eMaskWhichHandle which_handle,
58                                      const float scalex,
59                                      const float scaley,
60                                      float handle[2])
61 {
62   BKE_mask_point_handle(point, which_handle, handle);
63   handle[0] *= scalex;
64   handle[1] *= scaley;
65 }
66
67 MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C,
68                                             Mask *mask,
69                                             const float normal_co[2],
70                                             const float threshold,
71                                             MaskLayer **masklay_r,
72                                             MaskSpline **spline_r,
73                                             eMaskWhichHandle *which_handle_r,
74                                             float *score)
75 {
76   ScrArea *sa = CTX_wm_area(C);
77   ARegion *ar = CTX_wm_region(C);
78
79   MaskLayer *masklay;
80   MaskLayer *point_masklay = NULL;
81   MaskSpline *point_spline = NULL;
82   MaskSplinePoint *point = NULL;
83   float co[2];
84   const float threshold_sq = threshold * threshold;
85   float len_sq = FLT_MAX, scalex, scaley;
86   eMaskWhichHandle which_handle = MASK_WHICH_HANDLE_NONE;
87   int width, height;
88
89   ED_mask_get_size(sa, &width, &height);
90   ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
91
92   co[0] = normal_co[0] * scalex;
93   co[1] = normal_co[1] * scaley;
94
95   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
96     MaskSpline *spline;
97
98     if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
99       continue;
100     }
101
102     for (spline = masklay->splines.first; spline; spline = spline->next) {
103       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
104
105       int i;
106
107       for (i = 0; i < spline->tot_point; i++) {
108         MaskSplinePoint *cur_point = &spline->points[i];
109         MaskSplinePoint *cur_point_deform = &points_array[i];
110         eMaskWhichHandle cur_which_handle = MASK_WHICH_HANDLE_NONE;
111         BezTriple *bezt = &cur_point_deform->bezt;
112         float cur_len_sq, vec[2];
113
114         vec[0] = bezt->vec[1][0] * scalex;
115         vec[1] = bezt->vec[1][1] * scaley;
116
117         cur_len_sq = len_squared_v2v2(co, vec);
118
119         if (cur_len_sq < len_sq) {
120           point_spline = spline;
121           point_masklay = masklay;
122           point = cur_point;
123           len_sq = cur_len_sq;
124           which_handle = MASK_WHICH_HANDLE_NONE;
125         }
126
127         if (BKE_mask_point_handles_mode_get(cur_point_deform) == MASK_HANDLE_MODE_STICK) {
128           float handle[2];
129           mask_point_scaled_handle(
130               cur_point_deform, MASK_WHICH_HANDLE_STICK, scalex, scaley, handle);
131           cur_len_sq = len_squared_v2v2(co, handle);
132           cur_which_handle = MASK_WHICH_HANDLE_STICK;
133         }
134         else {
135           float handle_left[2], handle_right[2];
136           float len_left_sq, len_right_sq;
137           mask_point_scaled_handle(
138               cur_point_deform, MASK_WHICH_HANDLE_LEFT, scalex, scaley, handle_left);
139           mask_point_scaled_handle(
140               cur_point_deform, MASK_WHICH_HANDLE_RIGHT, scalex, scaley, handle_right);
141
142           len_left_sq = len_squared_v2v2(co, handle_left);
143           len_right_sq = len_squared_v2v2(co, handle_right);
144           if (i == 0) {
145             if (len_left_sq <= len_right_sq) {
146               if (bezt->h1 != HD_VECT) {
147                 cur_which_handle = MASK_WHICH_HANDLE_LEFT;
148                 cur_len_sq = len_left_sq;
149               }
150             }
151             else if (bezt->h2 != HD_VECT) {
152               cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
153               cur_len_sq = len_right_sq;
154             }
155           }
156           else {
157             if (len_right_sq <= len_left_sq) {
158               if (bezt->h2 != HD_VECT) {
159                 cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
160                 cur_len_sq = len_right_sq;
161               }
162             }
163             else if (bezt->h1 != HD_VECT) {
164               cur_which_handle = MASK_WHICH_HANDLE_LEFT;
165               cur_len_sq = len_left_sq;
166             }
167           }
168         }
169
170         if (cur_len_sq <= len_sq && cur_which_handle != MASK_WHICH_HANDLE_NONE) {
171           point_masklay = masklay;
172           point_spline = spline;
173           point = cur_point;
174           len_sq = cur_len_sq;
175           which_handle = cur_which_handle;
176         }
177       }
178     }
179   }
180
181   if (len_sq < threshold_sq) {
182     if (masklay_r) {
183       *masklay_r = point_masklay;
184     }
185
186     if (spline_r) {
187       *spline_r = point_spline;
188     }
189
190     if (which_handle_r) {
191       *which_handle_r = which_handle;
192     }
193
194     if (score) {
195       *score = sqrtf(len_sq);
196     }
197
198     return point;
199   }
200
201   if (masklay_r) {
202     *masklay_r = NULL;
203   }
204
205   if (spline_r) {
206     *spline_r = NULL;
207   }
208
209   if (which_handle_r) {
210     *which_handle_r = MASK_WHICH_HANDLE_NONE;
211   }
212
213   return NULL;
214 }
215
216 bool ED_mask_feather_find_nearest(const bContext *C,
217                                   Mask *mask,
218                                   const float normal_co[2],
219                                   const float threshold,
220                                   MaskLayer **masklay_r,
221                                   MaskSpline **spline_r,
222                                   MaskSplinePoint **point_r,
223                                   MaskSplinePointUW **uw_r,
224                                   float *score)
225 {
226   ScrArea *sa = CTX_wm_area(C);
227   ARegion *ar = CTX_wm_region(C);
228
229   MaskLayer *masklay, *point_masklay = NULL;
230   MaskSpline *point_spline = NULL;
231   MaskSplinePoint *point = NULL;
232   MaskSplinePointUW *uw = NULL;
233   const float threshold_sq = threshold * threshold;
234   float len = FLT_MAX, co[2];
235   float scalex, scaley;
236   int width, height;
237
238   ED_mask_get_size(sa, &width, &height);
239   ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
240
241   co[0] = normal_co[0] * scalex;
242   co[1] = normal_co[1] * scaley;
243
244   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
245     MaskSpline *spline;
246
247     for (spline = masklay->splines.first; spline; spline = spline->next) {
248       //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
249
250       int i, tot_feather_point;
251       float(*feather_points)[2], (*fp)[2];
252
253       if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
254         continue;
255       }
256
257       feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
258
259       for (i = 0; i < spline->tot_point; i++) {
260         int j;
261         MaskSplinePoint *cur_point = &spline->points[i];
262
263         for (j = 0; j <= cur_point->tot_uw; j++) {
264           float cur_len_sq, vec[2];
265
266           vec[0] = (*fp)[0] * scalex;
267           vec[1] = (*fp)[1] * scaley;
268
269           cur_len_sq = len_squared_v2v2(vec, co);
270
271           if (point == NULL || cur_len_sq < len) {
272             if (j == 0) {
273               uw = NULL;
274             }
275             else {
276               uw = &cur_point->uw[j - 1];
277             }
278
279             point_masklay = masklay;
280             point_spline = spline;
281             point = cur_point;
282             len = cur_len_sq;
283           }
284
285           fp++;
286         }
287       }
288
289       MEM_freeN(feather_points);
290     }
291   }
292
293   if (len < threshold_sq) {
294     if (masklay_r) {
295       *masklay_r = point_masklay;
296     }
297
298     if (spline_r) {
299       *spline_r = point_spline;
300     }
301
302     if (point_r) {
303       *point_r = point;
304     }
305
306     if (uw_r) {
307       *uw_r = uw;
308     }
309
310     if (score) {
311       *score = sqrtf(len);
312     }
313
314     return true;
315   }
316
317   if (masklay_r) {
318     *masklay_r = NULL;
319   }
320
321   if (spline_r) {
322     *spline_r = NULL;
323   }
324
325   if (point_r) {
326     *point_r = NULL;
327   }
328
329   return false;
330 }
331
332 /******************** create new mask *********************/
333
334 Mask *ED_mask_new(bContext *C, const char *name)
335 {
336   ScrArea *sa = CTX_wm_area(C);
337   Main *bmain = CTX_data_main(C);
338   Mask *mask;
339
340   mask = BKE_mask_new(bmain, name);
341
342   if (sa && sa->spacedata.first) {
343     switch (sa->spacetype) {
344       case SPACE_CLIP: {
345         SpaceClip *sc = sa->spacedata.first;
346         ED_space_clip_set_mask(C, sc, mask);
347         break;
348       }
349       case SPACE_SEQ: {
350         /* do nothing */
351         break;
352       }
353       case SPACE_IMAGE: {
354         SpaceImage *sima = sa->spacedata.first;
355         ED_space_image_set_mask(C, sima, mask);
356         break;
357       }
358     }
359   }
360
361   return mask;
362 }
363
364 /* Get ative layer. Will create mask/layer to be sure there's an active layer.  */
365 MaskLayer *ED_mask_layer_ensure(bContext *C, bool *r_added_mask)
366 {
367   Mask *mask = CTX_data_edit_mask(C);
368   MaskLayer *mask_layer;
369
370   if (mask == NULL) {
371     /* If there's no active mask, create one. */
372     mask = ED_mask_new(C, NULL);
373     *r_added_mask = true;
374   }
375
376   mask_layer = BKE_mask_layer_active(mask);
377   if (mask_layer == NULL) {
378     /* If there's no active mask layer, create one. */
379     mask_layer = BKE_mask_layer_new(mask, "");
380   }
381
382   return mask_layer;
383 }
384
385 static int mask_new_exec(bContext *C, wmOperator *op)
386 {
387   char name[MAX_ID_NAME - 2];
388
389   RNA_string_get(op->ptr, "name", name);
390
391   ED_mask_new(C, name);
392
393   WM_event_add_notifier(C, NC_MASK | NA_ADDED, NULL);
394
395   return OPERATOR_FINISHED;
396 }
397
398 void MASK_OT_new(wmOperatorType *ot)
399 {
400   /* identifiers */
401   ot->name = "New Mask";
402   ot->description = "Create new mask";
403   ot->idname = "MASK_OT_new";
404
405   /* flags */
406   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
407
408   /* api callbacks */
409   ot->exec = mask_new_exec;
410   ot->poll = ED_operator_mask;
411
412   /* properties */
413   RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask");
414 }
415
416 /******************** create new masklay *********************/
417
418 static int masklay_new_exec(bContext *C, wmOperator *op)
419 {
420   Mask *mask = CTX_data_edit_mask(C);
421   char name[MAX_ID_NAME - 2];
422
423   RNA_string_get(op->ptr, "name", name);
424
425   BKE_mask_layer_new(mask, name);
426   mask->masklay_act = mask->masklay_tot - 1;
427
428   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
429
430   return OPERATOR_FINISHED;
431 }
432
433 void MASK_OT_layer_new(wmOperatorType *ot)
434 {
435   /* identifiers */
436   ot->name = "Add Mask Layer";
437   ot->description = "Add new mask layer for masking";
438   ot->idname = "MASK_OT_layer_new";
439
440   /* api callbacks */
441   ot->exec = masklay_new_exec;
442   ot->poll = ED_maskedit_poll;
443
444   /* flags */
445   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
446
447   /* properties */
448   RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask layer");
449 }
450
451 /******************** remove mask layer *********************/
452
453 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
454 {
455   Mask *mask = CTX_data_edit_mask(C);
456   MaskLayer *masklay = BKE_mask_layer_active(mask);
457
458   if (masklay) {
459     BKE_mask_layer_remove(mask, masklay);
460
461     WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
462   }
463
464   return OPERATOR_FINISHED;
465 }
466
467 void MASK_OT_layer_remove(wmOperatorType *ot)
468 {
469   /* identifiers */
470   ot->name = "Remove Mask Layer";
471   ot->description = "Remove mask layer";
472   ot->idname = "MASK_OT_layer_remove";
473
474   /* api callbacks */
475   ot->exec = masklay_remove_exec;
476   ot->poll = ED_maskedit_poll;
477
478   /* flags */
479   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
480 }
481
482 /******************** slide *********************/
483
484 enum {
485   SLIDE_ACTION_NONE = 0,
486   SLIDE_ACTION_POINT = 1,
487   SLIDE_ACTION_HANDLE = 2,
488   SLIDE_ACTION_FEATHER = 3,
489   SLIDE_ACTION_SPLINE = 4,
490 };
491
492 typedef struct SlidePointData {
493   /* Generic fields. */
494   short event_invoke_type;
495   int action;
496   Mask *mask;
497   MaskLayer *masklay;
498   MaskSpline *spline, *orig_spline;
499   MaskSplinePoint *point;
500   MaskSplinePointUW *uw;
501   eMaskWhichHandle which_handle;
502   int width, height;
503
504   float prev_mouse_coord[2];
505   float no[2];
506
507   bool is_curvature_only, is_accurate, is_initial_feather, is_overall_feather;
508
509   bool is_sliding_new_point;
510
511   /* Data needed to restre the state. */
512   float vec[3][3];
513   char old_h1, old_h2;
514
515   /* Point sliding. */
516
517   /* Handle sliding. */
518   float orig_handle_coord[2], prev_handle_coord[2];
519
520   /* Feather sliding. */
521   float prev_feather_coord[2];
522   float weight, weight_scalar;
523 } SlidePointData;
524
525 static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float co[2])
526 {
527   BKE_mask_coord_to_movieclip(sc->clip, &sc->user, r_co, co);
528   ED_clip_point_undistorted_pos(sc, r_co, r_co);
529   BKE_mask_coord_from_movieclip(sc->clip, &sc->user, r_co, r_co);
530 }
531
532 static bool spline_under_mouse_get(const bContext *C,
533                                    Mask *mask,
534                                    const float co[2],
535                                    MaskLayer **mask_layer_r,
536                                    MaskSpline **mask_spline_r)
537 {
538   const float threshold = 19.0f;
539   ScrArea *sa = CTX_wm_area(C);
540   SpaceClip *sc = CTX_wm_space_clip(C);
541   MaskLayer *mask_layer;
542   int width, height;
543   float pixel_co[2];
544   float closest_dist_squared = 0.0f;
545   MaskLayer *closest_layer = NULL;
546   MaskSpline *closest_spline = NULL;
547   bool undistort = false;
548   *mask_layer_r = NULL;
549   *mask_spline_r = NULL;
550   ED_mask_get_size(sa, &width, &height);
551   pixel_co[0] = co[0] * width;
552   pixel_co[1] = co[1] * height;
553   if (sc != NULL) {
554     undistort = (sc->clip != NULL) && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0;
555   }
556   for (mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) {
557     MaskSpline *spline;
558     if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
559       continue;
560     }
561
562     for (spline = mask_layer->splines.first; spline != NULL; spline = spline->next) {
563       MaskSplinePoint *points_array;
564       float min[2], max[2], center[2];
565       float dist_squared;
566       int i;
567       float max_bb_side;
568       if ((spline->flag & SELECT) == 0) {
569         continue;
570       }
571
572       points_array = BKE_mask_spline_point_array(spline);
573       INIT_MINMAX2(min, max);
574       for (i = 0; i < spline->tot_point; i++) {
575         MaskSplinePoint *point_deform = &points_array[i];
576         BezTriple *bezt = &point_deform->bezt;
577
578         float vert[2];
579
580         copy_v2_v2(vert, bezt->vec[1]);
581
582         if (undistort) {
583           mask_point_undistort_pos(sc, vert, vert);
584         }
585
586         minmax_v2v2_v2(min, max, vert);
587       }
588
589       center[0] = (min[0] + max[0]) / 2.0f * width;
590       center[1] = (min[1] + max[1]) / 2.0f * height;
591       dist_squared = len_squared_v2v2(pixel_co, center);
592       max_bb_side = min_ff((max[0] - min[0]) * width, (max[1] - min[1]) * height);
593       if (dist_squared <= max_bb_side * max_bb_side * 0.5f &&
594           (closest_spline == NULL || dist_squared < closest_dist_squared)) {
595         closest_layer = mask_layer;
596         closest_spline = spline;
597         closest_dist_squared = dist_squared;
598       }
599     }
600   }
601   if (closest_dist_squared < SQUARE(threshold) && closest_spline != NULL) {
602     float diff_score;
603     if (ED_mask_find_nearest_diff_point(C,
604                                         mask,
605                                         co,
606                                         threshold,
607                                         false,
608                                         NULL,
609                                         true,
610                                         false,
611                                         NULL,
612                                         NULL,
613                                         NULL,
614                                         NULL,
615                                         &diff_score)) {
616       if (SQUARE(diff_score) < closest_dist_squared) {
617         return false;
618       }
619     }
620
621     *mask_layer_r = closest_layer;
622     *mask_spline_r = closest_spline;
623     return true;
624   }
625   return false;
626 }
627
628 static bool slide_point_check_initial_feather(MaskSpline *spline)
629 {
630   int i;
631
632   for (i = 0; i < spline->tot_point; i++) {
633     MaskSplinePoint *point = &spline->points[i];
634
635     if (point->bezt.weight != 0.0f) {
636       return false;
637     }
638   }
639
640   return true;
641 }
642
643 static void select_sliding_point(Mask *mask,
644                                  MaskLayer *mask_layer,
645                                  MaskSpline *spline,
646                                  MaskSplinePoint *point,
647                                  eMaskWhichHandle which_handle)
648 {
649   ED_mask_select_toggle_all(mask, SEL_DESELECT);
650
651   switch (which_handle) {
652     case MASK_WHICH_HANDLE_NONE:
653       BKE_mask_point_select_set(point, true);
654       break;
655     case MASK_WHICH_HANDLE_LEFT:
656       point->bezt.f1 |= SELECT;
657       break;
658     case MASK_WHICH_HANDLE_RIGHT:
659       point->bezt.f3 |= SELECT;
660       break;
661     case MASK_WHICH_HANDLE_STICK:
662       point->bezt.f1 |= SELECT;
663       point->bezt.f3 |= SELECT;
664       break;
665     default:
666       BLI_assert(!"Unexpected situation in select_sliding_point()");
667   }
668
669   mask_layer->act_spline = spline;
670   mask_layer->act_point = point;
671   ED_mask_select_flush_all(mask);
672 }
673
674 static void check_sliding_handle_type(MaskSplinePoint *point, eMaskWhichHandle which_handle)
675 {
676   BezTriple *bezt = &point->bezt;
677
678   if (which_handle == MASK_WHICH_HANDLE_LEFT) {
679     if (bezt->h1 == HD_VECT) {
680       bezt->h1 = HD_FREE;
681     }
682     else if (bezt->h1 == HD_AUTO) {
683       bezt->h1 = HD_ALIGN_DOUBLESIDE;
684       bezt->h2 = HD_ALIGN_DOUBLESIDE;
685     }
686   }
687   else if (which_handle == MASK_WHICH_HANDLE_RIGHT) {
688     if (bezt->h2 == HD_VECT) {
689       bezt->h2 = HD_FREE;
690     }
691     else if (bezt->h2 == HD_AUTO) {
692       bezt->h1 = HD_ALIGN_DOUBLESIDE;
693       bezt->h2 = HD_ALIGN_DOUBLESIDE;
694     }
695   }
696 }
697
698 static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *event)
699 {
700   ScrArea *sa = CTX_wm_area(C);
701   ARegion *ar = CTX_wm_region(C);
702
703   Mask *mask = CTX_data_edit_mask(C);
704   SlidePointData *customdata = NULL;
705   MaskLayer *masklay, *cv_masklay, *feather_masklay;
706   MaskSpline *spline, *cv_spline, *feather_spline;
707   MaskSplinePoint *point, *cv_point, *feather_point;
708   MaskSplinePointUW *uw = NULL;
709   int width, height, action = SLIDE_ACTION_NONE;
710   const bool slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
711   float co[2], cv_score, feather_score;
712   const float threshold = 19;
713   eMaskWhichHandle which_handle;
714
715   ED_mask_mouse_pos(sa, ar, event->mval, co);
716   ED_mask_get_size(sa, &width, &height);
717
718   cv_point = ED_mask_point_find_nearest(
719       C, mask, co, threshold, &cv_masklay, &cv_spline, &which_handle, &cv_score);
720
721   if (ED_mask_feather_find_nearest(C,
722                                    mask,
723                                    co,
724                                    threshold,
725                                    &feather_masklay,
726                                    &feather_spline,
727                                    &feather_point,
728                                    &uw,
729                                    &feather_score)) {
730     if (slide_feather || !cv_point || feather_score < cv_score) {
731       action = SLIDE_ACTION_FEATHER;
732
733       masklay = feather_masklay;
734       spline = feather_spline;
735       point = feather_point;
736     }
737   }
738
739   if (cv_point && action == SLIDE_ACTION_NONE) {
740     if (which_handle != MASK_WHICH_HANDLE_NONE) {
741       action = SLIDE_ACTION_HANDLE;
742     }
743     else {
744       action = SLIDE_ACTION_POINT;
745     }
746
747     masklay = cv_masklay;
748     spline = cv_spline;
749     point = cv_point;
750   }
751
752   if (action == SLIDE_ACTION_NONE) {
753     if (spline_under_mouse_get(C, mask, co, &masklay, &spline)) {
754       action = SLIDE_ACTION_SPLINE;
755       point = NULL;
756     }
757   }
758
759   if (action != SLIDE_ACTION_NONE) {
760     customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
761     customdata->event_invoke_type = event->type;
762     customdata->mask = mask;
763     customdata->masklay = masklay;
764     customdata->spline = spline;
765     customdata->point = point;
766     customdata->width = width;
767     customdata->height = height;
768     customdata->action = action;
769     customdata->uw = uw;
770
771     customdata->is_sliding_new_point = RNA_boolean_get(op->ptr, "is_new_point");
772
773     if (customdata->action != SLIDE_ACTION_SPLINE) {
774       customdata->old_h1 = point->bezt.h1;
775       customdata->old_h2 = point->bezt.h2;
776       select_sliding_point(mask, masklay, spline, point, which_handle);
777       check_sliding_handle_type(point, which_handle);
778     }
779
780     if (uw) {
781       float co_uw[2];
782       float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
783
784       customdata->weight = uw->w;
785       customdata->weight_scalar = weight_scalar;
786       BKE_mask_point_segment_co(spline, point, uw->u, co_uw);
787       BKE_mask_point_normal(spline, point, uw->u, customdata->no);
788
789       madd_v2_v2v2fl(customdata->prev_feather_coord, co_uw, customdata->no, uw->w * weight_scalar);
790     }
791     else if (customdata->action != SLIDE_ACTION_SPLINE) {
792       BezTriple *bezt = &point->bezt;
793
794       customdata->weight = bezt->weight;
795       customdata->weight_scalar = 1.0f;
796       BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
797
798       madd_v2_v2v2fl(customdata->prev_feather_coord, bezt->vec[1], customdata->no, bezt->weight);
799     }
800
801     if (customdata->action == SLIDE_ACTION_FEATHER) {
802       customdata->is_initial_feather = slide_point_check_initial_feather(spline);
803     }
804
805     if (customdata->action != SLIDE_ACTION_SPLINE) {
806       copy_m3_m3(customdata->vec, point->bezt.vec);
807       if (which_handle != MASK_WHICH_HANDLE_NONE) {
808         BKE_mask_point_handle(point, which_handle, customdata->orig_handle_coord);
809         copy_v2_v2(customdata->prev_handle_coord, customdata->orig_handle_coord);
810       }
811     }
812     customdata->which_handle = which_handle;
813
814     ED_mask_mouse_pos(sa, ar, event->mval, customdata->prev_mouse_coord);
815   }
816
817   return customdata;
818 }
819
820 static int slide_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
821 {
822   Mask *mask = CTX_data_edit_mask(C);
823   SlidePointData *slidedata;
824
825   if (mask == NULL) {
826     return OPERATOR_PASS_THROUGH;
827   }
828
829   slidedata = slide_point_customdata(C, op, event);
830
831   if (slidedata) {
832     op->customdata = slidedata;
833
834     WM_event_add_modal_handler(C, op);
835
836     slidedata->masklay->act_spline = slidedata->spline;
837     slidedata->masklay->act_point = slidedata->point;
838
839     WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
840
841     return OPERATOR_RUNNING_MODAL;
842   }
843
844   return OPERATOR_PASS_THROUGH;
845 }
846
847 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
848 {
849   int i;
850
851   for (i = 0; i < data->spline->tot_point; i++) {
852     MaskSplinePoint *point = &data->spline->points[i];
853     MaskSplinePoint *orig_point = &data->orig_spline->points[i];
854
855     point->bezt.weight = orig_point->bezt.weight + delta;
856     if (point->bezt.weight < 0.0f) {
857       point->bezt.weight = 0.0f;
858     }
859   }
860 }
861
862 static void slide_point_restore_spline(SlidePointData *data)
863 {
864   int i;
865
866   for (i = 0; i < data->spline->tot_point; i++) {
867     MaskSplinePoint *point = &data->spline->points[i];
868     MaskSplinePoint *orig_point = &data->orig_spline->points[i];
869     int j;
870
871     point->bezt = orig_point->bezt;
872
873     for (j = 0; j < point->tot_uw; j++) {
874       point->uw[j] = orig_point->uw[j];
875     }
876   }
877 }
878
879 static void cancel_slide_point(SlidePointData *data)
880 {
881   /* cancel sliding */
882
883   if (data->orig_spline) {
884     slide_point_restore_spline(data);
885   }
886   else {
887     if (data->action == SLIDE_ACTION_FEATHER) {
888       if (data->uw) {
889         data->uw->w = data->weight;
890       }
891       else {
892         data->point->bezt.weight = data->weight;
893       }
894     }
895     else if (data->action != SLIDE_ACTION_SPLINE) {
896       copy_m3_m3(data->point->bezt.vec, data->vec);
897       data->point->bezt.h1 = data->old_h1;
898       data->point->bezt.h2 = data->old_h2;
899     }
900   }
901 }
902
903 static void free_slide_point_data(SlidePointData *data)
904 {
905   if (data->orig_spline) {
906     BKE_mask_spline_free(data->orig_spline);
907   }
908
909   MEM_freeN(data);
910 }
911
912 static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
913 {
914   SlidePointData *data = (SlidePointData *)op->customdata;
915   BezTriple *bezt = &data->point->bezt;
916   float co[2];
917
918   switch (event->type) {
919     case LEFTALTKEY:
920     case RIGHTALTKEY:
921     case LEFTSHIFTKEY:
922     case RIGHTSHIFTKEY:
923       if (ELEM(event->type, LEFTALTKEY, RIGHTALTKEY)) {
924         if (data->action == SLIDE_ACTION_FEATHER) {
925           data->is_overall_feather = (event->val == KM_PRESS);
926         }
927         else {
928           data->is_curvature_only = (event->val == KM_PRESS);
929         }
930       }
931
932       if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) {
933         data->is_accurate = (event->val == KM_PRESS);
934       }
935
936       ATTR_FALLTHROUGH; /* update CV position */
937     case MOUSEMOVE: {
938       ScrArea *sa = CTX_wm_area(C);
939       ARegion *ar = CTX_wm_region(C);
940       float delta[2];
941
942       ED_mask_mouse_pos(sa, ar, event->mval, co);
943       sub_v2_v2v2(delta, co, data->prev_mouse_coord);
944       if (data->is_accurate) {
945         mul_v2_fl(delta, 0.2f);
946       }
947       copy_v2_v2(data->prev_mouse_coord, co);
948
949       if (data->action == SLIDE_ACTION_HANDLE) {
950         float new_handle[2];
951
952         if (data->is_sliding_new_point && data->which_handle == MASK_WHICH_HANDLE_STICK) {
953           if (ELEM(data->point,
954                    &data->spline->points[0],
955                    &data->spline->points[data->spline->tot_point - 1])) {
956             SWAP(float, delta[0], delta[1]);
957             delta[1] *= -1;
958
959             /* flip last point */
960             if (data->point != &data->spline->points[0]) {
961               negate_v2(delta);
962             }
963           }
964         }
965
966         add_v2_v2v2(new_handle, data->prev_handle_coord, delta);
967
968         BKE_mask_point_set_handle(data->point,
969                                   data->which_handle,
970                                   new_handle,
971                                   data->is_curvature_only,
972                                   data->orig_handle_coord,
973                                   data->vec);
974         BKE_mask_point_handle(data->point, data->which_handle, data->prev_handle_coord);
975
976         if (data->is_sliding_new_point) {
977           if (ELEM(data->which_handle, MASK_WHICH_HANDLE_LEFT, MASK_WHICH_HANDLE_RIGHT)) {
978             float vec[2];
979             short self_handle = (data->which_handle == MASK_WHICH_HANDLE_LEFT) ? 0 : 2;
980             short other_handle = (data->which_handle == MASK_WHICH_HANDLE_LEFT) ? 2 : 0;
981
982             sub_v2_v2v2(vec, bezt->vec[1], bezt->vec[self_handle]);
983             add_v2_v2v2(bezt->vec[other_handle], bezt->vec[1], vec);
984           }
985         }
986       }
987       else if (data->action == SLIDE_ACTION_POINT) {
988         add_v2_v2(bezt->vec[0], delta);
989         add_v2_v2(bezt->vec[1], delta);
990         add_v2_v2(bezt->vec[2], delta);
991       }
992       else if (data->action == SLIDE_ACTION_FEATHER) {
993         float vec[2], no[2], p[2], c[2], w, offco[2];
994         float *weight = NULL;
995         float weight_scalar = 1.0f;
996         bool is_overall_feather = data->is_overall_feather || data->is_initial_feather;
997
998         add_v2_v2v2(offco, data->prev_feather_coord, delta);
999
1000         if (data->uw) {
1001           /* project on both sides and find the closest one,
1002            * prevents flickering when projecting onto both sides can happen */
1003           const float u_pos = BKE_mask_spline_project_co(
1004               data->spline, data->point, data->uw->u, offco, MASK_PROJ_NEG);
1005           const float u_neg = BKE_mask_spline_project_co(
1006               data->spline, data->point, data->uw->u, offco, MASK_PROJ_POS);
1007           float dist_pos = FLT_MAX;
1008           float dist_neg = FLT_MAX;
1009           float co_pos[2];
1010           float co_neg[2];
1011           float u;
1012
1013           if (u_pos > 0.0f && u_pos < 1.0f) {
1014             BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
1015             dist_pos = len_squared_v2v2(offco, co_pos);
1016           }
1017
1018           if (u_neg > 0.0f && u_neg < 1.0f) {
1019             BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
1020             dist_neg = len_squared_v2v2(offco, co_neg);
1021           }
1022
1023           u = dist_pos < dist_neg ? u_pos : u_neg;
1024
1025           if (u > 0.0f && u < 1.0f) {
1026             data->uw->u = u;
1027
1028             data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
1029             weight = &data->uw->w;
1030             weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
1031             if (weight_scalar != 0.0f) {
1032               weight_scalar = 1.0f / weight_scalar;
1033             }
1034
1035             BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
1036             BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
1037           }
1038         }
1039         else {
1040           weight = &bezt->weight;
1041           /* weight_scalar = 1.0f; keep as is */
1042           copy_v2_v2(no, data->no);
1043           copy_v2_v2(p, bezt->vec[1]);
1044         }
1045
1046         if (weight) {
1047           sub_v2_v2v2(c, offco, p);
1048           project_v2_v2v2_normalized(vec, c, no);
1049
1050           w = len_v2(vec);
1051
1052           if (is_overall_feather) {
1053             float w_delta;
1054
1055             if (dot_v2v2(no, vec) <= 0.0f) {
1056               w = -w;
1057             }
1058
1059             w_delta = w - data->weight * data->weight_scalar;
1060
1061             if (data->orig_spline == NULL) {
1062               /* restore weight for currently sliding point, so orig_spline would be created
1063                * with original weights used
1064                */
1065               *weight = data->weight;
1066
1067               data->orig_spline = BKE_mask_spline_copy(data->spline);
1068             }
1069
1070             if (data->is_initial_feather) {
1071               *weight = w * weight_scalar;
1072             }
1073
1074             slide_point_delta_all_feather(data, w_delta);
1075           }
1076           else {
1077             if (dot_v2v2(no, vec) <= 0.0f) {
1078               w = 0.0f;
1079             }
1080
1081             if (data->orig_spline) {
1082               /* restore possible overall feather changes */
1083               slide_point_restore_spline(data);
1084
1085               BKE_mask_spline_free(data->orig_spline);
1086               data->orig_spline = NULL;
1087             }
1088
1089             if (weight_scalar != 0.0f) {
1090               *weight = w * weight_scalar;
1091             }
1092           }
1093
1094           copy_v2_v2(data->prev_feather_coord, offco);
1095         }
1096       }
1097       else if (data->action == SLIDE_ACTION_SPLINE) {
1098         int i;
1099
1100         if (data->orig_spline == NULL) {
1101           data->orig_spline = BKE_mask_spline_copy(data->spline);
1102         }
1103
1104         for (i = 0; i < data->spline->tot_point; i++) {
1105           MaskSplinePoint *point = &data->spline->points[i];
1106           add_v2_v2(point->bezt.vec[0], delta);
1107           add_v2_v2(point->bezt.vec[1], delta);
1108           add_v2_v2(point->bezt.vec[2], delta);
1109         }
1110       }
1111
1112       WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
1113       DEG_id_tag_update(&data->mask->id, 0);
1114
1115       break;
1116     }
1117
1118     case LEFTMOUSE:
1119     case RIGHTMOUSE:
1120       if (event->type == data->event_invoke_type && event->val == KM_RELEASE) {
1121         Scene *scene = CTX_data_scene(C);
1122
1123         /* dont key sliding feather uw's */
1124         if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == false) {
1125           if (IS_AUTOKEY_ON(scene)) {
1126             ED_mask_layer_shape_auto_key(data->masklay, CFRA);
1127           }
1128         }
1129
1130         if (data->is_sliding_new_point) {
1131           if (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) < FLT_EPSILON) {
1132             bezt->h1 = HD_VECT;
1133           }
1134           if (len_squared_v2v2(bezt->vec[2], bezt->vec[1]) < FLT_EPSILON) {
1135             bezt->h2 = HD_VECT;
1136           }
1137         }
1138
1139         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
1140         DEG_id_tag_update(&data->mask->id, 0);
1141
1142         free_slide_point_data(op->customdata); /* keep this last! */
1143         return OPERATOR_FINISHED;
1144       }
1145       else if (event->type != data->event_invoke_type && event->val == KM_PRESS) {
1146         /* pass to ESCKEY */
1147       }
1148       else {
1149         break;
1150       }
1151
1152     case ESCKEY:
1153       cancel_slide_point(op->customdata);
1154
1155       WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
1156       DEG_id_tag_update(&data->mask->id, 0);
1157
1158       free_slide_point_data(op->customdata); /* keep this last! */
1159       return OPERATOR_CANCELLED;
1160   }
1161
1162   return OPERATOR_RUNNING_MODAL;
1163 }
1164
1165 void MASK_OT_slide_point(wmOperatorType *ot)
1166 {
1167   PropertyRNA *prop;
1168
1169   /* identifiers */
1170   ot->name = "Slide Point";
1171   ot->description = "Slide control points";
1172   ot->idname = "MASK_OT_slide_point";
1173
1174   /* api callbacks */
1175   ot->invoke = slide_point_invoke;
1176   ot->modal = slide_point_modal;
1177   ot->poll = ED_operator_mask;
1178
1179   /* flags */
1180   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1181
1182   RNA_def_boolean(ot->srna,
1183                   "slide_feather",
1184                   0,
1185                   "Slide Feather",
1186                   "First try to slide feather instead of vertex");
1187
1188   prop = RNA_def_boolean(
1189       ot->srna, "is_new_point", 0, "Slide New Point", "Newly created vertex is being slid");
1190   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1191 }
1192
1193 /******************** slide spline curvature *********************/
1194
1195 typedef struct SlideSplineCurvatureData {
1196   short event_invoke_type;
1197
1198   Mask *mask;
1199   MaskLayer *mask_layer;
1200   MaskSpline *spline;
1201   MaskSplinePoint *point;
1202   float u;
1203   bool accurate;
1204
1205   BezTriple *adjust_bezt, *other_bezt;
1206   BezTriple bezt_backup, other_bezt_backup;
1207
1208   float prev_mouse_coord[2];
1209   float prev_spline_coord[2];
1210
1211   float P0[2], P1[2], P2[2], P3[3];
1212 } SlideSplineCurvatureData;
1213
1214 static void cancel_slide_spline_curvature(SlideSplineCurvatureData *slide_data)
1215 {
1216   *slide_data->adjust_bezt = slide_data->bezt_backup;
1217   *slide_data->other_bezt = slide_data->other_bezt_backup;
1218 }
1219
1220 static void free_slide_spline_curvature_data(SlideSplineCurvatureData *slide_data)
1221 {
1222   MEM_freeN(slide_data);
1223 }
1224
1225 static bool slide_spline_curvature_check(bContext *C, const wmEvent *event)
1226 {
1227   Mask *mask = CTX_data_edit_mask(C);
1228   float co[2];
1229   const float threshold = 19.0f;
1230
1231   ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co);
1232
1233   if (ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL)) {
1234     return false;
1235   }
1236
1237   if (ED_mask_feather_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL, NULL)) {
1238     return false;
1239   }
1240
1241   return true;
1242 }
1243
1244 static SlideSplineCurvatureData *slide_spline_curvature_customdata(bContext *C,
1245                                                                    const wmEvent *event)
1246 {
1247   const float threshold = 19.0f;
1248
1249   Mask *mask = CTX_data_edit_mask(C);
1250   SlideSplineCurvatureData *slide_data;
1251   MaskLayer *mask_layer;
1252   MaskSpline *spline;
1253   MaskSplinePoint *point;
1254   float u, co[2];
1255   BezTriple *next_bezt;
1256
1257   ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co);
1258
1259   if (!ED_mask_find_nearest_diff_point(C,
1260                                        mask,
1261                                        co,
1262                                        threshold,
1263                                        false,
1264                                        NULL,
1265                                        true,
1266                                        false,
1267                                        &mask_layer,
1268                                        &spline,
1269                                        &point,
1270                                        &u,
1271                                        NULL)) {
1272     return NULL;
1273   }
1274
1275   next_bezt = BKE_mask_spline_point_next_bezt(spline, spline->points, point);
1276   if (next_bezt == NULL) {
1277     return NULL;
1278   }
1279
1280   slide_data = MEM_callocN(sizeof(SlideSplineCurvatureData), "slide curvature slide");
1281   slide_data->event_invoke_type = event->type;
1282   slide_data->mask = mask;
1283   slide_data->mask_layer = mask_layer;
1284   slide_data->spline = spline;
1285   slide_data->point = point;
1286   slide_data->u = u;
1287
1288   copy_v2_v2(slide_data->prev_mouse_coord, co);
1289   BKE_mask_point_segment_co(spline, point, u, slide_data->prev_spline_coord);
1290
1291   copy_v2_v2(slide_data->P0, point->bezt.vec[1]);
1292   copy_v2_v2(slide_data->P1, point->bezt.vec[2]);
1293   copy_v2_v2(slide_data->P2, next_bezt->vec[0]);
1294   copy_v2_v2(slide_data->P3, next_bezt->vec[1]);
1295
1296   /* Depending to which end we're closer to adjust either left or right side of the spline. */
1297   if (u <= 0.5f) {
1298     slide_data->adjust_bezt = &point->bezt;
1299     slide_data->other_bezt = next_bezt;
1300   }
1301   else {
1302     slide_data->adjust_bezt = next_bezt;
1303     slide_data->other_bezt = &point->bezt;
1304   }
1305
1306   /* Data needed for restoring state. */
1307   slide_data->bezt_backup = *slide_data->adjust_bezt;
1308   slide_data->other_bezt_backup = *slide_data->other_bezt;
1309
1310   /* Let's dont touch other side of the point for now, so set handle to FREE. */
1311   if (u < 0.5f) {
1312     if (slide_data->adjust_bezt->h2 <= HD_VECT) {
1313       slide_data->adjust_bezt->h2 = HD_FREE;
1314     }
1315   }
1316   else {
1317     if (slide_data->adjust_bezt->h1 <= HD_VECT) {
1318       slide_data->adjust_bezt->h1 = HD_FREE;
1319     }
1320   }
1321
1322   /* Change selection */
1323   ED_mask_select_toggle_all(mask, SEL_DESELECT);
1324   slide_data->adjust_bezt->f2 |= SELECT;
1325   slide_data->other_bezt->f2 |= SELECT;
1326   if (u < 0.5f) {
1327     slide_data->adjust_bezt->f3 |= SELECT;
1328     slide_data->other_bezt->f1 |= SELECT;
1329   }
1330   else {
1331     slide_data->adjust_bezt->f1 |= SELECT;
1332     slide_data->other_bezt->f3 |= SELECT;
1333   }
1334   mask_layer->act_spline = spline;
1335   mask_layer->act_point = point;
1336   ED_mask_select_flush_all(mask);
1337
1338   return slide_data;
1339 }
1340
1341 static int slide_spline_curvature_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1342 {
1343   Mask *mask = CTX_data_edit_mask(C);
1344   SlideSplineCurvatureData *slide_data;
1345
1346   if (mask == NULL) {
1347     return OPERATOR_PASS_THROUGH;
1348   }
1349
1350   /* Be sure we don't conflict with point slide here. */
1351   if (!slide_spline_curvature_check(C, event)) {
1352     return OPERATOR_PASS_THROUGH;
1353   }
1354
1355   slide_data = slide_spline_curvature_customdata(C, event);
1356   if (slide_data != NULL) {
1357     op->customdata = slide_data;
1358     WM_event_add_modal_handler(C, op);
1359     WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1360     return OPERATOR_RUNNING_MODAL;
1361   }
1362
1363   return OPERATOR_PASS_THROUGH;
1364 }
1365
1366 static void slide_spline_solve_P1(const float u,
1367                                   const float B[2],
1368                                   const float P0[2],
1369                                   const float P2[2],
1370                                   const float P3[2],
1371                                   float solution[2])
1372 {
1373   const float u2 = u * u, u3 = u * u * u;
1374   const float v = 1.0f - u;
1375   const float v2 = v * v, v3 = v * v * v;
1376   const float inv_divider = 1.0f / (3.0f * v2 * u);
1377   const float t = 3.0f * v * u2;
1378   solution[0] = -(v3 * P0[0] + t * P2[0] + u3 * P3[0] - B[0]) * inv_divider;
1379   solution[1] = -(v3 * P0[1] + t * P2[1] + u3 * P3[1] - B[1]) * inv_divider;
1380 }
1381
1382 static void slide_spline_solve_P2(const float u,
1383                                   const float B[2],
1384                                   const float P0[2],
1385                                   const float P1[2],
1386                                   const float P3[2],
1387                                   float solution[2])
1388 {
1389   const float u2 = u * u, u3 = u * u * u;
1390   const float v = 1.0f - u;
1391   const float v2 = v * v, v3 = v * v * v;
1392   const float inv_divider = 1.0f / (3.0f * v * u2);
1393   const float t = 3.0f * v2 * u;
1394   solution[0] = -(v3 * P0[0] + t * P1[0] + u3 * P3[0] - B[0]) * inv_divider;
1395   solution[1] = -(v3 * P0[1] + t * P1[1] + u3 * P3[1] - B[1]) * inv_divider;
1396 }
1397
1398 static int slide_spline_curvature_modal(bContext *C, wmOperator *op, const wmEvent *event)
1399 {
1400   Scene *scene = CTX_data_scene(C);
1401   const float margin = 0.2f;
1402   SlideSplineCurvatureData *slide_data = (SlideSplineCurvatureData *)op->customdata;
1403   float u = slide_data->u;
1404
1405   switch (event->type) {
1406     case LEFTSHIFTKEY:
1407     case RIGHTSHIFTKEY:
1408     case LEFTCTRLKEY:
1409     case RIGHTCTRLKEY:
1410       if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) {
1411         slide_data->accurate = (event->val == KM_PRESS);
1412       }
1413
1414       if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
1415         if (event->val == KM_PRESS) {
1416           slide_data->adjust_bezt->h1 = slide_data->adjust_bezt->h2 = HD_FREE;
1417           if ((u > margin && u < 0.5f) || (u >= 0.5f && u < 1.0f - margin)) {
1418             slide_data->other_bezt->h1 = slide_data->other_bezt->h2 = HD_FREE;
1419           }
1420         }
1421         else if (event->val == KM_RELEASE) {
1422           slide_data->adjust_bezt->h1 = slide_data->bezt_backup.h1;
1423           slide_data->adjust_bezt->h2 = slide_data->bezt_backup.h2;
1424           slide_data->other_bezt->h1 = slide_data->other_bezt_backup.h1;
1425           slide_data->other_bezt->h2 = slide_data->other_bezt_backup.h2;
1426         }
1427
1428         if (u < 0.5f) {
1429           copy_v2_v2(slide_data->adjust_bezt->vec[0], slide_data->bezt_backup.vec[0]);
1430           copy_v2_v2(slide_data->other_bezt->vec[2], slide_data->other_bezt_backup.vec[2]);
1431         }
1432         else {
1433           copy_v2_v2(slide_data->adjust_bezt->vec[2], slide_data->bezt_backup.vec[2]);
1434           copy_v2_v2(slide_data->other_bezt->vec[0], slide_data->other_bezt_backup.vec[0]);
1435         }
1436       }
1437
1438       ATTR_FALLTHROUGH; /* update CV position */
1439     case MOUSEMOVE: {
1440       float B[2], mouse_coord[2], delta[2];
1441
1442       /* Get coordinate spline is expected to go through. */
1443       ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, mouse_coord);
1444       sub_v2_v2v2(delta, mouse_coord, slide_data->prev_mouse_coord);
1445       if (slide_data->accurate) {
1446         mul_v2_fl(delta, 0.2f);
1447       }
1448       add_v2_v2v2(B, slide_data->prev_spline_coord, delta);
1449       copy_v2_v2(slide_data->prev_spline_coord, B);
1450       copy_v2_v2(slide_data->prev_mouse_coord, mouse_coord);
1451
1452       if (u < 0.5f) {
1453         float oldP2[2];
1454         bool need_restore_P2 = false;
1455
1456         if (u > margin) {
1457           float solution[2];
1458           float x = (u - margin) * 0.5f / (0.5f - margin);
1459           float weight = (3 * x * x - 2 * x * x * x);
1460
1461           slide_spline_solve_P2(u, B, slide_data->P0, slide_data->P1, slide_data->P3, solution);
1462
1463           copy_v2_v2(oldP2, slide_data->P2);
1464           interp_v2_v2v2(slide_data->P2, slide_data->P2, solution, weight);
1465           copy_v2_v2(slide_data->other_bezt->vec[0], slide_data->P2);
1466           need_restore_P2 = true;
1467
1468           /* Tweak handle type in order to be able to apply the delta. */
1469           if (weight > 0.0f) {
1470             if (slide_data->other_bezt->h1 <= HD_VECT) {
1471               slide_data->other_bezt->h1 = HD_FREE;
1472             }
1473           }
1474         }
1475
1476         slide_spline_solve_P1(
1477             u, B, slide_data->P0, slide_data->P2, slide_data->P3, slide_data->adjust_bezt->vec[2]);
1478
1479         if (need_restore_P2) {
1480           copy_v2_v2(slide_data->P2, oldP2);
1481         }
1482       }
1483       else {
1484         float oldP1[2];
1485         bool need_restore_P1 = false;
1486
1487         if (u < 1.0f - margin) {
1488           float solution[2];
1489           float x = ((1.0f - u) - margin) * 0.5f / (0.5f - margin);
1490           float weight = 3 * x * x - 2 * x * x * x;
1491
1492           slide_spline_solve_P1(u, B, slide_data->P0, slide_data->P2, slide_data->P3, solution);
1493
1494           copy_v2_v2(oldP1, slide_data->P1);
1495           interp_v2_v2v2(slide_data->P1, slide_data->P1, solution, weight);
1496           copy_v2_v2(slide_data->other_bezt->vec[2], slide_data->P1);
1497           need_restore_P1 = true;
1498
1499           /* Tweak handle type in order to be able to apply the delta. */
1500           if (weight > 0.0f) {
1501             if (slide_data->other_bezt->h2 <= HD_VECT) {
1502               slide_data->other_bezt->h2 = HD_FREE;
1503             }
1504           }
1505         }
1506
1507         slide_spline_solve_P2(
1508             u, B, slide_data->P0, slide_data->P1, slide_data->P3, slide_data->adjust_bezt->vec[0]);
1509
1510         if (need_restore_P1) {
1511           copy_v2_v2(slide_data->P1, oldP1);
1512         }
1513       }
1514
1515       WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
1516       DEG_id_tag_update(&slide_data->mask->id, 0);
1517
1518       break;
1519     }
1520
1521     case LEFTMOUSE:
1522     case RIGHTMOUSE:
1523       if (event->type == slide_data->event_invoke_type && event->val == KM_RELEASE) {
1524         /* dont key sliding feather uw's */
1525         if (IS_AUTOKEY_ON(scene)) {
1526           ED_mask_layer_shape_auto_key(slide_data->mask_layer, CFRA);
1527         }
1528
1529         WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
1530         DEG_id_tag_update(&slide_data->mask->id, 0);
1531
1532         free_slide_spline_curvature_data(slide_data); /* keep this last! */
1533         return OPERATOR_FINISHED;
1534       }
1535
1536       break;
1537
1538     case ESCKEY:
1539       cancel_slide_spline_curvature(slide_data);
1540
1541       WM_event_add_notifier(C, NC_MASK | NA_EDITED, slide_data->mask);
1542       DEG_id_tag_update(&slide_data->mask->id, 0);
1543
1544       free_slide_spline_curvature_data(op->customdata); /* keep this last! */
1545       return OPERATOR_CANCELLED;
1546   }
1547
1548   return OPERATOR_RUNNING_MODAL;
1549 }
1550
1551 void MASK_OT_slide_spline_curvature(wmOperatorType *ot)
1552 {
1553   /* identifiers */
1554   ot->name = "Slide Spline Curvature";
1555   ot->description = "Slide a point on the spline to define it's curvature";
1556   ot->idname = "MASK_OT_slide_spline_curvature";
1557
1558   /* api callbacks */
1559   ot->invoke = slide_spline_curvature_invoke;
1560   ot->modal = slide_spline_curvature_modal;
1561   ot->poll = ED_operator_mask;
1562
1563   /* flags */
1564   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1565 }
1566
1567 /******************** toggle cyclic *********************/
1568
1569 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1570 {
1571   Mask *mask = CTX_data_edit_mask(C);
1572   MaskLayer *masklay;
1573
1574   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1575     MaskSpline *spline;
1576
1577     if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1578       continue;
1579     }
1580
1581     for (spline = masklay->splines.first; spline; spline = spline->next) {
1582       if (ED_mask_spline_select_check(spline)) {
1583         spline->flag ^= MASK_SPLINE_CYCLIC;
1584       }
1585     }
1586   }
1587
1588   DEG_id_tag_update(&mask->id, 0);
1589   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1590
1591   return OPERATOR_FINISHED;
1592 }
1593
1594 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
1595 {
1596   /* identifiers */
1597   ot->name = "Toggle Cyclic";
1598   ot->description = "Toggle cyclic for selected splines";
1599   ot->idname = "MASK_OT_cyclic_toggle";
1600
1601   /* api callbacks */
1602   ot->exec = cyclic_toggle_exec;
1603   ot->poll = ED_maskedit_mask_poll;
1604
1605   /* flags */
1606   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1607 }
1608
1609 /******************** delete *********************/
1610
1611 static void delete_feather_points(MaskSplinePoint *point)
1612 {
1613   int i, count = 0;
1614
1615   if (!point->tot_uw) {
1616     return;
1617   }
1618
1619   for (i = 0; i < point->tot_uw; i++) {
1620     if ((point->uw[i].flag & SELECT) == 0) {
1621       count++;
1622     }
1623   }
1624
1625   if (count == 0) {
1626     MEM_freeN(point->uw);
1627     point->uw = NULL;
1628     point->tot_uw = 0;
1629   }
1630   else {
1631     MaskSplinePointUW *new_uw;
1632     int j = 0;
1633
1634     new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
1635
1636     for (i = 0; i < point->tot_uw; i++) {
1637       if ((point->uw[i].flag & SELECT) == 0) {
1638         new_uw[j++] = point->uw[i];
1639       }
1640     }
1641
1642     MEM_freeN(point->uw);
1643
1644     point->uw = new_uw;
1645     point->tot_uw = count;
1646   }
1647 }
1648
1649 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
1650 {
1651   Scene *scene = CTX_data_scene(C);
1652   Mask *mask = CTX_data_edit_mask(C);
1653   MaskLayer *masklay;
1654   bool changed = false;
1655
1656   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1657     MaskSpline *spline;
1658     int mask_layer_shape_ofs = 0;
1659
1660     if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1661       continue;
1662     }
1663
1664     spline = masklay->splines.first;
1665
1666     while (spline) {
1667       const int tot_point_orig = spline->tot_point;
1668       int i, count = 0;
1669       MaskSpline *next_spline = spline->next;
1670
1671       /* count unselected points */
1672       for (i = 0; i < spline->tot_point; i++) {
1673         MaskSplinePoint *point = &spline->points[i];
1674
1675         if (!MASKPOINT_ISSEL_ANY(point)) {
1676           count++;
1677         }
1678       }
1679
1680       if (count == 0) {
1681         /* delete the whole spline */
1682         BLI_remlink(&masklay->splines, spline);
1683         BKE_mask_spline_free(spline);
1684
1685         if (spline == masklay->act_spline) {
1686           masklay->act_spline = NULL;
1687           masklay->act_point = NULL;
1688         }
1689
1690         BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
1691       }
1692       else {
1693         MaskSplinePoint *new_points;
1694         int j;
1695
1696         new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
1697
1698         for (i = 0, j = 0; i < tot_point_orig; i++) {
1699           MaskSplinePoint *point = &spline->points[i];
1700
1701           if (!MASKPOINT_ISSEL_ANY(point)) {
1702             if (point == masklay->act_point) {
1703               masklay->act_point = &new_points[j];
1704             }
1705
1706             delete_feather_points(point);
1707
1708             new_points[j] = *point;
1709             j++;
1710           }
1711           else {
1712             if (point == masklay->act_point) {
1713               masklay->act_point = NULL;
1714             }
1715
1716             BKE_mask_point_free(point);
1717             spline->tot_point--;
1718
1719             BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
1720           }
1721         }
1722
1723         mask_layer_shape_ofs += spline->tot_point;
1724
1725         MEM_freeN(spline->points);
1726         spline->points = new_points;
1727
1728         ED_mask_select_flush_all(mask);
1729       }
1730
1731       changed = true;
1732       spline = next_spline;
1733     }
1734
1735     /* not essential but confuses users when there are keys with no data!
1736      * assume if they delete all data from the layer they also dont care about keys */
1737     if (BLI_listbase_is_empty(&masklay->splines)) {
1738       BKE_mask_layer_free_shapes(masklay);
1739     }
1740   }
1741
1742   if (!changed) {
1743     return OPERATOR_CANCELLED;
1744   }
1745
1746   /* TODO: only update edited splines */
1747   BKE_mask_update_display(mask, CFRA);
1748
1749   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1750
1751   return OPERATOR_FINISHED;
1752 }
1753
1754 void MASK_OT_delete(wmOperatorType *ot)
1755 {
1756   /* identifiers */
1757   ot->name = "Delete";
1758   ot->description = "Delete selected control points or splines";
1759   ot->idname = "MASK_OT_delete";
1760
1761   /* api callbacks */
1762   ot->invoke = WM_operator_confirm;
1763   ot->exec = delete_exec;
1764   ot->poll = ED_maskedit_mask_poll;
1765
1766   /* flags */
1767   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1768 }
1769
1770 /* *** switch direction *** */
1771 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
1772 {
1773   Scene *scene = CTX_data_scene(C);
1774   Mask *mask = CTX_data_edit_mask(C);
1775   MaskLayer *masklay;
1776
1777   bool changed = false;
1778
1779   /* do actual selection */
1780   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1781     MaskSpline *spline;
1782     bool changed_layer = false;
1783
1784     if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1785       continue;
1786     }
1787
1788     for (spline = masklay->splines.first; spline; spline = spline->next) {
1789       if (ED_mask_spline_select_check(spline)) {
1790         BKE_mask_spline_direction_switch(masklay, spline);
1791         changed = true;
1792         changed_layer = true;
1793       }
1794     }
1795
1796     if (changed_layer) {
1797       if (IS_AUTOKEY_ON(scene)) {
1798         ED_mask_layer_shape_auto_key(masklay, CFRA);
1799       }
1800     }
1801   }
1802
1803   if (changed) {
1804     /* TODO: only update this spline */
1805     BKE_mask_update_display(mask, CFRA);
1806
1807     WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1808     WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1809
1810     return OPERATOR_FINISHED;
1811   }
1812
1813   return OPERATOR_CANCELLED;
1814 }
1815
1816 void MASK_OT_switch_direction(wmOperatorType *ot)
1817 {
1818   /* identifiers */
1819   ot->name = "Switch Direction";
1820   ot->description = "Switch direction of selected splines";
1821   ot->idname = "MASK_OT_switch_direction";
1822
1823   /* api callbacks */
1824   ot->exec = mask_switch_direction_exec;
1825   ot->poll = ED_maskedit_mask_poll;
1826
1827   /* flags */
1828   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1829 }
1830
1831 /* *** recalc normals *** */
1832 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1833 {
1834   Scene *scene = CTX_data_scene(C);
1835   Mask *mask = CTX_data_edit_mask(C);
1836   MaskLayer *masklay;
1837   int i;
1838
1839   bool changed = false;
1840
1841   /* do actual selection */
1842   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1843     MaskSpline *spline;
1844     bool changed_layer = false;
1845
1846     if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1847       continue;
1848     }
1849
1850     for (spline = masklay->splines.first; spline; spline = spline->next) {
1851       for (i = 0; i < spline->tot_point; i++) {
1852         MaskSplinePoint *point = &spline->points[i];
1853
1854         if (MASKPOINT_ISSEL_ANY(point)) {
1855           BKE_mask_calc_handle_point_auto(spline, point, false);
1856           changed = true;
1857           changed_layer = true;
1858         }
1859       }
1860     }
1861
1862     if (changed_layer) {
1863       if (IS_AUTOKEY_ON(scene)) {
1864         ED_mask_layer_shape_auto_key(masklay, CFRA);
1865       }
1866     }
1867   }
1868
1869   if (changed) {
1870     /* TODO: only update this spline */
1871     BKE_mask_update_display(mask, CFRA);
1872
1873     WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1874     WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1875
1876     return OPERATOR_FINISHED;
1877   }
1878
1879   return OPERATOR_CANCELLED;
1880 }
1881
1882 /* named to match mesh recalc normals */
1883 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1884 {
1885   /* identifiers */
1886   ot->name = "Recalc Normals";
1887   ot->description = "Re-calculate the direction of selected handles";
1888   ot->idname = "MASK_OT_normals_make_consistent";
1889
1890   /* api callbacks */
1891   ot->exec = mask_normals_make_consistent_exec;
1892   ot->poll = ED_maskedit_mask_poll;
1893
1894   /* flags */
1895   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1896 }
1897
1898 /******************** set handle type *********************/
1899
1900 static int set_handle_type_exec(bContext *C, wmOperator *op)
1901 {
1902   Mask *mask = CTX_data_edit_mask(C);
1903   MaskLayer *masklay;
1904   int handle_type = RNA_enum_get(op->ptr, "type");
1905
1906   bool changed = false;
1907
1908   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1909     MaskSpline *spline;
1910     int i;
1911
1912     if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1913       continue;
1914     }
1915
1916     for (spline = masklay->splines.first; spline; spline = spline->next) {
1917       for (i = 0; i < spline->tot_point; i++) {
1918         MaskSplinePoint *point = &spline->points[i];
1919
1920         if (MASKPOINT_ISSEL_ANY(point)) {
1921           BezTriple *bezt = &point->bezt;
1922
1923           if (bezt->f2 & SELECT) {
1924             bezt->h1 = handle_type;
1925             bezt->h2 = handle_type;
1926           }
1927           else {
1928             if (bezt->f1 & SELECT) {
1929               bezt->h1 = handle_type;
1930             }
1931             if (bezt->f3 & SELECT) {
1932               bezt->h2 = handle_type;
1933             }
1934           }
1935
1936           if (handle_type == HD_ALIGN) {
1937             float vec[3];
1938             sub_v3_v3v3(vec, bezt->vec[0], bezt->vec[1]);
1939             add_v3_v3v3(bezt->vec[2], bezt->vec[1], vec);
1940           }
1941
1942           changed = true;
1943         }
1944       }
1945     }
1946   }
1947
1948   if (changed) {
1949     WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1950     DEG_id_tag_update(&mask->id, 0);
1951
1952     return OPERATOR_FINISHED;
1953   }
1954   return OPERATOR_CANCELLED;
1955 }
1956
1957 void MASK_OT_handle_type_set(wmOperatorType *ot)
1958 {
1959   static const EnumPropertyItem editcurve_handle_type_items[] = {
1960       {HD_AUTO, "AUTO", 0, "Auto", ""},
1961       {HD_VECT, "VECTOR", 0, "Vector", ""},
1962       {HD_ALIGN, "ALIGNED", 0, "Aligned Single", ""},
1963       {HD_ALIGN_DOUBLESIDE, "ALIGNED_DOUBLESIDE", 0, "Aligned", ""},
1964       {HD_FREE, "FREE", 0, "Free", ""},
1965       {0, NULL, 0, NULL, NULL},
1966   };
1967
1968   /* identifiers */
1969   ot->name = "Set Handle Type";
1970   ot->description = "Set type of handles for selected control points";
1971   ot->idname = "MASK_OT_handle_type_set";
1972
1973   /* api callbacks */
1974   ot->invoke = WM_menu_invoke;
1975   ot->exec = set_handle_type_exec;
1976   ot->poll = ED_maskedit_mask_poll;
1977
1978   /* flags */
1979   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1980
1981   /* properties */
1982   ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1983 }
1984
1985 /* ********* clear/set restrict view *********/
1986 static int mask_hide_view_clear_exec(bContext *C, wmOperator *op)
1987 {
1988   Mask *mask = CTX_data_edit_mask(C);
1989   MaskLayer *masklay;
1990   bool changed = false;
1991   const bool select = RNA_boolean_get(op->ptr, "select");
1992
1993   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1994
1995     if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1996       ED_mask_layer_select_set(masklay, select);
1997       masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1998       changed = true;
1999     }
2000   }
2001
2002   if (changed) {
2003     WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
2004     DEG_id_tag_update(&mask->id, 0);
2005
2006     return OPERATOR_FINISHED;
2007   }
2008   else {
2009     return OPERATOR_CANCELLED;
2010   }
2011 }
2012
2013 void MASK_OT_hide_view_clear(wmOperatorType *ot)
2014 {
2015
2016   /* identifiers */
2017   ot->name = "Clear Restrict View";
2018   ot->description = "Reveal the layer by setting the hide flag";
2019   ot->idname = "MASK_OT_hide_view_clear";
2020
2021   /* api callbacks */
2022   ot->exec = mask_hide_view_clear_exec;
2023   ot->poll = ED_maskedit_mask_poll;
2024
2025   /* flags */
2026   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2027
2028   RNA_def_boolean(ot->srna, "select", true, "Select", "");
2029 }
2030
2031 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
2032 {
2033   Mask *mask = CTX_data_edit_mask(C);
2034   MaskLayer *masklay;
2035   const bool unselected = RNA_boolean_get(op->ptr, "unselected");
2036   bool changed = false;
2037
2038   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
2039
2040     if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
2041       continue;
2042     }
2043
2044     if (!unselected) {
2045       if (ED_mask_layer_select_check(masklay)) {
2046         ED_mask_layer_select_set(masklay, false);
2047
2048         masklay->restrictflag |= OB_RESTRICT_VIEW;
2049         changed = true;
2050         if (masklay == BKE_mask_layer_active(mask)) {
2051           BKE_mask_layer_active_set(mask, NULL);
2052         }
2053       }
2054     }
2055     else {
2056       if (!ED_mask_layer_select_check(masklay)) {
2057         masklay->restrictflag |= OB_RESTRICT_VIEW;
2058         changed = true;
2059         if (masklay == BKE_mask_layer_active(mask)) {
2060           BKE_mask_layer_active_set(mask, NULL);
2061         }
2062       }
2063     }
2064   }
2065
2066   if (changed) {
2067     WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
2068     DEG_id_tag_update(&mask->id, 0);
2069
2070     return OPERATOR_FINISHED;
2071   }
2072   else {
2073     return OPERATOR_CANCELLED;
2074   }
2075 }
2076
2077 void MASK_OT_hide_view_set(wmOperatorType *ot)
2078 {
2079   /* identifiers */
2080   ot->name = "Set Restrict View";
2081   ot->description = "Hide the layer by setting the hide flag";
2082   ot->idname = "MASK_OT_hide_view_set";
2083
2084   /* api callbacks */
2085   ot->exec = mask_hide_view_set_exec;
2086   ot->poll = ED_maskedit_mask_poll;
2087
2088   /* flags */
2089   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2090
2091   RNA_def_boolean(
2092       ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
2093 }
2094
2095 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
2096 {
2097   Scene *scene = CTX_data_scene(C);
2098   Mask *mask = CTX_data_edit_mask(C);
2099   MaskLayer *masklay;
2100   bool changed = false;
2101   int i;
2102
2103   for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
2104     MaskSpline *spline;
2105
2106     if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
2107       continue;
2108     }
2109
2110     for (spline = masklay->splines.first; spline; spline = spline->next) {
2111       for (i = 0; i < spline->tot_point; i++) {
2112         MaskSplinePoint *point = &spline->points[i];
2113
2114         if (MASKPOINT_ISSEL_ANY(point)) {
2115           BezTriple *bezt = &point->bezt;
2116           bezt->weight = 0.0f;
2117           changed = true;
2118         }
2119       }
2120     }
2121   }
2122
2123   if (changed) {
2124     /* TODO: only update edited splines */
2125     BKE_mask_update_display(mask, CFRA);
2126
2127     WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
2128     DEG_id_tag_update(&mask->id, 0);
2129
2130     return OPERATOR_FINISHED;
2131   }
2132   else {
2133     return OPERATOR_CANCELLED;
2134   }
2135 }
2136
2137 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
2138 {
2139   /* identifiers */
2140   ot->name = "Clear Feather Weight";
2141   ot->description = "Reset the feather weight to zero";
2142   ot->idname = "MASK_OT_feather_weight_clear";
2143
2144   /* api callbacks */
2145   ot->exec = mask_feather_weight_clear_exec;
2146   ot->poll = ED_maskedit_mask_poll;
2147
2148   /* flags */
2149   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2150 }
2151
2152 /******************** move mask layer operator *********************/
2153
2154 static bool mask_layer_move_poll(bContext *C)
2155 {
2156   if (ED_maskedit_mask_poll(C)) {
2157     Mask *mask = CTX_data_edit_mask(C);
2158
2159     return mask->masklay_tot > 0;
2160   }
2161
2162   return false;
2163 }
2164
2165 static int mask_layer_move_exec(bContext *C, wmOperator *op)
2166 {
2167   Mask *mask = CTX_data_edit_mask(C);
2168   MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
2169   MaskLayer *mask_layer_other;
2170   int direction = RNA_enum_get(op->ptr, "direction");
2171
2172   if (!mask_layer) {
2173     return OPERATOR_CANCELLED;
2174   }
2175
2176   if (direction == -1) {
2177     mask_layer_other = mask_layer->prev;
2178
2179     if (!mask_layer_other) {
2180       return OPERATOR_CANCELLED;
2181     }
2182
2183     BLI_remlink(&mask->masklayers, mask_layer);
2184     BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
2185     mask->masklay_act--;
2186   }
2187   else if (direction == 1) {
2188     mask_layer_other = mask_layer->next;
2189
2190     if (!mask_layer_other) {
2191       return OPERATOR_CANCELLED;
2192     }
2193
2194     BLI_remlink(&mask->masklayers, mask_layer);
2195     BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
2196     mask->masklay_act++;
2197   }
2198
2199   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
2200
2201   return OPERATOR_FINISHED;
2202 }
2203
2204 void MASK_OT_layer_move(wmOperatorType *ot)
2205 {
2206   static const EnumPropertyItem direction_items[] = {
2207       {-1, "UP", 0, "Up", ""},
2208       {1, "DOWN", 0, "Down", ""},
2209       {0, NULL, 0, NULL, NULL},
2210   };
2211
2212   /* identifiers */
2213   ot->name = "Move Layer";
2214   ot->description = "Move the active layer up/down in the list";
2215   ot->idname = "MASK_OT_layer_move";
2216
2217   /* api callbacks */
2218   ot->exec = mask_layer_move_exec;
2219   ot->poll = mask_layer_move_poll;
2220
2221   /* flags */
2222   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2223
2224   /* properties */
2225   RNA_def_enum(ot->srna,
2226                "direction",
2227                direction_items,
2228                0,
2229                "Direction",
2230                "Direction to move the active layer");
2231 }
2232
2233 /******************** duplicate *********************/
2234
2235 static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
2236 {
2237   Scene *scene = CTX_data_scene(C);
2238   Mask *mask = CTX_data_edit_mask(C);
2239   MaskLayer *mask_layer;
2240
2241   for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) {
2242     MaskSpline *spline;
2243
2244     for (spline = mask_layer->splines.last; spline; spline = spline->prev) {
2245       MaskSplinePoint *point = spline->points;
2246       int i = 0;
2247       while (i < spline->tot_point) {
2248         int start = i, end = -1;
2249         /* Find next selected segment. */
2250         while (MASKPOINT_ISSEL_ANY(point)) {
2251           BKE_mask_point_select_set(point, false);
2252           end = i;
2253           if (i >= spline->tot_point - 1) {
2254             break;
2255           }
2256           i++;
2257           point++;
2258         }
2259         if (end >= start) {
2260           int tot_point;
2261           int tot_point_shape_start = 0;
2262           MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
2263           MaskSplinePoint *new_point;
2264           int b;
2265
2266           /* BKE_mask_spline_add might allocate the points,
2267            * need to free them in this case. */
2268           if (new_spline->points) {
2269             MEM_freeN(new_spline->points);
2270           }
2271
2272           /* Copy options from old spline. */
2273           new_spline->flag = spline->flag;
2274           new_spline->offset_mode = spline->offset_mode;
2275           new_spline->weight_interp = spline->weight_interp;
2276           new_spline->parent = spline->parent;
2277
2278           /* Allocate new points and copy them from old spline. */
2279           new_spline->tot_point = end - start + 1;
2280           new_spline->points = MEM_mallocN(sizeof(MaskSplinePoint) * new_spline->tot_point,
2281                                            "duplicated mask points");
2282
2283           memcpy(new_spline->points,
2284                  spline->points + start,
2285                  new_spline->tot_point * sizeof(MaskSplinePoint));
2286
2287           tot_point = new_spline->tot_point;
2288
2289           /* animation requires points added one by one */
2290           if (mask_layer->splines_shapes.first) {
2291             new_spline->tot_point = 0;
2292             tot_point_shape_start = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
2293           }
2294
2295           /* Select points and duplicate their UWs (if needed). */
2296           for (b = 0, new_point = new_spline->points; b < tot_point; b++, new_point++) {
2297             if (new_point->uw) {
2298               new_point->uw = MEM_dupallocN(new_point->uw);
2299             }
2300             BKE_mask_point_select_set(new_point, true);
2301
2302             if (mask_layer->splines_shapes.first) {
2303               new_spline->tot_point++;
2304               BKE_mask_layer_shape_changed_add(mask_layer, tot_point_shape_start + b, true, false);
2305             }
2306           }
2307
2308           /* Clear cyclic flag if we didn't copy the whole spline. */
2309           if (new_spline->flag & MASK_SPLINE_CYCLIC) {
2310             if (start != 0 || end != spline->tot_point - 1) {
2311               new_spline->flag &= ~MASK_SPLINE_CYCLIC;
2312             }
2313           }
2314
2315           /* Flush selection to splines. */
2316           new_spline->flag |= SELECT;
2317           spline->flag &= ~SELECT;
2318
2319           mask_layer->act_spline = new_spline;
2320         }
2321         i++;
2322         point++;
2323       }
2324     }
2325   }
2326
2327   /* TODO: only update edited splines */
2328   BKE_mask_update_display(mask, CFRA);
2329
2330   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
2331
2332   return OPERATOR_FINISHED;
2333 }
2334
2335 void MASK_OT_duplicate(wmOperatorType *ot)
2336 {
2337   /* identifiers */
2338   ot->name = "Duplicate Mask";
2339   ot->description = "Duplicate selected control points and segments between them";
2340   ot->idname = "MASK_OT_duplicate";
2341
2342   /* api callbacks */
2343   ot->exec = mask_duplicate_exec;
2344   ot->poll = ED_maskedit_mask_poll;
2345
2346   /* flags */
2347   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2348 }
2349
2350 /********************** copy splines to clipboard operator *********************/
2351
2352 static int copy_splines_exec(bContext *C, wmOperator *UNUSED(op))
2353 {
2354   Mask *mask = CTX_data_edit_mask(C);
2355   MaskLayer *mask_layer = BKE_mask_layer_active(mask);
2356
2357   if (mask_layer == NULL) {
2358     return OPERATOR_CANCELLED;
2359   }
2360
2361   BKE_mask_clipboard_copy_from_layer(mask_layer);
2362
2363   return OPERATOR_FINISHED;
2364 }
2365
2366 void MASK_OT_copy_splines(wmOperatorType *ot)
2367 {
2368   /* identifiers */
2369   ot->name = "Copy Splines";
2370   ot->description = "Copy selected splines to clipboard";
2371   ot->idname = "MASK_OT_copy_splines";
2372
2373   /* api callbacks */
2374   ot->exec = copy_splines_exec;
2375   ot->poll = ED_maskedit_mask_poll;
2376
2377   /* flags */
2378   ot->flag = OPTYPE_REGISTER;
2379 }
2380
2381 /********************** paste tracks from clipboard operator *********************/
2382
2383 static bool paste_splines_poll(bContext *C)
2384 {
2385   if (ED_maskedit_mask_poll(C)) {
2386     return BKE_mask_clipboard_is_empty() == false;
2387   }
2388
2389   return 0;
2390 }
2391
2392 static int paste_splines_exec(bContext *C, wmOperator *UNUSED(op))
2393 {
2394   Scene *scene = CTX_data_scene(C);
2395   Mask *mask = CTX_data_edit_mask(C);
2396   MaskLayer *mask_layer = BKE_mask_layer_active(mask);
2397
2398   if (mask_layer == NULL) {
2399     mask_layer = BKE_mask_layer_new(mask, "");
2400   }
2401
2402   BKE_mask_clipboard_paste_to_layer(CTX_data_main(C), mask_layer);
2403
2404   /* TODO: only update edited splines */
2405   BKE_mask_update_display(mask, CFRA);
2406
2407   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
2408
2409   return OPERATOR_FINISHED;
2410 }
2411
2412 void MASK_OT_paste_splines(wmOperatorType *ot)
2413 {
2414   /* identifiers */
2415   ot->name = "Paste Splines";
2416   ot->description = "Paste splines from clipboard";
2417   ot->idname = "MASK_OT_paste_splines";
2418
2419   /* api callbacks */
2420   ot->exec = paste_splines_exec;
2421   ot->poll = paste_splines_poll;
2422
2423   /* flags */
2424   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2425 }