added mask tool to recalculate handles (Ctrl+N)
[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                                 /* dont key sliding feather uw's */
755                                 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
756                                         if (IS_AUTOKEY_ON(scene)) {
757                                                 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
758                                         }
759                                 }
760
761                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
762                                 DAG_id_tag_update(&data->mask->id, 0);
763
764                                 free_slide_point_data(op->customdata); /* keep this last! */
765                                 return OPERATOR_FINISHED;
766                         }
767
768                         break;
769
770                 case ESCKEY:
771                         cancel_slide_point(op->customdata);
772
773                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
774                         DAG_id_tag_update(&data->mask->id, 0);
775
776                         free_slide_point_data(op->customdata); /* keep this last! */
777                         return OPERATOR_CANCELLED;
778         }
779
780         return OPERATOR_RUNNING_MODAL;
781 }
782
783 void MASK_OT_slide_point(wmOperatorType *ot)
784 {
785         /* identifiers */
786         ot->name = "Slide Point";
787         ot->description = "Slide control points";
788         ot->idname = "MASK_OT_slide_point";
789
790         /* api callbacks */
791         ot->invoke = slide_point_invoke;
792         ot->modal = slide_point_modal;
793         ot->poll = ED_maskedit_mask_poll;
794
795         /* flags */
796         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
797
798         RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
799 }
800
801 /******************** toggle cyclic *********************/
802
803 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
804 {
805         Mask *mask = CTX_data_edit_mask(C);
806         MaskLayer *masklay;
807
808         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
809                 MaskSpline *spline;
810
811                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
812                         continue;
813                 }
814
815                 for (spline = masklay->splines.first; spline; spline = spline->next) {
816                         if (ED_mask_spline_select_check(spline)) {
817                                 spline->flag ^= MASK_SPLINE_CYCLIC;
818                         }
819                 }
820         }
821
822         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
823
824         return OPERATOR_FINISHED;
825 }
826
827 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
828 {
829         /* identifiers */
830         ot->name = "Toggle Cyclic";
831         ot->description = "Toggle cyclic for selected splines";
832         ot->idname = "MASK_OT_cyclic_toggle";
833
834         /* api callbacks */
835         ot->exec = cyclic_toggle_exec;
836         ot->poll = ED_maskedit_mask_poll;
837
838         /* flags */
839         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
840 }
841
842 /******************** delete *********************/
843
844 static void delete_feather_points(MaskSplinePoint *point)
845 {
846         int i, count = 0;
847
848         if (!point->tot_uw)
849                 return;
850
851         for (i = 0; i < point->tot_uw; i++) {
852                 if ((point->uw[i].flag & SELECT) == 0)
853                         count++;
854         }
855
856         if (count == 0) {
857                 MEM_freeN(point->uw);
858                 point->uw = NULL;
859                 point->tot_uw = 0;
860         }
861         else {
862                 MaskSplinePointUW *new_uw;
863                 int j = 0;
864
865                 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
866
867                 for (i = 0; i < point->tot_uw; i++) {
868                         if ((point->uw[i].flag & SELECT) == 0) {
869                                 new_uw[j++] = point->uw[i];
870                         }
871                 }
872
873                 MEM_freeN(point->uw);
874
875                 point->uw = new_uw;
876                 point->tot_uw = count;
877         }
878 }
879
880 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
881 {
882         Mask *mask = CTX_data_edit_mask(C);
883         MaskLayer *masklay;
884
885         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
886                 MaskSpline *spline;
887                 int mask_layer_shape_ofs = 0;
888
889                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
890                         continue;
891                 }
892
893                 spline = masklay->splines.first;
894
895                 while (spline) {
896                         const int tot_point_orig = spline->tot_point;
897                         int i, count = 0;
898                         MaskSpline *next_spline = spline->next;
899
900                         /* count unselected points */
901                         for (i = 0; i < spline->tot_point; i++) {
902                                 MaskSplinePoint *point = &spline->points[i];
903
904                                 if (!MASKPOINT_ISSEL_ANY(point))
905                                         count++;
906                         }
907
908                         if (count == 0) {
909
910                                 /* delete the whole spline */
911                                 BLI_remlink(&masklay->splines, spline);
912                                 BKE_mask_spline_free(spline);
913
914                                 if (spline == masklay->act_spline) {
915                                         masklay->act_spline = NULL;
916                                         masklay->act_point = NULL;
917                                 }
918
919                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
920                         }
921                         else {
922                                 MaskSplinePoint *new_points;
923                                 int j;
924
925                                 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
926
927                                 for (i = 0, j = 0; i < tot_point_orig; i++) {
928                                         MaskSplinePoint *point = &spline->points[i];
929
930                                         if (!MASKPOINT_ISSEL_ANY(point)) {
931                                                 if (point == masklay->act_point)
932                                                         masklay->act_point = &new_points[j];
933
934                                                 delete_feather_points(point);
935
936                                                 new_points[j] = *point;
937                                                 j++;
938                                         }
939                                         else {
940                                                 if (point == masklay->act_point)
941                                                         masklay->act_point = NULL;
942
943                                                 BKE_mask_point_free(point);
944                                                 spline->tot_point--;
945
946                                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
947                                         }
948                                 }
949
950                                 mask_layer_shape_ofs += spline->tot_point;
951
952                                 MEM_freeN(spline->points);
953                                 spline->points = new_points;
954
955                                 ED_mask_select_flush_all(mask);
956                         }
957
958                         spline = next_spline;
959                 }
960
961                 /* not essential but confuses users when there are keys with no data!
962                  * assume if they delete all data from the layer they also dont care about keys */
963                 if (masklay->splines.first == NULL) {
964                         BKE_mask_layer_free_shapes(masklay);
965                 }
966         }
967
968         /* TODO: only update edited splines */
969         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
970
971         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
972
973         return OPERATOR_FINISHED;
974 }
975
976 void MASK_OT_delete(wmOperatorType *ot)
977 {
978         /* identifiers */
979         ot->name = "Delete";
980         ot->description = "Delete selected control points or splines";
981         ot->idname = "MASK_OT_delete";
982
983         /* api callbacks */
984         ot->invoke = WM_operator_confirm;
985         ot->exec = delete_exec;
986         ot->poll = ED_maskedit_mask_poll;
987
988         /* flags */
989         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
990 }
991
992 /* *** switch direction *** */
993 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
994 {
995         Scene *scene = CTX_data_scene(C);
996         Mask *mask = CTX_data_edit_mask(C);
997         MaskLayer *masklay;
998
999         int change = FALSE;
1000
1001         /* do actual selection */
1002         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1003                 MaskSpline *spline;
1004                 int change_layer = FALSE;
1005
1006                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1007                         continue;
1008                 }
1009
1010                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1011                         if (ED_mask_spline_select_check(spline)) {
1012                                 BKE_mask_spline_direction_switch(masklay, spline);
1013                                 change = TRUE;
1014                                 change_layer = TRUE;
1015                         }
1016                 }
1017
1018                 if (change_layer) {
1019                         if (IS_AUTOKEY_ON(scene)) {
1020                                 ED_mask_layer_shape_auto_key(masklay, CFRA);
1021                         }
1022                 }
1023         }
1024
1025         if (change) {
1026                 /* TODO: only update this spline */
1027                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1028
1029                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1030
1031                 return OPERATOR_FINISHED;
1032         }
1033
1034         return OPERATOR_CANCELLED;
1035 }
1036
1037 void MASK_OT_switch_direction(wmOperatorType *ot)
1038 {
1039         /* identifiers */
1040         ot->name = "Switch Direction";
1041         ot->description = "Switch direction of selected splines";
1042         ot->idname = "MASK_OT_switch_direction";
1043
1044         /* api callbacks */
1045         ot->exec = mask_switch_direction_exec;
1046         ot->poll = ED_maskedit_mask_poll;
1047
1048         /* flags */
1049         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1050 }
1051
1052
1053 /* *** recalc normals *** */
1054 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1055 {
1056         Scene *scene = CTX_data_scene(C);
1057         Mask *mask = CTX_data_edit_mask(C);
1058         MaskLayer *masklay;
1059         int i;
1060
1061         int change = FALSE;
1062
1063         /* do actual selection */
1064         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1065                 MaskSpline *spline;
1066                 int change_layer = FALSE;
1067
1068                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1069                         continue;
1070                 }
1071
1072                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1073                         for (i = 0; i < spline->tot_point; i++) {
1074                                 MaskSplinePoint *point = &spline->points[i];
1075
1076                                 if (MASKPOINT_ISSEL_ANY(point)) {
1077                                         BKE_mask_calc_handle_point_auto(spline, point, FALSE);
1078                                         change = TRUE;
1079                                         change_layer = TRUE;
1080                                 }
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, CTX_data_scene(C)->r.cfra);
1094
1095                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1096
1097                 return OPERATOR_FINISHED;
1098         }
1099
1100         return OPERATOR_CANCELLED;
1101 }
1102
1103 /* named to match mesh recalc normals */
1104 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1105 {
1106         /* identifiers */
1107         ot->name = "Recalc Normals";
1108         ot->description = "Re-calculate the direction of selected handles";
1109         ot->idname = "MASK_OT_normals_make_consistent";
1110
1111         /* api callbacks */
1112         ot->exec = mask_normals_make_consistent_exec;
1113         ot->poll = ED_maskedit_mask_poll;
1114
1115         /* flags */
1116         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1117 }
1118
1119
1120 /******************** set handle type *********************/
1121
1122 static int set_handle_type_exec(bContext *C, wmOperator *op)
1123 {
1124         Mask *mask = CTX_data_edit_mask(C);
1125         MaskLayer *masklay;
1126         int handle_type = RNA_enum_get(op->ptr, "type");
1127
1128         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1129                 MaskSpline *spline;
1130                 int i;
1131
1132                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1133                         continue;
1134                 }
1135
1136                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1137                         for (i = 0; i < spline->tot_point; i++) {
1138                                 MaskSplinePoint *point = &spline->points[i];
1139
1140                                 if (MASKPOINT_ISSEL_ANY(point)) {
1141                                         BezTriple *bezt = &point->bezt;
1142
1143                                         bezt->h1 = bezt->h2 = handle_type;
1144                                 }
1145                         }
1146                 }
1147         }
1148
1149         WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1150         DAG_id_tag_update(&mask->id, 0);
1151
1152         return OPERATOR_FINISHED;
1153 }
1154
1155 void MASK_OT_handle_type_set(wmOperatorType *ot)
1156 {
1157         static EnumPropertyItem editcurve_handle_type_items[] = {
1158                 {HD_AUTO, "AUTO", 0, "Auto", ""},
1159                 {HD_VECT, "VECTOR", 0, "Vector", ""},
1160                 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1161                 {0, NULL, 0, NULL, NULL}
1162         };
1163
1164         /* identifiers */
1165         ot->name = "Set Handle Type";
1166         ot->description = "Set type of handles for selected control points";
1167         ot->idname = "MASK_OT_handle_type_set";
1168
1169         /* api callbacks */
1170         ot->invoke = WM_menu_invoke;
1171         ot->exec = set_handle_type_exec;
1172         ot->poll = ED_maskedit_mask_poll;
1173
1174         /* flags */
1175         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1176
1177         /* properties */
1178         ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1179 }
1180
1181
1182 /* ********* clear/set restrict view *********/
1183 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1184 {
1185         Mask *mask = CTX_data_edit_mask(C);
1186         MaskLayer *masklay;
1187         int changed = FALSE;
1188
1189         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1190
1191                 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1192                         ED_mask_layer_select_set(masklay, TRUE);
1193                         masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1194                         changed = 1;
1195                 }
1196         }
1197
1198         if (changed) {
1199                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1200                 DAG_id_tag_update(&mask->id, 0);
1201
1202                 return OPERATOR_FINISHED;
1203         }
1204         else {
1205                 return OPERATOR_CANCELLED;
1206         }
1207 }
1208
1209 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1210 {
1211
1212         /* identifiers */
1213         ot->name = "Clear Restrict View";
1214         ot->description = "Reveal the layer by setting the hide flag";
1215         ot->idname = "MASK_OT_hide_view_clear";
1216
1217         /* api callbacks */
1218         ot->exec = mask_hide_view_clear_exec;
1219         ot->poll = ED_maskedit_mask_poll;
1220
1221         /* flags */
1222         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1223 }
1224
1225 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1226 {
1227         Mask *mask = CTX_data_edit_mask(C);
1228         MaskLayer *masklay;
1229         const int unselected = RNA_boolean_get(op->ptr, "unselected");
1230         int changed = FALSE;
1231
1232         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1233
1234                 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1235                         continue;
1236                 }
1237
1238                 if (!unselected) {
1239                         if (ED_mask_layer_select_check(masklay)) {
1240                                 ED_mask_layer_select_set(masklay, FALSE);
1241
1242                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1243                                 changed = 1;
1244                                 if (masklay == BKE_mask_layer_active(mask)) {
1245                                         BKE_mask_layer_active_set(mask, NULL);
1246                                 }
1247                         }
1248                 }
1249                 else {
1250                         if (!ED_mask_layer_select_check(masklay)) {
1251                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1252                                 changed = 1;
1253                                 if (masklay == BKE_mask_layer_active(mask)) {
1254                                         BKE_mask_layer_active_set(mask, NULL);
1255                                 }
1256                         }
1257                 }
1258         }
1259
1260         if (changed) {
1261                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1262                 DAG_id_tag_update(&mask->id, 0);
1263
1264                 return OPERATOR_FINISHED;
1265         }
1266         else {
1267                 return OPERATOR_CANCELLED;
1268         }
1269 }
1270
1271 void MASK_OT_hide_view_set(wmOperatorType *ot)
1272 {
1273         /* identifiers */
1274         ot->name = "Set Restrict View";
1275         ot->description = "Hide the layer by setting the hide flag";
1276         ot->idname = "MASK_OT_hide_view_set";
1277
1278         /* api callbacks */
1279         ot->exec = mask_hide_view_set_exec;
1280         ot->poll = ED_maskedit_mask_poll;
1281
1282         /* flags */
1283         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1284
1285         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1286 }
1287
1288
1289 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1290 {
1291         Mask *mask = CTX_data_edit_mask(C);
1292         MaskLayer *masklay;
1293         int changed = FALSE;
1294         int i;
1295
1296         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1297                 MaskSpline *spline;
1298
1299                 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1300                         continue;
1301                 }
1302
1303                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1304                         for (i = 0; i < spline->tot_point; i++) {
1305                                 MaskSplinePoint *point = &spline->points[i];
1306
1307                                 if (MASKPOINT_ISSEL_ANY(point)) {
1308                                         BezTriple *bezt = &point->bezt;
1309                                         bezt->weight = 0.0f;
1310                                         changed = TRUE;
1311                                 }
1312                         }
1313                 }
1314         }
1315
1316         if (changed) {
1317                 /* TODO: only update edited splines */
1318                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1319
1320                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1321                 DAG_id_tag_update(&mask->id, 0);
1322
1323                 return OPERATOR_FINISHED;
1324         }
1325         else {
1326                 return OPERATOR_CANCELLED;
1327         }
1328 }
1329
1330 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1331 {
1332         /* identifiers */
1333         ot->name = "Clear Feather Weight";
1334         ot->description = "Reset the feather weight to zero";
1335         ot->idname = "MASK_OT_feather_weight_clear";
1336
1337         /* api callbacks */
1338         ot->exec = mask_feather_weight_clear_exec;
1339         ot->poll = ED_maskedit_mask_poll;
1340
1341         /* flags */
1342         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1343 }