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