mask mode
[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_mask.h"
40
41 #include "DNA_scene_types.h"
42 #include "DNA_mask_types.h"
43 #include "DNA_object_types.h"  /* SELECT */
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "ED_screen.h"
49 #include "ED_mask.h"
50 #include "ED_clip.h"
51 #include "ED_keyframing.h"
52
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55
56 #include "mask_intern.h"  /* own include */
57
58 /******************** utility functions *********************/
59
60 MaskSplinePoint *ED_mask_point_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
61                                             MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
62                                             float *score)
63 {
64         MaskLayer *masklay;
65         MaskLayer *point_masklay = NULL;
66         MaskSpline *point_spline = NULL;
67         MaskSplinePoint *point = NULL;
68         float co[2], aspx, aspy;
69         float len = FLT_MAX, scalex, scaley;
70         int is_handle = FALSE, width, height;
71
72         ED_mask_size(C, &width, &height);
73         ED_mask_aspect(C, &aspx, &aspy);
74         ED_mask_pixelspace_factor(C, &scalex, &scaley);
75
76         co[0] = normal_co[0] * scalex;
77         co[1] = normal_co[1] * scaley;
78
79         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
80                 MaskSpline *spline;
81
82                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
83                         continue;
84                 }
85
86                 for (spline = masklay->splines.first; spline; spline = spline->next) {
87                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
88
89                         int i;
90
91                         for (i = 0; i < spline->tot_point; i++) {
92                                 MaskSplinePoint *cur_point = &spline->points[i];
93                                 MaskSplinePoint *cur_point_deform = &points_array[i];
94                                 float cur_len, vec[2], handle[2];
95
96                                 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
97                                 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
98
99                                 if (BKE_mask_point_has_handle(cur_point)) {
100                                         BKE_mask_point_handle(cur_point_deform, handle);
101                                         handle[0] *= scalex;
102                                         handle[1] *= scaley;
103
104                                         cur_len = len_v2v2(co, handle);
105
106                                         if (cur_len < len) {
107                                                 point_masklay = masklay;
108                                                 point_spline = spline;
109                                                 point = cur_point;
110                                                 len = cur_len;
111                                                 is_handle = TRUE;
112                                         }
113                                 }
114
115                                 cur_len = len_v2v2(co, vec);
116
117                                 if (cur_len < len) {
118                                         point_spline = spline;
119                                         point_masklay = masklay;
120                                         point = cur_point;
121                                         len = cur_len;
122                                         is_handle = FALSE;
123                                 }
124                         }
125                 }
126         }
127
128         if (len < threshold) {
129                 if (masklay_r)
130                         *masklay_r = point_masklay;
131
132                 if (spline_r)
133                         *spline_r = point_spline;
134
135                 if (is_handle_r)
136                         *is_handle_r = is_handle;
137
138                 if (score)
139                         *score = len;
140
141                 return point;
142         }
143
144         if (masklay_r)
145                 *masklay_r = NULL;
146
147         if (spline_r)
148                 *spline_r = NULL;
149
150         if (is_handle_r)
151                 *is_handle_r = FALSE;
152
153         return NULL;
154 }
155
156 int ED_mask_feather_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
157                                  MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
158                                  MaskSplinePointUW **uw_r, float *score)
159 {
160         MaskLayer *masklay, *point_masklay = NULL;
161         MaskSpline *point_spline = NULL;
162         MaskSplinePoint *point = NULL;
163         MaskSplinePointUW *uw = NULL;
164         float len = FLT_MAX, co[2];
165         float scalex, scaley, aspx, aspy;
166         int width, height;
167
168         ED_mask_size(C, &width, &height);
169         ED_mask_aspect(C, &aspx, &aspy);
170         ED_mask_pixelspace_factor(C, &scalex, &scaley);
171
172         co[0] = normal_co[0] * scalex;
173         co[1] = normal_co[1] * scaley;
174
175         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
176                 MaskSpline *spline;
177
178                 for (spline = masklay->splines.first; spline; spline = spline->next) {
179                         //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
180
181                         int i, tot_feather_point;
182                         float (*feather_points)[2], (*fp)[2];
183
184                         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
185                                 continue;
186                         }
187
188                         feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
189
190                         for (i = 0; i < spline->tot_point; i++) {
191                                 int j;
192                                 MaskSplinePoint *cur_point = &spline->points[i];
193
194                                 for (j = 0; j < cur_point->tot_uw + 1; j++) {
195                                         float cur_len, vec[2];
196
197                                         vec[0] = (*fp)[0] * scalex;
198                                         vec[1] = (*fp)[1] * scaley;
199
200                                         cur_len = len_v2v2(vec, co);
201
202                                         if (point == NULL || cur_len < len) {
203                                                 if (j == 0)
204                                                         uw = NULL;
205                                                 else
206                                                         uw = &cur_point->uw[j - 1];
207
208                                                 point_masklay = masklay;
209                                                 point_spline = spline;
210                                                 point = cur_point;
211                                                 len = cur_len;
212                                         }
213
214                                         fp++;
215                                 }
216                         }
217
218                         MEM_freeN(feather_points);
219                 }
220         }
221
222         if (len < threshold) {
223                 if (masklay_r)
224                         *masklay_r = point_masklay;
225
226                 if (spline_r)
227                         *spline_r = point_spline;
228
229                 if (point_r)
230                         *point_r = point;
231
232                 if (uw_r)
233                         *uw_r = uw;
234
235                 if (score)
236                         *score = len;
237
238                 return TRUE;
239         }
240
241         if (masklay_r)
242                 *masklay_r = NULL;
243
244         if (spline_r)
245                 *spline_r = NULL;
246
247         if (point_r)
248                 *point_r = NULL;
249
250         return FALSE;
251 }
252
253
254 /******************** create new mask *********************/
255
256 static int mask_new_exec(bContext *C, wmOperator *op)
257 {
258         SpaceClip *sc = CTX_wm_space_clip(C);
259         Mask *mask;
260         char name[MAX_ID_NAME - 2];
261
262         RNA_string_get(op->ptr, "name", name);
263
264         mask = BKE_mask_new(name);
265
266         if (sc)
267                 ED_space_clip_set_mask(C, sc, mask);
268
269         return OPERATOR_FINISHED;
270 }
271
272 void MASK_OT_new(wmOperatorType *ot)
273 {
274         /* identifiers */
275         ot->name = "New Mask";
276         ot->description = "Create new mask";
277         ot->idname = "MASK_OT_new";
278
279         /* flags */
280         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
281
282         /* api callbacks */
283         ot->exec = mask_new_exec;
284         ot->poll = ED_operator_mask;
285
286         /* properties */
287         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
288 }
289
290 /******************** create new masklay *********************/
291
292 static int masklay_new_exec(bContext *C, wmOperator *op)
293 {
294         Mask *mask = CTX_data_edit_mask(C);
295         char name[MAX_ID_NAME - 2];
296
297         RNA_string_get(op->ptr, "name", name);
298
299         BKE_mask_layer_new(mask, name);
300         mask->masklay_act = mask->masklay_tot - 1;
301
302         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
303
304         return OPERATOR_FINISHED;
305 }
306
307 void MASK_OT_layer_new(wmOperatorType *ot)
308 {
309         /* identifiers */
310         ot->name = "Add Mask Layer";
311         ot->description = "Add new mask layer for masking";
312         ot->idname = "MASK_OT_layer_new";
313
314         /* api callbacks */
315         ot->exec = masklay_new_exec;
316         ot->poll = ED_maskedit_poll;
317
318         /* flags */
319         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
320
321         /* properties */
322         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
323 }
324
325 /******************** remove mask layer *********************/
326
327 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
328 {
329         Mask *mask = CTX_data_edit_mask(C);
330         MaskLayer *masklay = BKE_mask_layer_active(mask);
331
332         if (masklay) {
333                 BKE_mask_layer_remove(mask, masklay);
334
335                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
336         }
337
338         return OPERATOR_FINISHED;
339 }
340
341 void MASK_OT_layer_remove(wmOperatorType *ot)
342 {
343         /* identifiers */
344         ot->name = "Remove Mask Layer";
345         ot->description = "Remove mask layer";
346         ot->idname = "MASK_OT_layer_remove";
347
348         /* api callbacks */
349         ot->exec = masklay_remove_exec;
350         ot->poll = ED_maskedit_poll;
351
352         /* flags */
353         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
354 }
355
356 /******************** slide *********************/
357
358 enum {
359         SLIDE_ACTION_NONE    = 0,
360         SLIDE_ACTION_POINT   = 1,
361         SLIDE_ACTION_HANDLE  = 2,
362         SLIDE_ACTION_FEATHER = 3
363 };
364
365 typedef struct SlidePointData {
366         int action;
367
368         float co[2];
369         float vec[3][3];
370
371         Mask *mask;
372         MaskLayer *masklay;
373         MaskSpline *spline, *orig_spline;
374         MaskSplinePoint *point;
375         MaskSplinePointUW *uw;
376         float handle[2], no[2], feather[2];
377         int width, height;
378         float weight;
379
380         short curvature_only, accurate;
381         short initial_feather, overall_feather;
382 } SlidePointData;
383
384 static int slide_point_check_initial_feather(MaskSpline *spline)
385 {
386         int i;
387
388         for (i = 0; i < spline->tot_point; i++) {
389                 MaskSplinePoint *point = &spline->points[i];
390
391                 if (point->bezt.weight != 0.0f)
392                         return FALSE;
393
394                 /* comment for now. if all bezt weights are zero - this is as good-as initial */
395 #if 0
396                 int j;
397                 for (j = 0; j < point->tot_uw; j++) {
398                         if (point->uw[j].w != 0.0f)
399                                 return FALSE;
400                 }
401 #endif
402         }
403
404         return TRUE;
405 }
406
407 static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
408 {
409         Mask *mask = CTX_data_edit_mask(C);
410         SlidePointData *customdata = NULL;
411         MaskLayer *masklay, *cv_masklay, *feather_masklay;
412         MaskSpline *spline, *cv_spline, *feather_spline;
413         MaskSplinePoint *point, *cv_point, *feather_point;
414         MaskSplinePointUW *uw = NULL;
415         int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
416         int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
417         float co[2], cv_score, feather_score;
418         const float threshold = 19;
419
420         ED_mask_mouse_pos(C, event, co);
421         ED_mask_size(C, &width, &height);
422
423         cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
424
425         if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
426                 if (slide_feather || !cv_point || feather_score < cv_score) {
427                         action = SLIDE_ACTION_FEATHER;
428
429                         masklay = feather_masklay;
430                         spline = feather_spline;
431                         point = feather_point;
432                 }
433         }
434
435         if (cv_point && action == SLIDE_ACTION_NONE) {
436                 if (is_handle)
437                         action = SLIDE_ACTION_HANDLE;
438                 else
439                         action = SLIDE_ACTION_POINT;
440
441                 masklay = cv_masklay;
442                 spline = cv_spline;
443                 point = cv_point;
444         }
445
446         if (action != SLIDE_ACTION_NONE) {
447                 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
448
449                 customdata->mask = mask;
450                 customdata->masklay = masklay;
451                 customdata->spline = spline;
452                 customdata->point = point;
453                 customdata->width = width;
454                 customdata->height = height;
455                 customdata->action = action;
456                 customdata->uw = uw;
457
458                 if (uw) {
459                         float co[2];
460                         float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
461
462                         customdata->weight = uw->w;
463                         BKE_mask_point_segment_co(spline, point, uw->u, co);
464                         BKE_mask_point_normal(spline, point, uw->u, customdata->no);
465
466                         madd_v2_v2v2fl(customdata->feather, co, customdata->no, uw->w * weight_scalar);
467                 }
468                 else {
469                         BezTriple *bezt = &point->bezt;
470
471                         customdata->weight = bezt->weight;
472                         BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
473
474                         madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
475                 }
476
477                 if (customdata->action == SLIDE_ACTION_FEATHER)
478                         customdata->initial_feather = slide_point_check_initial_feather(spline);
479
480                 copy_m3_m3(customdata->vec, point->bezt.vec);
481                 if (BKE_mask_point_has_handle(point))
482                         BKE_mask_point_handle(point, customdata->handle);
483                 ED_mask_mouse_pos(C, event, customdata->co);
484         }
485
486         return customdata;
487 }
488
489 static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
490 {
491         SlidePointData *slidedata = slide_point_customdata(C, op, event);
492
493         if (slidedata) {
494                 Mask *mask = CTX_data_edit_mask(C);
495
496                 op->customdata = slidedata;
497
498                 WM_event_add_modal_handler(C, op);
499
500                 if (slidedata->uw) {
501                         if ((slidedata->uw->flag & SELECT) == 0) {
502                                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
503
504                                 slidedata->uw->flag |= SELECT;
505
506                                 ED_mask_select_flush_all(mask);
507                         }
508                 }
509                 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
510                         ED_mask_select_toggle_all(mask, SEL_DESELECT);
511
512                         BKE_mask_point_select_set(slidedata->point, TRUE);
513
514                         ED_mask_select_flush_all(mask);
515                 }
516
517                 slidedata->masklay->act_spline = slidedata->spline;
518                 slidedata->masklay->act_point = slidedata->point;
519
520                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
521
522                 return OPERATOR_RUNNING_MODAL;
523         }
524
525         return OPERATOR_PASS_THROUGH;
526 }
527
528 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
529 {
530         int i;
531
532         for (i = 0; i < data->spline->tot_point; i++) {
533                 MaskSplinePoint *point = &data->spline->points[i];
534                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
535
536                 point->bezt.weight = orig_point->bezt.weight + delta;
537                 if (point->bezt.weight < 0.0f)
538                         point->bezt.weight = 0.0f;
539
540                 /* not needed anymore */
541 #if 0
542                 int j;
543                 for (j = 0; j < point->tot_uw; j++) {
544                         point->uw[j].w = orig_point->uw[j].w + delta;
545                         if (point->uw[j].w < 0.0f)
546                                 point->uw[j].w = 0.0f;
547                 }
548 #endif
549         }
550 }
551
552 static void slide_point_restore_spline(SlidePointData *data)
553 {
554         int i;
555
556         for (i = 0; i < data->spline->tot_point; i++) {
557                 MaskSplinePoint *point = &data->spline->points[i];
558                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
559                 int j;
560
561                 point->bezt = orig_point->bezt;
562
563                 for (j = 0; j < point->tot_uw; j++)
564                         point->uw[j] = orig_point->uw[j];
565         }
566 }
567
568 static void cancel_slide_point(SlidePointData *data)
569 {
570         /* cancel sliding */
571
572         if (data->orig_spline) {
573                 slide_point_restore_spline(data);
574         }
575         else {
576                 if (data->action == SLIDE_ACTION_FEATHER) {
577                         if (data->uw)
578                                 data->uw->w = data->weight;
579                         else
580                                 data->point->bezt.weight = data->weight;
581                 }
582                 else {
583                         copy_m3_m3(data->point->bezt.vec, data->vec);
584                 }
585         }
586 }
587
588 static void free_slide_point_data(SlidePointData *data)
589 {
590         if (data->orig_spline)
591                 BKE_mask_spline_free(data->orig_spline);
592
593         MEM_freeN(data);
594 }
595
596 static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
597 {
598         SlidePointData *data = (SlidePointData *)op->customdata;
599         BezTriple *bezt = &data->point->bezt;
600         float co[2], dco[2];
601
602         switch (event->type) {
603                 case LEFTCTRLKEY:
604                 case RIGHTCTRLKEY:
605                 case LEFTSHIFTKEY:
606                 case RIGHTSHIFTKEY:
607                         if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
608                                 if (data->action == SLIDE_ACTION_FEATHER)
609                                         data->overall_feather = event->val == KM_PRESS;
610                                 else
611                                         data->curvature_only = event->val == KM_PRESS;
612                         }
613
614                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
615                                 data->accurate = event->val == KM_PRESS;
616
617                 /* no break! update CV position */
618
619                 case MOUSEMOVE:
620                         ED_mask_mouse_pos(C, event, co);
621                         sub_v2_v2v2(dco, co, data->co);
622
623                         if (data->action == SLIDE_ACTION_HANDLE) {
624                                 float delta[2], offco[2];
625
626                                 sub_v2_v2v2(delta, data->handle, data->co);
627
628                                 sub_v2_v2v2(offco, co, data->co);
629                                 if (data->accurate)
630                                         mul_v2_fl(offco, 0.2f);
631                                 add_v2_v2(offco, data->co);
632                                 add_v2_v2(offco, delta);
633
634                                 BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
635                         }
636                         else if (data->action == SLIDE_ACTION_POINT) {
637                                 float delta[2];
638
639                                 copy_v2_v2(delta, dco);
640                                 if (data->accurate)
641                                         mul_v2_fl(delta, 0.2f);
642
643                                 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
644                                 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
645                                 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
646                         }
647                         else if (data->action == SLIDE_ACTION_FEATHER) {
648                                 float vec[2], no[2], p[2], c[2], w, offco[2];
649                                 float *weight = NULL;
650                                 float weight_scalar = 1.0f;
651                                 int overall_feather = data->overall_feather || data->initial_feather;
652
653                                 add_v2_v2v2(offco, data->feather, dco);
654
655                                 if (data->uw) {
656                                         /* project on both sides and find the closest one,
657                                          * prevents flickering when projecting onto both sides can happen */
658                                         const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
659                                                                                        data->uw->u, offco, MASK_PROJ_NEG);
660                                         const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
661                                                                                        data->uw->u, offco, MASK_PROJ_POS);
662                                         float dist_pos = FLT_MAX;
663                                         float dist_neg = FLT_MAX;
664                                         float co_pos[2];
665                                         float co_neg[2];
666                                         float u;
667
668                                         if (u_pos > 0.0f && u_pos < 1.0f) {
669                                                 BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
670                                                 dist_pos = len_squared_v2v2(offco, co_pos);
671                                         }
672
673                                         if (u_neg > 0.0f && u_neg < 1.0f) {
674                                                 BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
675                                                 dist_neg = len_squared_v2v2(offco, co_neg);
676                                         }
677
678                                         u = dist_pos < dist_neg ? u_pos : u_neg;
679
680                                         if (u > 0.0f && u < 1.0f) {
681                                                 data->uw->u = u;
682
683                                                 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
684                                                 weight = &data->uw->w;
685                                                 weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
686                                                 if (weight_scalar != 0.0f) {
687                                                         weight_scalar = 1.0f / weight_scalar;
688                                                 }
689
690                                                 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
691                                                 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
692                                         }
693                                 }
694                                 else {
695                                         weight = &bezt->weight;
696                                         /* weight_scalar = 1.0f; keep as is */
697                                         copy_v2_v2(no, data->no);
698                                         copy_v2_v2(p, bezt->vec[1]);
699                                 }
700
701                                 if (weight) {
702                                         sub_v2_v2v2(c, offco, p);
703                                         project_v2_v2v2(vec, c, no);
704
705                                         w = len_v2(vec);
706
707                                         if (overall_feather) {
708                                                 float delta;
709
710                                                 if (dot_v2v2(no, vec) <= 0.0f)
711                                                         w = -w;
712
713                                                 delta = w - data->weight;
714
715                                                 if (data->orig_spline == NULL) {
716                                                         /* restore weight for currently sliding point, so orig_spline would be created
717                                                          * with original weights used
718                                                          */
719                                                         *weight = data->weight * weight_scalar;
720
721                                                         data->orig_spline = BKE_mask_spline_copy(data->spline);
722                                                 }
723
724                                                 slide_point_delta_all_feather(data, delta);
725                                         }
726                                         else {
727                                                 if (dot_v2v2(no, vec) <= 0.0f)
728                                                         w = 0.0f;
729
730                                                 if (data->orig_spline) {
731                                                         /* restore possible overall feather changes */
732                                                         slide_point_restore_spline(data);
733
734                                                         BKE_mask_spline_free(data->orig_spline);
735                                                         data->orig_spline = NULL;
736                                                 }
737
738                                                 if (weight_scalar != 0.0f) {
739                                                         *weight = w * weight_scalar;
740                                                 }
741                                         }
742                                 }
743                         }
744
745                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
746                         DAG_id_tag_update(&data->mask->id, 0);
747
748                         break;
749
750                 case LEFTMOUSE:
751                         if (event->val == KM_RELEASE) {
752                                 Scene *scene = CTX_data_scene(C);
753
754                                 free_slide_point_data(op->customdata);
755
756                                 /* dont key sliding feather uw's */
757                                 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
758                                         if (IS_AUTOKEY_ON(scene)) {
759                                                 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
760                                         }
761                                 }
762
763                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
764                                 DAG_id_tag_update(&data->mask->id, 0);
765
766                                 return OPERATOR_FINISHED;
767                         }
768
769                         break;
770
771                 case ESCKEY:
772                         cancel_slide_point(op->customdata);
773
774                         free_slide_point_data(op->customdata);
775
776                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
777                         DAG_id_tag_update(&data->mask->id, 0);
778
779                         return OPERATOR_CANCELLED;
780         }
781
782         return OPERATOR_RUNNING_MODAL;
783 }
784
785 void MASK_OT_slide_point(wmOperatorType *ot)
786 {
787         /* identifiers */
788         ot->name = "Slide Point";
789         ot->description = "Slide control points";
790         ot->idname = "MASK_OT_slide_point";
791
792         /* api callbacks */
793         ot->invoke = slide_point_invoke;
794         ot->modal = slide_point_modal;
795         ot->poll = ED_maskedit_mask_poll;
796
797         /* flags */
798         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
799
800         RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
801 }
802
803 /******************** toggle cyclic *********************/
804
805 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
806 {
807         Mask *mask = CTX_data_edit_mask(C);
808         MaskLayer *masklay;
809
810         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
811                 MaskSpline *spline;
812
813                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
814                         continue;
815                 }
816
817                 for (spline = masklay->splines.first; spline; spline = spline->next) {
818                         if (ED_mask_spline_select_check(spline)) {
819                                 spline->flag ^= MASK_SPLINE_CYCLIC;
820                         }
821                 }
822         }
823
824         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
825
826         return OPERATOR_FINISHED;
827 }
828
829 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
830 {
831         /* identifiers */
832         ot->name = "Toggle Cyclic";
833         ot->description = "Toggle cyclic for selected splines";
834         ot->idname = "MASK_OT_cyclic_toggle";
835
836         /* api callbacks */
837         ot->exec = cyclic_toggle_exec;
838         ot->poll = ED_maskedit_mask_poll;
839
840         /* flags */
841         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
842 }
843
844 /******************** delete *********************/
845
846 static void delete_feather_points(MaskSplinePoint *point)
847 {
848         int i, count = 0;
849
850         if (!point->tot_uw)
851                 return;
852
853         for (i = 0; i < point->tot_uw; i++) {
854                 if ((point->uw[i].flag & SELECT) == 0)
855                         count++;
856         }
857
858         if (count == 0) {
859                 MEM_freeN(point->uw);
860                 point->uw = NULL;
861                 point->tot_uw = 0;
862         }
863         else {
864                 MaskSplinePointUW *new_uw;
865                 int j = 0;
866
867                 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
868
869                 for (i = 0; i < point->tot_uw; i++) {
870                         if ((point->uw[i].flag & SELECT) == 0) {
871                                 new_uw[j++] = point->uw[i];
872                         }
873                 }
874
875                 MEM_freeN(point->uw);
876
877                 point->uw = new_uw;
878                 point->tot_uw = count;
879         }
880 }
881
882 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
883 {
884         Mask *mask = CTX_data_edit_mask(C);
885         MaskLayer *masklay;
886
887         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
888                 MaskSpline *spline;
889                 int mask_layer_shape_ofs = 0;
890
891                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
892                         continue;
893                 }
894
895                 spline = masklay->splines.first;
896
897                 while (spline) {
898                         const int tot_point_orig = spline->tot_point;
899                         int i, count = 0;
900                         MaskSpline *next_spline = spline->next;
901
902                         /* count unselected points */
903                         for (i = 0; i < spline->tot_point; i++) {
904                                 MaskSplinePoint *point = &spline->points[i];
905
906                                 if (!MASKPOINT_ISSEL_ANY(point))
907                                         count++;
908                         }
909
910                         if (count == 0) {
911
912                                 /* delete the whole spline */
913                                 BLI_remlink(&masklay->splines, spline);
914                                 BKE_mask_spline_free(spline);
915
916                                 if (spline == masklay->act_spline) {
917                                         masklay->act_spline = NULL;
918                                         masklay->act_point = NULL;
919                                 }
920
921                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
922                         }
923                         else {
924                                 MaskSplinePoint *new_points;
925                                 int j;
926
927                                 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
928
929                                 for (i = 0, j = 0; i < tot_point_orig; i++) {
930                                         MaskSplinePoint *point = &spline->points[i];
931
932                                         if (!MASKPOINT_ISSEL_ANY(point)) {
933                                                 if (point == masklay->act_point)
934                                                         masklay->act_point = &new_points[j];
935
936                                                 delete_feather_points(point);
937
938                                                 new_points[j] = *point;
939                                                 j++;
940                                         }
941                                         else {
942                                                 if (point == masklay->act_point)
943                                                         masklay->act_point = NULL;
944
945                                                 BKE_mask_point_free(point);
946                                                 spline->tot_point--;
947
948                                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
949                                         }
950                                 }
951
952                                 mask_layer_shape_ofs += spline->tot_point;
953
954                                 MEM_freeN(spline->points);
955                                 spline->points = new_points;
956
957                                 ED_mask_select_flush_all(mask);
958                         }
959
960                         spline = next_spline;
961                 }
962         }
963
964         /* TODO: only update edited splines */
965         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
966
967         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
968
969         return OPERATOR_FINISHED;
970 }
971
972 void MASK_OT_delete(wmOperatorType *ot)
973 {
974         /* identifiers */
975         ot->name = "Delete";
976         ot->description = "Delete selected control points or splines";
977         ot->idname = "MASK_OT_delete";
978
979         /* api callbacks */
980         ot->invoke = WM_operator_confirm;
981         ot->exec = delete_exec;
982         ot->poll = ED_maskedit_mask_poll;
983
984         /* flags */
985         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
986 }
987
988 /* *** switch direction *** */
989 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
990 {
991         Mask *mask = CTX_data_edit_mask(C);
992         MaskLayer *masklay;
993
994         int change = FALSE;
995
996         /* do actual selection */
997         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
998                 MaskSpline *spline;
999
1000                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1001                         continue;
1002                 }
1003
1004                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1005                         if (ED_mask_spline_select_check(spline)) {
1006                                 BKE_mask_spline_direction_switch(masklay, spline);
1007                                 change = TRUE;
1008                         }
1009                 }
1010         }
1011
1012         if (change) {
1013                 /* TODO: only update this spline */
1014                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1015
1016                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1017
1018                 return OPERATOR_FINISHED;
1019         }
1020
1021         return OPERATOR_CANCELLED;
1022 }
1023
1024 void MASK_OT_switch_direction(wmOperatorType *ot)
1025 {
1026         /* identifiers */
1027         ot->name = "Switch Direction";
1028         ot->description = "Switch direction of selected splines";
1029         ot->idname = "MASK_OT_switch_direction";
1030
1031         /* api callbacks */
1032         ot->exec = mask_switch_direction_exec;
1033         ot->poll = ED_maskedit_mask_poll;
1034
1035         /* flags */
1036         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1037 }
1038
1039
1040 /******************** set handle type *********************/
1041
1042 static int set_handle_type_exec(bContext *C, wmOperator *op)
1043 {
1044         Mask *mask = CTX_data_edit_mask(C);
1045         MaskLayer *masklay;
1046         int handle_type = RNA_enum_get(op->ptr, "type");
1047
1048         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1049                 MaskSpline *spline;
1050                 int i;
1051
1052                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1053                         continue;
1054                 }
1055
1056                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1057                         for (i = 0; i < spline->tot_point; i++) {
1058                                 MaskSplinePoint *point = &spline->points[i];
1059
1060                                 if (MASKPOINT_ISSEL_ANY(point)) {
1061                                         BezTriple *bezt = &point->bezt;
1062
1063                                         bezt->h1 = bezt->h2 = handle_type;
1064                                 }
1065                         }
1066                 }
1067         }
1068
1069         WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1070         DAG_id_tag_update(&mask->id, 0);
1071
1072         return OPERATOR_FINISHED;
1073 }
1074
1075 void MASK_OT_handle_type_set(wmOperatorType *ot)
1076 {
1077         static EnumPropertyItem editcurve_handle_type_items[] = {
1078                 {HD_AUTO, "AUTO", 0, "Auto", ""},
1079                 {HD_VECT, "VECTOR", 0, "Vector", ""},
1080                 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1081                 {0, NULL, 0, NULL, NULL}
1082         };
1083
1084         /* identifiers */
1085         ot->name = "Set Handle Type";
1086         ot->description = "Set type of handles for selected control points";
1087         ot->idname = "MASK_OT_handle_type_set";
1088
1089         /* api callbacks */
1090         ot->invoke = WM_menu_invoke;
1091         ot->exec = set_handle_type_exec;
1092         ot->poll = ED_maskedit_mask_poll;
1093
1094         /* flags */
1095         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1096
1097         /* properties */
1098         ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1099 }
1100
1101
1102 /* ********* clear/set restrict view *********/
1103 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1104 {
1105         Mask *mask = CTX_data_edit_mask(C);
1106         MaskLayer *masklay;
1107         int changed = FALSE;
1108
1109         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1110
1111                 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1112                         ED_mask_layer_select_set(masklay, TRUE);
1113                         masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1114                         changed = 1;
1115                 }
1116         }
1117
1118         if (changed) {
1119                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1120                 DAG_id_tag_update(&mask->id, 0);
1121
1122                 return OPERATOR_FINISHED;
1123         }
1124         else {
1125                 return OPERATOR_CANCELLED;
1126         }
1127 }
1128
1129 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1130 {
1131
1132         /* identifiers */
1133         ot->name = "Clear Restrict View";
1134         ot->description = "Reveal the layer by setting the hide flag";
1135         ot->idname = "MASK_OT_hide_view_clear";
1136
1137         /* api callbacks */
1138         ot->exec = mask_hide_view_clear_exec;
1139         ot->poll = ED_maskedit_mask_poll;
1140
1141         /* flags */
1142         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1143 }
1144
1145 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1146 {
1147         Mask *mask = CTX_data_edit_mask(C);
1148         MaskLayer *masklay;
1149         const int unselected = RNA_boolean_get(op->ptr, "unselected");
1150         int changed = FALSE;
1151
1152         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1153
1154                 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1155                         continue;
1156                 }
1157
1158                 if (!unselected) {
1159                         if (ED_mask_layer_select_check(masklay)) {
1160                                 ED_mask_layer_select_set(masklay, FALSE);
1161
1162                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1163                                 changed = 1;
1164                                 if (masklay == BKE_mask_layer_active(mask)) {
1165                                         BKE_mask_layer_active_set(mask, NULL);
1166                                 }
1167                         }
1168                 }
1169                 else {
1170                         if (!ED_mask_layer_select_check(masklay)) {
1171                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1172                                 changed = 1;
1173                                 if (masklay == BKE_mask_layer_active(mask)) {
1174                                         BKE_mask_layer_active_set(mask, NULL);
1175                                 }
1176                         }
1177                 }
1178         }
1179
1180         if (changed) {
1181                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1182                 DAG_id_tag_update(&mask->id, 0);
1183
1184                 return OPERATOR_FINISHED;
1185         }
1186         else {
1187                 return OPERATOR_CANCELLED;
1188         }
1189 }
1190
1191 void MASK_OT_hide_view_set(wmOperatorType *ot)
1192 {
1193         /* identifiers */
1194         ot->name = "Set Restrict View";
1195         ot->description = "Hide the layer by setting the hide flag";
1196         ot->idname = "MASK_OT_hide_view_set";
1197
1198         /* api callbacks */
1199         ot->exec = mask_hide_view_set_exec;
1200         ot->poll = ED_maskedit_mask_poll;
1201
1202         /* flags */
1203         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1204
1205         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1206 }
1207
1208
1209 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1210 {
1211         Mask *mask = CTX_data_edit_mask(C);
1212         MaskLayer *masklay;
1213         int changed = FALSE;
1214         int i;
1215
1216         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1217                 MaskSpline *spline;
1218
1219                 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1220                         continue;
1221                 }
1222
1223                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1224                         for (i = 0; i < spline->tot_point; i++) {
1225                                 MaskSplinePoint *point = &spline->points[i];
1226
1227                                 if (MASKPOINT_ISSEL_ANY(point)) {
1228                                         BezTriple *bezt = &point->bezt;
1229                                         bezt->weight = 0.0f;
1230                                         changed = TRUE;
1231                                 }
1232                         }
1233                 }
1234         }
1235
1236         if (changed) {
1237                 /* TODO: only update edited splines */
1238                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1239
1240                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1241                 DAG_id_tag_update(&mask->id, 0);
1242
1243                 return OPERATOR_FINISHED;
1244         }
1245         else {
1246                 return OPERATOR_CANCELLED;
1247         }
1248 }
1249
1250 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1251 {
1252         /* identifiers */
1253         ot->name = "Clear Feather Weight";
1254         ot->description = "Reset the feather weight to zero";
1255         ot->idname = "MASK_OT_feather_weight_clear";
1256
1257         /* api callbacks */
1258         ot->exec = mask_feather_weight_clear_exec;
1259         ot->poll = ED_maskedit_mask_poll;
1260
1261         /* flags */
1262         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1263 }