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