style cleanup
[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_utildefines.h"
35 #include "BLI_listbase.h"
36 #include "BLI_math.h"
37
38 #include "BKE_context.h"
39 #include "BKE_curve.h"
40 #include "BKE_depsgraph.h"
41 #include "BKE_mask.h"
42
43 #include "DNA_scene_types.h"
44 #include "DNA_mask_types.h"
45 #include "DNA_object_types.h"  /* SELECT */
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_screen.h"
51 #include "ED_mask.h"
52 #include "ED_clip.h"
53 #include "ED_keyframing.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "mask_intern.h"  /* own include */
59
60 /******************** utility functions *********************/
61
62 static float projection_on_spline(MaskSpline *spline, MaskSplinePoint *point, float start_u, const float co[2])
63 {
64         const float proj_eps         = 1e-3;
65         const float proj_eps_squared = proj_eps * proj_eps;
66         const int N = 1000;
67         float u = -1.0f, du = 1.0f / N, u1 = start_u, u2 = start_u;
68         float ang = -1.0f;
69
70         while (u1 > 0.0f || u2 < 1.0f) {
71                 float n1[2], n2[2], co1[2], co2[2];
72                 float v1[2], v2[2];
73                 float ang1, ang2;
74
75                 if (u1 >= 0.0f) {
76                         BKE_mask_point_segment_co(spline, point, u1, co1);
77                         BKE_mask_point_normal(spline, point, u1, n1);
78                         sub_v2_v2v2(v1, co, co1);
79
80                         if (len_squared_v2(v1) > proj_eps_squared) {
81                                 ang1 = angle_v2v2(v1, n1);
82                                 if (ang1 > M_PI / 2.0f)
83                                         ang1 = M_PI  - ang1;
84
85                                 if (ang < 0.0f || ang1 < ang) {
86                                         ang = ang1;
87                                         u = u1;
88                                 }
89                         }
90                         else {
91                                 u = u1;
92                                 break;
93                         }
94                 }
95
96                 if (u2 <= 1.0f) {
97                         BKE_mask_point_segment_co(spline, point, u2, co2);
98                         BKE_mask_point_normal(spline, point, u2, n2);
99                         sub_v2_v2v2(v2, co, co2);
100
101                         if (len_squared_v2(v2) > proj_eps_squared) {
102                                 ang2 = angle_v2v2(v2, n2);
103                                 if (ang2 > M_PI / 2.0f)
104                                         ang2 = M_PI  - ang2;
105
106                                 if (ang2 < ang) {
107                                         ang = ang2;
108                                         u = u2;
109                                 }
110                         }
111                         else {
112                                 u = u2;
113                                 break;
114                         }
115                 }
116
117                 u1 -= du;
118                 u2 += du;
119         }
120
121         return u;
122 }
123
124 MaskSplinePoint *ED_mask_point_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
125                                             MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
126                                             float *score)
127 {
128         MaskLayer *masklay;
129         MaskLayer *point_masklay = NULL;
130         MaskSpline *point_spline = NULL;
131         MaskSplinePoint *point = NULL;
132         float co[2], aspx, aspy;
133         float len = FLT_MAX, scalex, scaley;
134         int is_handle = FALSE, width, height;
135
136         ED_mask_size(C, &width, &height);
137         ED_mask_aspect(C, &aspx, &aspy);
138         ED_mask_pixelspace_factor(C, &scalex, &scaley);
139
140         co[0] = normal_co[0] * scalex;
141         co[1] = normal_co[1] * scaley;
142
143         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
144                 MaskSpline *spline;
145
146                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
147                         continue;
148                 }
149
150                 for (spline = masklay->splines.first; spline; spline = spline->next) {
151                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
152
153                         int i;
154
155                         for (i = 0; i < spline->tot_point; i++) {
156                                 MaskSplinePoint *cur_point = &spline->points[i];
157                                 MaskSplinePoint *cur_point_deform = &points_array[i];
158                                 float cur_len, vec[2], handle[2];
159
160                                 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
161                                 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
162
163                                 if (BKE_mask_point_has_handle(cur_point)) {
164                                         BKE_mask_point_handle(cur_point_deform, handle);
165                                         handle[0] *= scalex;
166                                         handle[1] *= scaley;
167
168                                         cur_len = len_v2v2(co, handle);
169
170                                         if (cur_len < len) {
171                                                 point_masklay = masklay;
172                                                 point_spline = spline;
173                                                 point = cur_point;
174                                                 len = cur_len;
175                                                 is_handle = TRUE;
176                                         }
177                                 }
178
179                                 cur_len = len_v2v2(co, vec);
180
181                                 if (cur_len < len) {
182                                         point_spline = spline;
183                                         point_masklay = masklay;
184                                         point = cur_point;
185                                         len = cur_len;
186                                         is_handle = FALSE;
187                                 }
188                         }
189                 }
190         }
191
192         if (len < threshold) {
193                 if (masklay_r)
194                         *masklay_r = point_masklay;
195
196                 if (spline_r)
197                         *spline_r = point_spline;
198
199                 if (is_handle_r)
200                         *is_handle_r = is_handle;
201
202                 if (score)
203                         *score = len;
204
205                 return point;
206         }
207
208         if (masklay_r)
209                 *masklay_r = NULL;
210
211         if (spline_r)
212                 *spline_r = NULL;
213
214         if (is_handle_r)
215                 *is_handle_r = FALSE;
216
217         return NULL;
218 }
219
220 int ED_mask_feather_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
221                                  MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
222                                  MaskSplinePointUW **uw_r, float *score)
223 {
224         MaskLayer *masklay, *point_masklay = NULL;
225         MaskSpline *point_spline = NULL;
226         MaskSplinePoint *point = NULL;
227         MaskSplinePointUW *uw = NULL;
228         float len = FLT_MAX, co[2];
229         float scalex, scaley, aspx, aspy;
230         int width, height;
231
232         ED_mask_size(C, &width, &height);
233         ED_mask_aspect(C, &aspx, &aspy);
234         ED_mask_pixelspace_factor(C, &scalex, &scaley);
235
236         co[0] = normal_co[0] * scalex;
237         co[1] = normal_co[1] * scaley;
238
239         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
240                 MaskSpline *spline;
241
242                 for (spline = masklay->splines.first; spline; spline = spline->next) {
243                         //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
244
245                         int i, tot_feather_point;
246                         float (*feather_points)[2], (*fp)[2];
247
248                         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
249                                 continue;
250                         }
251
252                         feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
253
254                         for (i = 0; i < spline->tot_point; i++) {
255                                 int j;
256                                 MaskSplinePoint *cur_point = &spline->points[i];
257
258                                 for (j = 0; j < cur_point->tot_uw + 1; j++) {
259                                         float cur_len, vec[2];
260
261                                         vec[0] = (*fp)[0] * scalex;
262                                         vec[1] = (*fp)[1] * scaley;
263
264                                         cur_len = len_v2v2(vec, co);
265
266                                         if (point == NULL || cur_len < len) {
267                                                 if (j == 0)
268                                                         uw = NULL;
269                                                 else
270                                                         uw = &cur_point->uw[j - 1];
271
272                                                 point_masklay = masklay;
273                                                 point_spline = spline;
274                                                 point = cur_point;
275                                                 len = cur_len;
276                                         }
277
278                                         fp++;
279                                 }
280                         }
281
282                         MEM_freeN(feather_points);
283                 }
284         }
285
286         if (len < threshold) {
287                 if (masklay_r)
288                         *masklay_r = point_masklay;
289
290                 if (spline_r)
291                         *spline_r = point_spline;
292
293                 if (point_r)
294                         *point_r = point;
295
296                 if (uw_r)
297                         *uw_r = uw;
298
299                 if (score)
300                         *score = len;
301
302                 return TRUE;
303         }
304
305         if (masklay_r)
306                 *masklay_r = NULL;
307
308         if (spline_r)
309                 *spline_r = NULL;
310
311         if (point_r)
312                 *point_r = NULL;
313
314         return FALSE;
315 }
316
317 static int find_nearest_diff_point(bContext *C, Mask *mask, const float normal_co[2], int threshold, int feather,
318                                    MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
319                                    float *u_r, float tangent[2])
320 {
321         MaskLayer *masklay, *point_masklay;
322         MaskSpline *point_spline;
323         MaskSplinePoint *point = NULL;
324         float dist, co[2];
325         int width, height;
326         float u;
327         float scalex, scaley, aspx, aspy;
328
329         ED_mask_size(C, &width, &height);
330         ED_mask_aspect(C, &aspx, &aspy);
331         ED_mask_pixelspace_factor(C, &scalex, &scaley);
332
333         co[0] = normal_co[0] * scalex;
334         co[1] = normal_co[1] * scaley;
335
336         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
337                 MaskSpline *spline;
338
339                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
340                         continue;
341                 }
342
343                 for (spline = masklay->splines.first; spline; spline = spline->next) {
344                         int i;
345
346                         for (i = 0; i < spline->tot_point; i++) {
347                                 MaskSplinePoint *cur_point = &spline->points[i];
348                                 float *diff_points;
349                                 int tot_diff_point;
350
351                                 diff_points = BKE_mask_point_segment_diff(spline, cur_point, &tot_diff_point);
352
353                                 if (diff_points) {
354                                         int i, tot_feather_point, tot_point;
355                                         float *feather_points = NULL, *points;
356
357                                         if (feather) {
358                                                 feather_points = BKE_mask_point_segment_feather_diff(spline, cur_point,
359                                                                                                      &tot_feather_point);
360
361                                                 points = feather_points;
362                                                 tot_point = tot_feather_point;
363                                         }
364                                         else {
365                                                 points = diff_points;
366                                                 tot_point = tot_diff_point;
367                                         }
368
369                                         for (i = 0; i < tot_point - 1; i++) {
370                                                 float cur_dist, a[2], b[2];
371
372                                                 a[0] = points[2 * i] * scalex;
373                                                 a[1] = points[2 * i + 1] * scaley;
374
375                                                 b[0] = points[2 * i + 2] * scalex;
376                                                 b[1] = points[2 * i + 3] * scaley;
377
378                                                 cur_dist = dist_to_line_segment_v2(co, a, b);
379
380                                                 if (point == NULL || cur_dist < dist) {
381                                                         if (tangent)
382                                                                 sub_v2_v2v2(tangent, &diff_points[2 * i + 2], &diff_points[2 * i]);
383
384                                                         point_masklay = masklay;
385                                                         point_spline = spline;
386                                                         point = cur_point;
387                                                         dist = cur_dist;
388                                                         u = (float)i / tot_point;
389
390                                                 }
391                                         }
392
393                                         if (feather_points)
394                                                 MEM_freeN(feather_points);
395
396                                         MEM_freeN(diff_points);
397                                 }
398                         }
399                 }
400         }
401
402         if (point && dist < threshold) {
403                 if (masklay_r)
404                         *masklay_r = point_masklay;
405
406                 if (spline_r)
407                         *spline_r = point_spline;
408
409                 if (point_r)
410                         *point_r = point;
411
412                 if (u_r) {
413                         u = projection_on_spline(point_spline, point, u, normal_co);
414
415                         *u_r = u;
416                 }
417
418                 return TRUE;
419         }
420
421         if (masklay_r)
422                 *masklay_r = NULL;
423
424         if (spline_r)
425                 *spline_r = NULL;
426
427         if (point_r)
428                 *point_r = NULL;
429
430         return FALSE;
431 }
432
433 /******************** create new mask *********************/
434
435 static int mask_new_exec(bContext *C, wmOperator *op)
436 {
437         SpaceClip *sc = CTX_wm_space_clip(C);
438         Mask *mask;
439         char name[MAX_ID_NAME - 2];
440
441         RNA_string_get(op->ptr, "name", name);
442
443         mask = BKE_mask_new(name);
444
445         if (sc)
446                 ED_space_clip_set_mask(C, sc, mask);
447
448         return OPERATOR_FINISHED;
449 }
450
451 void MASK_OT_new(wmOperatorType *ot)
452 {
453         /* identifiers */
454         ot->name = "New Mask";
455         ot->description = "Create new mask";
456         ot->idname = "MASK_OT_new";
457
458         /* flags */
459         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
460
461         /* api callbacks */
462         ot->exec = mask_new_exec;
463         ot->poll = ED_operator_mask;
464
465         /* properties */
466         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
467 }
468
469 /******************** create new masklay *********************/
470
471 static int masklay_new_exec(bContext *C, wmOperator *op)
472 {
473         Mask *mask = CTX_data_edit_mask(C);
474         char name[MAX_ID_NAME - 2];
475
476         RNA_string_get(op->ptr, "name", name);
477
478         BKE_mask_layer_new(mask, name);
479         mask->masklay_act = mask->masklay_tot - 1;
480
481         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
482
483         return OPERATOR_FINISHED;
484 }
485
486 void MASK_OT_layer_new(wmOperatorType *ot)
487 {
488         /* identifiers */
489         ot->name = "Add Mask Layer";
490         ot->description = "Add new mask layer for masking";
491         ot->idname = "MASK_OT_layer_new";
492
493         /* api callbacks */
494         ot->exec = masklay_new_exec;
495         ot->poll = ED_maskediting_poll;
496
497         /* flags */
498         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
499
500         /* properties */
501         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
502 }
503
504 /******************** remove mask layer *********************/
505
506 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
507 {
508         Mask *mask = CTX_data_edit_mask(C);
509         MaskLayer *masklay = BKE_mask_layer_active(mask);
510
511         if (masklay) {
512                 BKE_mask_layer_remove(mask, masklay);
513
514                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
515         }
516
517         return OPERATOR_FINISHED;
518 }
519
520 void MASK_OT_layer_remove(wmOperatorType *ot)
521 {
522         /* identifiers */
523         ot->name = "Remove Mask Layer";
524         ot->description = "Remove mask layer";
525         ot->idname = "MASK_OT_layer_remove";
526
527         /* api callbacks */
528         ot->exec = masklay_remove_exec;
529         ot->poll = ED_maskediting_poll;
530
531         /* flags */
532         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
533 }
534
535 /******************** slide *********************/
536
537 #define SLIDE_ACTION_NONE       0
538 #define SLIDE_ACTION_POINT      1
539 #define SLIDE_ACTION_HANDLE     2
540 #define SLIDE_ACTION_FEATHER    3
541
542 typedef struct SlidePointData {
543         int action;
544
545         float co[2];
546         float vec[3][3];
547
548         Mask *mask;
549         MaskLayer *masklay;
550         MaskSpline *spline;
551         MaskSplinePoint *point;
552         MaskSplinePointUW *uw;
553         float handle[2], no[2], feather[2];
554         float aspx, aspy;
555         int width, height;
556         float weight;
557
558         short curvature_only, accurate;
559 } SlidePointData;
560
561 static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
562 {
563         Mask *mask = CTX_data_edit_mask(C);
564         SlidePointData *customdata = NULL;
565         MaskLayer *masklay, *cv_masklay, *feather_masklay;
566         MaskSpline *spline, *cv_spline, *feather_spline;
567         MaskSplinePoint *point, *cv_point, *feather_point;
568         MaskSplinePointUW *uw = NULL;
569         int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
570         int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
571         float co[2], cv_score, feather_score;
572         const float threshold = 19;
573
574         ED_mask_mouse_pos(C, event, co);
575         ED_mask_size(C, &width, &height);
576
577         cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
578
579         if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
580                 if (slide_feather || !cv_point || feather_score < cv_score) {
581                         action = SLIDE_ACTION_FEATHER;
582
583                         masklay = feather_masklay;
584                         spline = feather_spline;
585                         point = feather_point;
586                 }
587         }
588
589         if (cv_point && action == SLIDE_ACTION_NONE) {
590                 if (is_handle)
591                         action = SLIDE_ACTION_HANDLE;
592                 else
593                         action = SLIDE_ACTION_POINT;
594
595                 masklay = cv_masklay;
596                 spline = cv_spline;
597                 point = cv_point;
598         }
599
600         if (action != SLIDE_ACTION_NONE) {
601                 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
602
603                 customdata->mask = mask;
604                 customdata->masklay = masklay;
605                 customdata->spline = spline;
606                 customdata->point = point;
607                 customdata->width = width;
608                 customdata->height = height;
609                 customdata->action = action;
610                 customdata->uw = uw;
611
612                 ED_mask_aspect(C, &customdata->aspx, &customdata->aspy);
613
614                 if (uw) {
615                         float co[2];
616
617                         customdata->weight = point->bezt.weight;
618
619                         customdata->weight = uw->w;
620                         BKE_mask_point_segment_co(spline, point, uw->u, co);
621                         BKE_mask_point_normal(spline, point, uw->u, customdata->no);
622
623                         customdata->feather[0] = co[0] + customdata->no[0] * uw->w;
624                         customdata->feather[1] = co[1] + customdata->no[1] * uw->w;
625                 }
626                 else {
627                         BezTriple *bezt = &point->bezt;
628                         BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
629
630                         customdata->feather[0] = bezt->vec[1][0] + customdata->no[0] * bezt->weight;
631                         customdata->feather[1] = bezt->vec[1][1] + customdata->no[1] * bezt->weight;
632                 }
633
634                 copy_m3_m3(customdata->vec, point->bezt.vec);
635                 if (BKE_mask_point_has_handle(point))
636                         BKE_mask_point_handle(point, customdata->handle);
637                 ED_mask_mouse_pos(C, event, customdata->co);
638         }
639
640         return customdata;
641 }
642
643 static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
644 {
645         SlidePointData *slidedata = slide_point_customdata(C, op, event);
646
647         if (slidedata) {
648                 Mask *mask = CTX_data_edit_mask(C);
649
650                 op->customdata = slidedata;
651
652                 WM_event_add_modal_handler(C, op);
653
654                 if (slidedata->uw) {
655                         if ((slidedata->uw->flag & SELECT) == 0) {
656                                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
657
658                                 slidedata->uw->flag |= SELECT;
659
660                                 ED_mask_select_flush_all(mask);
661                         }
662                 }
663                 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
664                         ED_mask_select_toggle_all(mask, SEL_DESELECT);
665
666                         BKE_mask_point_select_set(slidedata->point, TRUE);
667
668                         ED_mask_select_flush_all(mask);
669                 }
670
671                 slidedata->masklay->act_spline = slidedata->spline;
672                 slidedata->masklay->act_point = slidedata->point;
673
674                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
675
676                 return OPERATOR_RUNNING_MODAL;
677         }
678
679         return OPERATOR_PASS_THROUGH;
680 }
681
682 static void cancel_slide_point(SlidePointData *data)
683 {
684         /* cancel sliding */
685         if (data->action == SLIDE_ACTION_FEATHER) {
686                 if (data->uw)
687                         data->uw->w = data->weight;
688                 else
689                         data->point->bezt.weight = data->weight;
690         }
691         else {
692                 copy_m3_m3(data->point->bezt.vec, data->vec);
693         }
694 }
695
696 static void free_slide_point_data(SlidePointData *data)
697 {
698         MEM_freeN(data);
699 }
700
701 static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
702 {
703         SlidePointData *data = (SlidePointData *)op->customdata;
704         BezTriple *bezt = &data->point->bezt;
705         float co[2], dco[2];
706
707         switch (event->type) {
708                 case LEFTCTRLKEY:
709                 case RIGHTCTRLKEY:
710                 case LEFTSHIFTKEY:
711                 case RIGHTSHIFTKEY:
712                         if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY))
713                                 data->curvature_only = event->val == KM_PRESS;
714
715                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
716                                 data->accurate = event->val == KM_PRESS;
717
718                 /* no break! update CV position */
719
720                 case MOUSEMOVE:
721                         ED_mask_mouse_pos(C, event, co);
722                         sub_v2_v2v2(dco, co, data->co);
723
724                         if (data->action == SLIDE_ACTION_HANDLE) {
725                                 float delta[2], offco[2];
726
727                                 sub_v2_v2v2(delta, data->handle, data->co);
728
729                                 sub_v2_v2v2(offco, co, data->co);
730                                 if (data->accurate)
731                                         mul_v2_fl(offco, 0.2f);
732                                 add_v2_v2(offco, data->co);
733                                 add_v2_v2(offco, delta);
734
735                                 BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->aspx, data->aspy, data->handle, data->vec);
736                         }
737                         else if (data->action == SLIDE_ACTION_POINT) {
738                                 float delta[2];
739
740                                 copy_v2_v2(delta, dco);
741                                 if (data->accurate)
742                                         mul_v2_fl(delta, 0.2f);
743
744                                 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
745                                 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
746                                 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
747                         }
748                         else if (data->action == SLIDE_ACTION_FEATHER) {
749                                 float vec[2], no[2], p[2], c[2], w, offco[2];
750                                 float *weight;
751
752                                 add_v2_v2v2(offco, data->feather, dco);
753
754                                 if (data->uw) {
755                                         float u = projection_on_spline(data->spline, data->point, data->uw->u, offco);
756
757                                         if (u > 0.0f && u < 1.0f)
758                                                 data->uw->u = u;
759
760                                         data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
761                                         weight = &data->uw->w;
762                                         BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
763                                         BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
764                                 }
765                                 else {
766                                         weight = &bezt->weight;
767                                         copy_v2_v2(no, data->no);
768                                         copy_v2_v2(p, bezt->vec[1]);
769                                 }
770
771                                 sub_v2_v2v2(c, offco, p);
772                                 project_v2_v2v2(vec, c, no);
773
774                                 vec[0] *= data->aspx;
775                                 vec[1] *= data->aspy;
776
777                                 w = len_v2(vec);
778
779                                 if (dot_v2v2(no, vec) > 0.0f)
780                                         *weight = w;
781                                 else
782                                         *weight = 0;
783                         }
784
785                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
786                         DAG_id_tag_update(&data->mask->id, 0);
787
788                         break;
789
790                 case LEFTMOUSE:
791                         if (event->val == KM_RELEASE) {
792                                 Scene *scene = CTX_data_scene(C);
793
794                                 free_slide_point_data(op->customdata);
795
796                                 if (IS_AUTOKEY_ON(scene)) {
797                                         ED_mask_layer_shape_auto_key_all(data->mask, CFRA);
798                                 }
799
800                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
801                                 DAG_id_tag_update(&data->mask->id, 0);
802
803                                 return OPERATOR_FINISHED;
804                         }
805
806                         break;
807
808                 case ESCKEY:
809                         cancel_slide_point(op->customdata);
810
811                         free_slide_point_data(op->customdata);
812
813                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
814                         DAG_id_tag_update(&data->mask->id, 0);
815
816                         return OPERATOR_CANCELLED;
817         }
818
819         return OPERATOR_RUNNING_MODAL;
820 }
821
822 void MASK_OT_slide_point(wmOperatorType *ot)
823 {
824         /* identifiers */
825         ot->name = "Slide Point";
826         ot->description = "Slide control points";
827         ot->idname = "MASK_OT_slide_point";
828
829         /* api callbacks */
830         ot->invoke = slide_point_invoke;
831         ot->modal = slide_point_modal;
832         ot->poll = ED_maskediting_mask_poll;
833
834         /* flags */
835         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
836
837         RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide slide feather instead of vertex");
838 }
839
840 /******************** add vertex *********************/
841
842 static void setup_vertex_point(bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point,
843                                const float point_co[2], const float tangent[2],
844                                MaskSplinePoint *reference_point, const short reference_adjacent)
845 {
846         MaskSplinePoint *prev_point = NULL;
847         MaskSplinePoint *next_point = NULL;
848         BezTriple *bezt;
849         int width, height;
850         float co[3];
851         const float len = 20.0; /* default length of handle in pixel space */
852
853         copy_v2_v2(co, point_co);
854         co[2] = 0.0f;
855
856         ED_mask_size(C, &width, &height);
857
858         /* point coordinate */
859         bezt = &new_point->bezt;
860
861         bezt->h1 = bezt->h2 = HD_ALIGN;
862
863         if (reference_point) {
864                 bezt->h1 = bezt->h2 = MAX2(reference_point->bezt.h2, reference_point->bezt.h1);
865         }
866         else if (reference_adjacent) {
867                 if (spline->tot_point != 1) {
868                         int index = (int)(new_point - spline->points);
869                         prev_point = &spline->points[(index - 1) % spline->tot_point];
870                         next_point = &spline->points[(index + 1) % spline->tot_point];
871
872                         bezt->h1 = bezt->h2 = MAX2(prev_point->bezt.h2, next_point->bezt.h1);
873
874                         /* note, we may want to copy other attributes later, radius? pressure? color? */
875                 }
876         }
877
878         copy_v3_v3(bezt->vec[0], co);
879         copy_v3_v3(bezt->vec[1], co);
880         copy_v3_v3(bezt->vec[2], co);
881
882         /* initial offset for handles */
883         if (spline->tot_point == 1) {
884                 /* first point of splien is aligned horizontally */
885                 bezt->vec[0][0] -= len / width;
886                 bezt->vec[2][0] += len / width;
887         }
888         else if (tangent) {
889                 float vec[2];
890
891                 copy_v2_v2(vec, tangent);
892
893                 vec[0] *= width;
894                 vec[1] *= height;
895
896                 mul_v2_fl(vec, len / len_v2(vec));
897
898                 vec[0] /= width;
899                 vec[1] /= height;
900
901                 sub_v2_v2(bezt->vec[0], vec);
902                 add_v2_v2(bezt->vec[2], vec);
903
904                 if (reference_adjacent) {
905                         BKE_mask_calc_handle_adjacent_length(mask, spline, new_point);
906                 }
907         }
908         else {
909
910                 /* calculating auto handles works much nicer */
911 #if 0
912                 /* next points are aligning in the direction of previous/next point */
913                 MaskSplinePoint *point;
914                 float v1[2], v2[2], vec[2];
915                 float dir = 1.0f;
916
917                 if (new_point == spline->points) {
918                         point = new_point + 1;
919                         dir = -1.0f;
920                 }
921                 else
922                         point = new_point - 1;
923
924                 if (spline->tot_point < 3) {
925                         v1[0] = point->bezt.vec[1][0] * width;
926                         v1[1] = point->bezt.vec[1][1] * height;
927
928                         v2[0] = new_point->bezt.vec[1][0] * width;
929                         v2[1] = new_point->bezt.vec[1][1] * height;
930                 }
931                 else {
932                         if (new_point == spline->points) {
933                                 v1[0] = spline->points[1].bezt.vec[1][0] * width;
934                                 v1[1] = spline->points[1].bezt.vec[1][1] * height;
935
936                                 v2[0] = spline->points[spline->tot_point - 1].bezt.vec[1][0] * width;
937                                 v2[1] = spline->points[spline->tot_point - 1].bezt.vec[1][1] * height;
938                         }
939                         else {
940                                 v1[0] = spline->points[0].bezt.vec[1][0] * width;
941                                 v1[1] = spline->points[0].bezt.vec[1][1] * height;
942
943                                 v2[0] = spline->points[spline->tot_point - 2].bezt.vec[1][0] * width;
944                                 v2[1] = spline->points[spline->tot_point - 2].bezt.vec[1][1] * height;
945                         }
946                 }
947
948                 sub_v2_v2v2(vec, v1, v2);
949                 mul_v2_fl(vec, len * dir / len_v2(vec));
950
951                 vec[0] /= width;
952                 vec[1] /= height;
953
954                 add_v2_v2(bezt->vec[0], vec);
955                 sub_v2_v2(bezt->vec[2], vec);
956 #else
957                 BKE_mask_calc_handle_point_auto(mask, spline, new_point, TRUE);
958                 BKE_mask_calc_handle_adjacent_length(mask, spline, new_point);
959
960 #endif
961         }
962
963         BKE_mask_parent_init(&new_point->parent);
964
965         /* select new point */
966         MASKPOINT_SEL_ALL(new_point);
967         ED_mask_select_flush_all(mask);
968 }
969
970 /* **** add subdivide vertex **** */
971
972 static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index)
973 {
974         MaskSplinePoint *new_point_array;
975
976         new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1), "add mask vert points");
977
978         memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * (point_index + 1));
979         memcpy(new_point_array + point_index + 2, spline->points + point_index + 1,
980                sizeof(MaskSplinePoint) * (spline->tot_point - point_index - 1));
981
982         MEM_freeN(spline->points);
983         spline->points = new_point_array;
984         spline->tot_point++;
985 }
986
987 static int add_vertex_subdivide(bContext *C, Mask *mask, const float co[2])
988 {
989         MaskLayer *masklay;
990         MaskSpline *spline;
991         MaskSplinePoint *point = NULL;
992         const float threshold = 9;
993         float tangent[2];
994
995         if (find_nearest_diff_point(C, mask, co, threshold, FALSE, &masklay, &spline, &point, NULL, tangent)) {
996                 MaskSplinePoint *new_point;
997                 int point_index = point - spline->points;
998
999                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
1000
1001                 mask_spline_add_point_at_index(spline, point_index);
1002
1003                 new_point = &spline->points[point_index + 1];
1004
1005                 setup_vertex_point(C, mask, spline, new_point, co, tangent, NULL, TRUE);
1006
1007                 /* TODO - we could pass the spline! */
1008                 BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index + 1, TRUE, TRUE);
1009
1010                 masklay->act_point = new_point;
1011
1012                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1013
1014                 return TRUE;
1015         }
1016
1017         return FALSE;
1018 }
1019
1020 /* **** add extrude vertex **** */
1021
1022 static void finSelectedSplinePoint(MaskLayer *masklay, MaskSpline **spline, MaskSplinePoint **point, short check_active)
1023 {
1024         MaskSpline *cur_spline = masklay->splines.first;
1025
1026         *spline = NULL;
1027         *point = NULL;
1028
1029         if (check_active) {
1030                 if (masklay->act_spline && masklay->act_point) {
1031                         *spline = masklay->act_spline;
1032                         *point = masklay->act_point;
1033                         return;
1034                 }
1035         }
1036
1037         while (cur_spline) {
1038                 int i;
1039
1040                 for (i = 0; i < cur_spline->tot_point; i++) {
1041                         MaskSplinePoint *cur_point = &cur_spline->points[i];
1042
1043                         if (MASKPOINT_ISSEL_ANY(cur_point)) {
1044                                 if (*spline != NULL && *spline != cur_spline) {
1045                                         *spline = NULL;
1046                                         *point = NULL;
1047                                         return;
1048                                 }
1049                                 else if (*point) {
1050                                         *point = NULL;
1051                                 }
1052                                 else {
1053                                         *spline = cur_spline;
1054                                         *point = cur_point;
1055                                 }
1056                         }
1057                 }
1058
1059                 cur_spline = cur_spline->next;
1060         }
1061 }
1062
1063 static int add_vertex_extrude(bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
1064 {
1065         MaskSpline *spline;
1066         MaskSplinePoint *point;
1067         MaskSplinePoint *new_point = NULL, *ref_point = NULL;
1068
1069         /* check on which side we want to add the point */
1070         int point_index;
1071         float tangent_point[2];
1072         float tangent_co[2];
1073         int do_cyclic_correct = FALSE;
1074         int do_recalc_src = FALSE;  /* when extruding from endpoints only */
1075         int do_prev;                /* use prev point rather then next?? */
1076
1077         ED_mask_select_toggle_all(mask, SEL_DESELECT);
1078
1079         if (!masklay) {
1080                 return FALSE;
1081         }
1082         else {
1083                 finSelectedSplinePoint(masklay, &spline, &point, TRUE);
1084         }
1085
1086         point_index = (point - spline->points);
1087
1088         MASKPOINT_DESEL_ALL(point);
1089
1090         if ((spline->flag & MASK_SPLINE_CYCLIC) ||
1091             (point_index > 0 && point_index != spline->tot_point - 1))
1092         {
1093                 BKE_mask_calc_tangent_polyline(mask, spline, point, tangent_point);
1094                 sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]);
1095
1096                 if (dot_v2v2(tangent_point, tangent_co) < 0.0f) {
1097                         do_prev = TRUE;
1098                 }
1099                 else {
1100                         do_prev = FALSE;
1101                 }
1102         }
1103         else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) {
1104                 do_prev = TRUE;
1105                 do_recalc_src = TRUE;
1106         }
1107         else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) {
1108                 do_prev = FALSE;
1109                 do_recalc_src = TRUE;
1110         }
1111         else {
1112                 /* should never get here */
1113                 BLI_assert(0);
1114         }
1115
1116         /* use the point before the active one */
1117         if (do_prev) {
1118                 point_index--;
1119                 if (point_index < 0) {
1120                         point_index += spline->tot_point; /* wrap index */
1121                         if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) {
1122                                 do_cyclic_correct = TRUE;
1123                                 point_index = 0;
1124                         }
1125                 }
1126         }
1127
1128 //              print_v2("", tangent_point);
1129 //              printf("%d\n", point_index);
1130
1131         mask_spline_add_point_at_index(spline, point_index);
1132
1133         if (do_cyclic_correct) {
1134                 ref_point = &spline->points[point_index + 1];
1135                 new_point = &spline->points[point_index];
1136                 *ref_point = *new_point;
1137                 memset(new_point, 0, sizeof(*new_point));
1138         }
1139         else {
1140                 ref_point = &spline->points[point_index];
1141                 new_point = &spline->points[point_index + 1];
1142         }
1143
1144         masklay->act_point = new_point;
1145
1146         setup_vertex_point(C, mask, spline, new_point, co, NULL, ref_point, FALSE);
1147
1148         if (masklay->splines_shapes.first) {
1149                 point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
1150                 BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE);
1151         }
1152
1153         if (do_recalc_src) {
1154                 /* TODO, update keyframes in time */
1155                 BKE_mask_calc_handle_point_auto(mask, spline, ref_point, FALSE);
1156         }
1157
1158         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1159
1160         return TRUE;
1161 }
1162
1163 static int add_vertex_new(bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
1164 {
1165         MaskSpline *spline;
1166         MaskSplinePoint *point;
1167         MaskSplinePoint *new_point = NULL, *ref_point = NULL;
1168
1169         ED_mask_select_toggle_all(mask, SEL_DESELECT);
1170
1171         if (!masklay) {
1172                 /* if there's no masklay currently operationg on, create new one */
1173                 masklay = BKE_mask_layer_new(mask, "");
1174                 mask->masklay_act = mask->masklay_tot - 1;
1175                 spline = NULL;
1176                 point = NULL;
1177         }
1178         else {
1179                 finSelectedSplinePoint(masklay, &spline, &point, TRUE);
1180         }
1181
1182         if (!spline) {
1183                 /* no selected splines in active masklay, create new spline */
1184                 spline = BKE_mask_spline_add(masklay);
1185         }
1186
1187         masklay->act_spline = spline;
1188         new_point = spline->points;
1189
1190         masklay->act_point = new_point;
1191
1192         setup_vertex_point(C, mask, spline, new_point, co, NULL, ref_point, FALSE);
1193
1194         {
1195                 int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
1196                 BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE);
1197         }
1198
1199         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1200
1201         return TRUE;
1202 }
1203
1204 static int add_vertex_exec(bContext *C, wmOperator *op)
1205 {
1206         Mask *mask = CTX_data_edit_mask(C);
1207         MaskLayer *masklay;
1208
1209         float co[2];
1210
1211         masklay = BKE_mask_layer_active(mask);
1212
1213         if (masklay && masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1214                 masklay = NULL;
1215         }
1216
1217         RNA_float_get_array(op->ptr, "location", co);
1218
1219         if (masklay && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) {
1220
1221                 /* cheap trick - double click for cyclic */
1222                 MaskSpline *spline = masklay->act_spline;
1223                 MaskSplinePoint *point = masklay->act_point;
1224
1225                 int is_sta = (point == spline->points);
1226                 int is_end = (point == &spline->points[spline->tot_point - 1]);
1227
1228                 /* then check are we overlapping the mouse */
1229                 if ((is_sta || is_end) && equals_v2v2(co, point->bezt.vec[1])) {
1230                         if (spline->flag & MASK_SPLINE_CYCLIC) {
1231                                 /* nothing to do */
1232                                 return OPERATOR_CANCELLED;
1233                         }
1234                         else {
1235                                 /* recalc the connecting point as well to make a nice even curve */
1236                                 MaskSplinePoint *point_other = is_end ? spline->points : &spline->points[spline->tot_point - 1];
1237                                 spline->flag |= MASK_SPLINE_CYCLIC;
1238
1239                                 /* TODO, update keyframes in time */
1240                                 BKE_mask_calc_handle_point_auto(mask, spline, point, FALSE);
1241                                 BKE_mask_calc_handle_point_auto(mask, spline, point_other, FALSE);
1242
1243                                 /* TODO: only update this spline */
1244                                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1245
1246                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1247                                 return OPERATOR_FINISHED;
1248                         }
1249                 }
1250
1251                 if (!add_vertex_subdivide(C, mask, co)) {
1252                         if (!add_vertex_extrude(C, mask, masklay, co)) {
1253                                 return OPERATOR_CANCELLED;
1254                         }
1255                 }
1256         }
1257         else {
1258                 if (!add_vertex_subdivide(C, mask, co)) {
1259                         if (!add_vertex_new(C, mask, masklay, co)) {
1260                                 return OPERATOR_CANCELLED;
1261                         }
1262                 }
1263         }
1264
1265         /* TODO: only update this spline */
1266         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1267
1268         return OPERATOR_FINISHED;
1269 }
1270
1271 static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
1272 {
1273         float co[2];
1274
1275         ED_mask_mouse_pos(C, event, co);
1276
1277         RNA_float_set_array(op->ptr, "location", co);
1278
1279         return add_vertex_exec(C, op);
1280 }
1281
1282 void MASK_OT_add_vertex(wmOperatorType *ot)
1283 {
1284         /* identifiers */
1285         ot->name = "Add Vertex";
1286         ot->description = "Add vertex to active spline";
1287         ot->idname = "MASK_OT_add_vertex";
1288
1289         /* api callbacks */
1290         ot->exec = add_vertex_exec;
1291         ot->invoke = add_vertex_invoke;
1292         ot->poll = ED_maskediting_mask_poll;
1293
1294         /* flags */
1295         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1296
1297         /* properties */
1298         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
1299                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
1300 }
1301
1302 /******************** add feather vertex *********************/
1303
1304 static int add_feather_vertex_exec(bContext *C, wmOperator *op)
1305 {
1306         Mask *mask = CTX_data_edit_mask(C);
1307         MaskLayer *masklay;
1308         MaskSpline *spline;
1309         MaskSplinePoint *point = NULL;
1310         const float threshold = 9;
1311         float co[2], u;
1312
1313         RNA_float_get_array(op->ptr, "location", co);
1314
1315         point = ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL);
1316         if (point)
1317                 return OPERATOR_FINISHED;
1318
1319         if (find_nearest_diff_point(C, mask, co, threshold, TRUE, &masklay, &spline, &point, &u, NULL)) {
1320                 Scene *scene = CTX_data_scene(C);
1321                 float w = BKE_mask_point_weight(spline, point, u);
1322
1323                 BKE_mask_point_add_uw(point, u, w);
1324
1325                 BKE_mask_update_display(mask, scene->r.cfra);
1326
1327                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1328
1329                 DAG_id_tag_update(&mask->id, 0);
1330
1331                 return OPERATOR_FINISHED;
1332         }
1333
1334         return OPERATOR_CANCELLED;
1335 }
1336
1337 static int add_feather_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
1338 {
1339         float co[2];
1340
1341         ED_mask_mouse_pos(C, event, co);
1342
1343         RNA_float_set_array(op->ptr, "location", co);
1344
1345         return add_feather_vertex_exec(C, op);
1346 }
1347
1348 void MASK_OT_add_feather_vertex(wmOperatorType *ot)
1349 {
1350         /* identifiers */
1351         ot->name = "Add feather Vertex";
1352         ot->description = "Add vertex to feather";
1353         ot->idname = "MASK_OT_add_feather_vertex";
1354
1355         /* api callbacks */
1356         ot->exec = add_feather_vertex_exec;
1357         ot->invoke = add_feather_vertex_invoke;
1358         ot->poll = ED_maskediting_mask_poll;
1359
1360         /* flags */
1361         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1362
1363         /* properties */
1364         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
1365                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
1366 }
1367
1368 /******************** toggle cyclic *********************/
1369
1370 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1371 {
1372         Mask *mask = CTX_data_edit_mask(C);
1373         MaskLayer *masklay;
1374
1375         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1376                 MaskSpline *spline;
1377
1378                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1379                         continue;
1380                 }
1381
1382                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1383                         if (ED_mask_spline_select_check(spline->points, spline->tot_point)) {
1384                                 spline->flag ^= MASK_SPLINE_CYCLIC;
1385                         }
1386                 }
1387         }
1388
1389         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1390
1391         return OPERATOR_FINISHED;
1392 }
1393
1394 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
1395 {
1396         /* identifiers */
1397         ot->name = "Toggle Cyclic";
1398         ot->description = "Toggle cyclic for selected splines";
1399         ot->idname = "MASK_OT_cyclic_toggle";
1400
1401         /* api callbacks */
1402         ot->exec = cyclic_toggle_exec;
1403         ot->poll = ED_maskediting_mask_poll;
1404
1405         /* flags */
1406         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1407 }
1408
1409 /******************** delete *********************/
1410
1411 static void delete_feather_points(MaskSplinePoint *point)
1412 {
1413         int i, count = 0;
1414
1415         if (!point->tot_uw)
1416                 return;
1417
1418         for (i = 0; i < point->tot_uw; i++) {
1419                 if ((point->uw[i].flag & SELECT) == 0)
1420                         count++;
1421         }
1422
1423         if (count == 0) {
1424                 MEM_freeN(point->uw);
1425                 point->uw = NULL;
1426                 point->tot_uw = 0;
1427         }
1428         else {
1429                 MaskSplinePointUW *new_uw;
1430                 int j = 0;
1431
1432                 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
1433
1434                 for (i = 0; i < point->tot_uw; i++) {
1435                         if ((point->uw[i].flag & SELECT) == 0) {
1436                                 new_uw[j++] = point->uw[i];
1437                         }
1438                 }
1439
1440                 MEM_freeN(point->uw);
1441
1442                 point->uw = new_uw;
1443                 point->tot_uw = count;
1444         }
1445 }
1446
1447 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
1448 {
1449         Mask *mask = CTX_data_edit_mask(C);
1450         MaskLayer *masklay;
1451         int mask_layer_shape_ofs = 0;
1452
1453         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1454                 MaskSpline *spline;
1455
1456                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1457                         continue;
1458                 }
1459
1460                 spline = masklay->splines.first;
1461
1462                 while (spline) {
1463                         const int tot_point_orig = spline->tot_point;
1464                         int i, count = 0;
1465                         MaskSpline *next_spline = spline->next;
1466
1467                         /* count unselected points */
1468                         for (i = 0; i < spline->tot_point; i++) {
1469                                 MaskSplinePoint *point = &spline->points[i];
1470
1471                                 if (!MASKPOINT_ISSEL_ANY(point))
1472                                         count++;
1473                         }
1474
1475                         if (count == 0) {
1476
1477                                 /* delete the whole spline */
1478                                 BLI_remlink(&masklay->splines, spline);
1479                                 BKE_mask_spline_free(spline);
1480
1481                                 if (spline == masklay->act_spline) {
1482                                         masklay->act_spline = NULL;
1483                                         masklay->act_point = NULL;
1484                                 }
1485
1486                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
1487                         }
1488                         else {
1489                                 MaskSplinePoint *new_points;
1490                                 int j;
1491
1492                                 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
1493
1494                                 for (i = 0, j = 0; i < tot_point_orig; i++) {
1495                                         MaskSplinePoint *point = &spline->points[i];
1496
1497                                         if (!MASKPOINT_ISSEL_ANY(point)) {
1498                                                 if (point == masklay->act_point)
1499                                                         masklay->act_point = &new_points[j];
1500
1501                                                 delete_feather_points(point);
1502
1503                                                 new_points[j] = *point;
1504                                                 j++;
1505                                         }
1506                                         else {
1507                                                 if (point == masklay->act_point)
1508                                                         masklay->act_point = NULL;
1509
1510                                                 BKE_mask_point_free(point);
1511                                                 spline->tot_point--;
1512
1513                                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
1514                                         }
1515                                 }
1516
1517                                 mask_layer_shape_ofs += spline->tot_point;
1518
1519                                 MEM_freeN(spline->points);
1520                                 spline->points = new_points;
1521
1522                                 ED_mask_select_flush_all(mask);
1523                         }
1524
1525                         spline = next_spline;
1526                 }
1527         }
1528
1529         /* TODO: only update edited splines */
1530         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1531
1532         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1533
1534         return OPERATOR_FINISHED;
1535 }
1536
1537 void MASK_OT_delete(wmOperatorType *ot)
1538 {
1539         /* identifiers */
1540         ot->name = "Delete";
1541         ot->description = "Delete selected control points or splines";
1542         ot->idname = "MASK_OT_delete";
1543
1544         /* api callbacks */
1545         ot->invoke = WM_operator_confirm;
1546         ot->exec = delete_exec;
1547         ot->poll = ED_maskediting_mask_poll;
1548
1549         /* flags */
1550         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1551 }
1552
1553 /******************** set handle type *********************/
1554
1555 static int set_handle_type_exec(bContext *C, wmOperator *op)
1556 {
1557         Mask *mask = CTX_data_edit_mask(C);
1558         MaskLayer *masklay;
1559         int handle_type = RNA_enum_get(op->ptr, "type");
1560
1561         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1562                 MaskSpline *spline;
1563                 int i;
1564
1565                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1566                         continue;
1567                 }
1568
1569                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1570                         for (i = 0; i < spline->tot_point; i++) {
1571                                 MaskSplinePoint *point = &spline->points[i];
1572
1573                                 if (MASKPOINT_ISSEL_ANY(point)) {
1574                                         BezTriple *bezt = &point->bezt;
1575
1576                                         bezt->h1 = bezt->h2 = handle_type;
1577                                 }
1578                         }
1579                 }
1580         }
1581
1582         WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1583         DAG_id_tag_update(&mask->id, 0);
1584
1585         return OPERATOR_FINISHED;
1586 }
1587
1588 void MASK_OT_handle_type_set(wmOperatorType *ot)
1589 {
1590         static EnumPropertyItem editcurve_handle_type_items[] = {
1591                 {HD_AUTO, "AUTO", 0, "Auto", ""},
1592                 {HD_VECT, "VECTOR", 0, "Vector", ""},
1593                 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1594                 {0, NULL, 0, NULL, NULL}
1595         };
1596
1597         /* identifiers */
1598         ot->name = "Set Handle Type";
1599         ot->description = "Set type of handles for selected control points";
1600         ot->idname = "MASK_OT_handle_type_set";
1601
1602         /* api callbacks */
1603         ot->invoke = WM_menu_invoke;
1604         ot->exec = set_handle_type_exec;
1605         ot->poll = ED_maskediting_mask_poll;
1606
1607         /* flags */
1608         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1609
1610         /* properties */
1611         ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1612 }
1613
1614
1615 /* ********* clear/set restrict view *********/
1616 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1617 {
1618         Mask *mask = CTX_data_edit_mask(C);
1619         MaskLayer *masklay;
1620         int changed = FALSE;
1621
1622         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1623
1624                 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1625                         ED_mask_layer_select_set(masklay, TRUE);
1626                         masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1627                         changed = 1;
1628                 }
1629         }
1630
1631         if (changed) {
1632                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1633                 DAG_id_tag_update(&mask->id, 0);
1634
1635                 return OPERATOR_FINISHED;
1636         }
1637         else {
1638                 return OPERATOR_CANCELLED;
1639         }
1640 }
1641
1642 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1643 {
1644
1645         /* identifiers */
1646         ot->name = "Clear Restrict View";
1647         ot->description = "Reveal the layer by setting the hide flag";
1648         ot->idname = "MASK_OT_hide_view_clear";
1649
1650         /* api callbacks */
1651         ot->exec = mask_hide_view_clear_exec;
1652         ot->poll = ED_maskediting_mask_poll;
1653
1654         /* flags */
1655         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1656 }
1657
1658 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1659 {
1660         Mask *mask = CTX_data_edit_mask(C);
1661         MaskLayer *masklay;
1662         const int unselected = RNA_boolean_get(op->ptr, "unselected");
1663         int changed = FALSE;
1664
1665         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1666
1667                 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1668                         continue;
1669                 }
1670
1671                 if (!unselected) {
1672                         if (ED_mask_layer_select_check(masklay)) {
1673                                 ED_mask_layer_select_set(masklay, FALSE);
1674
1675                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1676                                 changed = 1;
1677                                 if (masklay == BKE_mask_layer_active(mask)) {
1678                                         BKE_mask_layer_active_set(mask, NULL);
1679                                 }
1680                         }
1681                 }
1682                 else {
1683                         if (!ED_mask_layer_select_check(masklay)) {
1684                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1685                                 changed = 1;
1686                                 if (masklay == BKE_mask_layer_active(mask)) {
1687                                         BKE_mask_layer_active_set(mask, NULL);
1688                                 }
1689                         }
1690                 }
1691         }
1692
1693         if (changed) {
1694                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1695                 DAG_id_tag_update(&mask->id, 0);
1696
1697                 return OPERATOR_FINISHED;
1698         }
1699         else {
1700                 return OPERATOR_CANCELLED;
1701         }
1702 }
1703
1704 void MASK_OT_hide_view_set(wmOperatorType *ot)
1705 {
1706         /* identifiers */
1707         ot->name = "Set Restrict View";
1708         ot->description = "Hide the layer by setting the hide flag";
1709         ot->idname = "MASK_OT_hide_view_set";
1710
1711         /* api callbacks */
1712         ot->exec = mask_hide_view_set_exec;
1713         ot->poll = ED_maskediting_mask_poll;
1714
1715         /* flags */
1716         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1717
1718         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1719
1720 }