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