Merge branch 'blender-v2.81-release'
[blender.git] / source / blender / editors / mask / mask_add.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edmask
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_math.h"
27
28 #include "BKE_context.h"
29 #include "BKE_mask.h"
30
31 #include "DEG_depsgraph.h"
32 #include "DEG_depsgraph_query.h"
33
34 #include "DNA_scene_types.h"
35 #include "DNA_screen_types.h"
36 #include "DNA_mask_types.h"
37
38 #include "WM_api.h"
39 #include "WM_types.h"
40
41 #include "ED_select_utils.h"
42 #include "ED_mask.h" /* own include */
43 #include "ED_screen.h"
44
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47
48 #include "mask_intern.h" /* own include */
49
50 bool ED_mask_find_nearest_diff_point(const bContext *C,
51                                      struct Mask *mask_orig,
52                                      const float normal_co[2],
53                                      int threshold,
54                                      bool feather,
55                                      float tangent[2],
56                                      const bool use_deform,
57                                      const bool use_project,
58                                      MaskLayer **masklay_r,
59                                      MaskSpline **spline_r,
60                                      MaskSplinePoint **point_r,
61                                      float *u_r,
62                                      float *score_r)
63 {
64   ScrArea *sa = CTX_wm_area(C);
65   ARegion *ar = CTX_wm_region(C);
66
67   MaskLayer *point_masklay;
68   MaskSpline *point_spline;
69   MaskSplinePoint *point = NULL;
70   float dist_best_sq = FLT_MAX, co[2];
71   int width, height;
72   float u = 0.0f;
73   float scalex, scaley;
74
75   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
76   Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id);
77
78   ED_mask_get_size(sa, &width, &height);
79   ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
80
81   co[0] = normal_co[0] * scalex;
82   co[1] = normal_co[1] * scaley;
83
84   for (MaskLayer *masklay_orig = mask_orig->masklayers.first,
85                  *masklay_eval = mask_eval->masklayers.first;
86        masklay_orig != NULL;
87        masklay_orig = masklay_orig->next, masklay_eval = masklay_eval->next) {
88     if (masklay_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
89       continue;
90     }
91
92     for (MaskSpline *spline_orig = masklay_orig->splines.first,
93                     *spline_eval = masklay_eval->splines.first;
94          spline_orig != NULL;
95          spline_orig = spline_orig->next, spline_eval = spline_eval->next) {
96       int i;
97       MaskSplinePoint *cur_point_eval;
98
99       for (i = 0, cur_point_eval = use_deform ? spline_eval->points_deform : spline_eval->points;
100            i < spline_eval->tot_point;
101            i++, cur_point_eval++) {
102         unsigned int tot_diff_point;
103         float *diff_points = BKE_mask_point_segment_diff(
104             spline_eval, cur_point_eval, width, height, &tot_diff_point);
105
106         if (diff_points) {
107           int j, tot_point;
108           unsigned int tot_feather_point;
109           float *feather_points = NULL, *points;
110
111           if (feather) {
112             feather_points = BKE_mask_point_segment_feather_diff(
113                 spline_eval, cur_point_eval, width, height, &tot_feather_point);
114
115             points = feather_points;
116             tot_point = tot_feather_point;
117           }
118           else {
119             points = diff_points;
120             tot_point = tot_diff_point;
121           }
122
123           for (j = 0; j < tot_point - 1; j++) {
124             float dist_sq, a[2], b[2];
125
126             a[0] = points[2 * j] * scalex;
127             a[1] = points[2 * j + 1] * scaley;
128
129             b[0] = points[2 * j + 2] * scalex;
130             b[1] = points[2 * j + 3] * scaley;
131
132             dist_sq = dist_squared_to_line_segment_v2(co, a, b);
133
134             if (dist_sq < dist_best_sq) {
135               if (tangent) {
136                 sub_v2_v2v2(tangent, &diff_points[2 * j + 2], &diff_points[2 * j]);
137               }
138
139               point_masklay = masklay_orig;
140               point_spline = spline_orig;
141               point = use_deform ?
142                           &spline_orig->points[(cur_point_eval - spline_eval->points_deform)] :
143                           &spline_orig->points[(cur_point_eval - spline_eval->points)];
144               dist_best_sq = dist_sq;
145               u = (float)j / tot_point;
146             }
147           }
148
149           if (feather_points != NULL) {
150             MEM_freeN(feather_points);
151           }
152           MEM_freeN(diff_points);
153         }
154       }
155     }
156   }
157
158   if (point && dist_best_sq < threshold) {
159     if (masklay_r) {
160       *masklay_r = point_masklay;
161     }
162
163     if (spline_r) {
164       *spline_r = point_spline;
165     }
166
167     if (point_r) {
168       *point_r = point;
169     }
170
171     if (u_r) {
172       /* TODO(sergey): Projection fails in some weirdo cases.. */
173       if (use_project) {
174         u = BKE_mask_spline_project_co(point_spline, point, u, normal_co, MASK_PROJ_ANY);
175       }
176
177       *u_r = u;
178     }
179
180     if (score_r) {
181       *score_r = dist_best_sq;
182     }
183
184     return true;
185   }
186
187   if (masklay_r) {
188     *masklay_r = NULL;
189   }
190
191   if (spline_r) {
192     *spline_r = NULL;
193   }
194
195   if (point_r) {
196     *point_r = NULL;
197   }
198
199   return false;
200 }
201
202 /******************** add vertex *********************/
203
204 static void setup_vertex_point(Mask *mask,
205                                MaskSpline *spline,
206                                MaskSplinePoint *new_point,
207                                const float point_co[2],
208                                const float u,
209                                const float ctime,
210                                const MaskSplinePoint *reference_point,
211                                const bool reference_adjacent)
212 {
213   const MaskSplinePoint *reference_parent_point = NULL;
214   BezTriple *bezt;
215   float co[3];
216
217   copy_v2_v2(co, point_co);
218   co[2] = 0.0f;
219
220   /* point coordinate */
221   bezt = &new_point->bezt;
222
223   bezt->h1 = bezt->h2 = HD_ALIGN;
224
225   if (reference_point) {
226     if (reference_point->bezt.h1 == HD_VECT && reference_point->bezt.h2 == HD_VECT) {
227       /* If the reference point is sharp try using some smooth point as reference
228        * for handles.
229        */
230       int point_index = reference_point - spline->points;
231       int delta = new_point == spline->points ? 1 : -1;
232       int i = 0;
233       for (i = 0; i < spline->tot_point - 1; i++) {
234         MaskSplinePoint *current_point;
235
236         point_index += delta;
237         if (point_index == -1 || point_index >= spline->tot_point) {
238           if (spline->flag & MASK_SPLINE_CYCLIC) {
239             if (point_index == -1) {
240               point_index = spline->tot_point - 1;
241             }
242             else if (point_index >= spline->tot_point) {
243               point_index = 0;
244             }
245           }
246           else {
247             break;
248           }
249         }
250
251         current_point = &spline->points[point_index];
252         if (current_point->bezt.h1 != HD_VECT || current_point->bezt.h2 != HD_VECT) {
253           bezt->h1 = bezt->h2 = MAX2(current_point->bezt.h2, current_point->bezt.h1);
254           break;
255         }
256       }
257     }
258     else {
259       bezt->h1 = bezt->h2 = MAX2(reference_point->bezt.h2, reference_point->bezt.h1);
260     }
261
262     reference_parent_point = reference_point;
263   }
264   else if (reference_adjacent) {
265     if (spline->tot_point != 1) {
266       MaskSplinePoint *prev_point, *next_point, *close_point;
267
268       const int index = (int)(new_point - spline->points);
269       if (spline->flag & MASK_SPLINE_CYCLIC) {
270         prev_point = &spline->points[mod_i(index - 1, spline->tot_point)];
271         next_point = &spline->points[mod_i(index + 1, spline->tot_point)];
272       }
273       else {
274         prev_point = (index != 0) ? &spline->points[index - 1] : NULL;
275         next_point = (index != spline->tot_point - 1) ? &spline->points[index + 1] : NULL;
276       }
277
278       if (prev_point && next_point) {
279         close_point = (len_squared_v2v2(new_point->bezt.vec[1], prev_point->bezt.vec[1]) <
280                        len_squared_v2v2(new_point->bezt.vec[1], next_point->bezt.vec[1])) ?
281                           prev_point :
282                           next_point;
283       }
284       else {
285         close_point = prev_point ? prev_point : next_point;
286       }
287
288       /* handle type */
289       char handle_type = 0;
290       if (prev_point) {
291         handle_type = prev_point->bezt.h2;
292       }
293       if (next_point) {
294         handle_type = MAX2(next_point->bezt.h2, handle_type);
295       }
296       bezt->h1 = bezt->h2 = handle_type;
297
298       /* parent */
299       reference_parent_point = close_point;
300
301       /* note, we may want to copy other attributes later, radius? pressure? color? */
302     }
303   }
304
305   copy_v3_v3(bezt->vec[0], co);
306   copy_v3_v3(bezt->vec[1], co);
307   copy_v3_v3(bezt->vec[2], co);
308
309   if (reference_parent_point) {
310     new_point->parent = reference_parent_point->parent;
311
312     if (new_point->parent.id) {
313       float parent_matrix[3][3];
314       BKE_mask_point_parent_matrix_get(new_point, ctime, parent_matrix);
315       invert_m3(parent_matrix);
316       mul_m3_v2(parent_matrix, new_point->bezt.vec[1]);
317     }
318   }
319   else {
320     BKE_mask_parent_init(&new_point->parent);
321   }
322
323   if (spline->tot_point != 1) {
324     BKE_mask_calc_handle_adjacent_interp(spline, new_point, u);
325   }
326
327   /* select new point */
328   MASKPOINT_SEL_ALL(new_point);
329   ED_mask_select_flush_all(mask);
330 }
331
332 /* **** add extrude vertex **** */
333
334 static void finSelectedSplinePoint(MaskLayer *masklay,
335                                    MaskSpline **spline,
336                                    MaskSplinePoint **point,
337                                    bool check_active)
338 {
339   MaskSpline *cur_spline = masklay->splines.first;
340
341   *spline = NULL;
342   *point = NULL;
343
344   if (check_active) {
345     /* TODO, having an active point but no active spline is possible, why? */
346     if (masklay->act_spline && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) {
347       *spline = masklay->act_spline;
348       *point = masklay->act_point;
349       return;
350     }
351   }
352
353   while (cur_spline) {
354     int i;
355
356     for (i = 0; i < cur_spline->tot_point; i++) {
357       MaskSplinePoint *cur_point = &cur_spline->points[i];
358
359       if (MASKPOINT_ISSEL_ANY(cur_point)) {
360         if (*spline != NULL && *spline != cur_spline) {
361           *spline = NULL;
362           *point = NULL;
363           return;
364         }
365         else if (*point) {
366           *point = NULL;
367         }
368         else {
369           *spline = cur_spline;
370           *point = cur_point;
371         }
372       }
373     }
374
375     cur_spline = cur_spline->next;
376   }
377 }
378
379 /* **** add subdivide vertex **** */
380
381 static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index)
382 {
383   MaskSplinePoint *new_point_array;
384
385   new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1),
386                                 "add mask vert points");
387
388   memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * (point_index + 1));
389   memcpy(new_point_array + point_index + 2,
390          spline->points + point_index + 1,
391          sizeof(MaskSplinePoint) * (spline->tot_point - point_index - 1));
392
393   MEM_freeN(spline->points);
394   spline->points = new_point_array;
395   spline->tot_point++;
396 }
397
398 static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2])
399 {
400   MaskLayer *masklay;
401   MaskSpline *spline;
402   MaskSplinePoint *point = NULL;
403   const float threshold = 9;
404   float tangent[2];
405   float u;
406
407   if (ED_mask_find_nearest_diff_point(C,
408                                       mask,
409                                       co,
410                                       threshold,
411                                       false,
412                                       tangent,
413                                       true,
414                                       true,
415                                       &masklay,
416                                       &spline,
417                                       &point,
418                                       &u,
419                                       NULL)) {
420     Scene *scene = CTX_data_scene(C);
421     const float ctime = CFRA;
422
423     MaskSplinePoint *new_point;
424     int point_index = point - spline->points;
425
426     ED_mask_select_toggle_all(mask, SEL_DESELECT);
427
428     mask_spline_add_point_at_index(spline, point_index);
429
430     new_point = &spline->points[point_index + 1];
431
432     setup_vertex_point(mask, spline, new_point, co, u, ctime, NULL, true);
433
434     /* TODO - we could pass the spline! */
435     BKE_mask_layer_shape_changed_add(masklay,
436                                      BKE_mask_layer_shape_spline_to_index(masklay, spline) +
437                                          point_index + 1,
438                                      true,
439                                      true);
440
441     masklay->act_spline = spline;
442     masklay->act_point = new_point;
443
444     WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
445
446     return true;
447   }
448
449   return false;
450 }
451
452 static bool add_vertex_extrude(const bContext *C,
453                                Mask *mask,
454                                MaskLayer *masklay,
455                                const float co[2])
456 {
457   Scene *scene = CTX_data_scene(C);
458   const float ctime = CFRA;
459
460   MaskSpline *spline;
461   MaskSplinePoint *point;
462   MaskSplinePoint *new_point = NULL, *ref_point = NULL;
463
464   /* check on which side we want to add the point */
465   int point_index;
466   float tangent_point[2];
467   float tangent_co[2];
468   bool do_cyclic_correct = false;
469   bool do_prev; /* use prev point rather then next?? */
470
471   if (!masklay) {
472     return false;
473   }
474   else {
475     finSelectedSplinePoint(masklay, &spline, &point, true);
476   }
477
478   ED_mask_select_toggle_all(mask, SEL_DESELECT);
479
480   point_index = (point - spline->points);
481
482   MASKPOINT_DESEL_ALL(point);
483
484   if ((spline->flag & MASK_SPLINE_CYCLIC) ||
485       (point_index > 0 && point_index != spline->tot_point - 1)) {
486     BKE_mask_calc_tangent_polyline(spline, point, tangent_point);
487     sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]);
488
489     if (dot_v2v2(tangent_point, tangent_co) < 0.0f) {
490       do_prev = true;
491     }
492     else {
493       do_prev = false;
494     }
495   }
496   else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) {
497     do_prev = true;
498   }
499   else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) {
500     do_prev = false;
501   }
502   else {
503     do_prev = false; /* quiet warning */
504     /* should never get here */
505     BLI_assert(0);
506   }
507
508   /* use the point before the active one */
509   if (do_prev) {
510     point_index--;
511     if (point_index < 0) {
512       point_index += spline->tot_point; /* wrap index */
513       if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) {
514         do_cyclic_correct = true;
515         point_index = 0;
516       }
517     }
518   }
519
520   //      print_v2("", tangent_point);
521   //      printf("%d\n", point_index);
522
523   mask_spline_add_point_at_index(spline, point_index);
524
525   if (do_cyclic_correct) {
526     ref_point = &spline->points[point_index + 1];
527     new_point = &spline->points[point_index];
528     *ref_point = *new_point;
529     memset(new_point, 0, sizeof(*new_point));
530   }
531   else {
532     ref_point = &spline->points[point_index];
533     new_point = &spline->points[point_index + 1];
534   }
535
536   masklay->act_point = new_point;
537
538   setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false);
539
540   if (masklay->splines_shapes.first) {
541     point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
542     BKE_mask_layer_shape_changed_add(
543         masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, true, true);
544   }
545
546   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
547
548   return true;
549 }
550
551 static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
552 {
553   Scene *scene = CTX_data_scene(C);
554   const float ctime = CFRA;
555
556   MaskSpline *spline;
557   MaskSplinePoint *new_point = NULL, *ref_point = NULL;
558
559   if (!masklay) {
560     /* if there's no masklay currently operationg on, create new one */
561     masklay = BKE_mask_layer_new(mask, "");
562     mask->masklay_act = mask->masklay_tot - 1;
563   }
564
565   ED_mask_select_toggle_all(mask, SEL_DESELECT);
566
567   spline = BKE_mask_spline_add(masklay);
568
569   masklay->act_spline = spline;
570   new_point = spline->points;
571
572   masklay->act_point = new_point;
573
574   setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false);
575
576   {
577     int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
578     BKE_mask_layer_shape_changed_add(
579         masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, true, true);
580   }
581
582   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
583
584   return true;
585 }
586
587 static int add_vertex_exec(bContext *C, wmOperator *op)
588 {
589   Mask *mask = CTX_data_edit_mask(C);
590   MaskLayer *masklay;
591
592   float co[2];
593
594   if (mask == NULL) {
595     /* if there's no active mask, create one */
596     mask = ED_mask_new(C, NULL);
597   }
598
599   masklay = BKE_mask_layer_active(mask);
600
601   if (masklay && masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
602     masklay = NULL;
603   }
604
605   RNA_float_get_array(op->ptr, "location", co);
606
607   /* TODO, having an active point but no active spline is possible, why? */
608   if (masklay && masklay->act_spline && masklay->act_point &&
609       MASKPOINT_ISSEL_ANY(masklay->act_point)) {
610
611     /* cheap trick - double click for cyclic */
612     MaskSpline *spline = masklay->act_spline;
613     MaskSplinePoint *point = masklay->act_point;
614
615     const bool is_sta = (point == spline->points);
616     const bool is_end = (point == &spline->points[spline->tot_point - 1]);
617
618     /* then check are we overlapping the mouse */
619     if ((is_sta || is_end) && equals_v2v2(co, point->bezt.vec[1])) {
620       if (spline->flag & MASK_SPLINE_CYCLIC) {
621         /* nothing to do */
622         return OPERATOR_CANCELLED;
623       }
624       else {
625         /* recalc the connecting point as well to make a nice even curve */
626         MaskSplinePoint *point_other = is_end ? spline->points :
627                                                 &spline->points[spline->tot_point - 1];
628         spline->flag |= MASK_SPLINE_CYCLIC;
629
630         /* TODO, update keyframes in time */
631         BKE_mask_calc_handle_point_auto(spline, point, false);
632         BKE_mask_calc_handle_point_auto(spline, point_other, false);
633
634         DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
635
636         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
637         return OPERATOR_FINISHED;
638       }
639     }
640
641     if (!add_vertex_subdivide(C, mask, co)) {
642       if (!add_vertex_extrude(C, mask, masklay, co)) {
643         return OPERATOR_CANCELLED;
644       }
645     }
646   }
647   else {
648     if (!add_vertex_subdivide(C, mask, co)) {
649       if (!add_vertex_new(C, mask, masklay, co)) {
650         return OPERATOR_CANCELLED;
651       }
652     }
653   }
654
655   DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
656
657   return OPERATOR_FINISHED;
658 }
659
660 static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
661 {
662   ScrArea *sa = CTX_wm_area(C);
663   ARegion *ar = CTX_wm_region(C);
664
665   float co[2];
666
667   ED_mask_mouse_pos(sa, ar, event->mval, co);
668
669   RNA_float_set_array(op->ptr, "location", co);
670
671   return add_vertex_exec(C, op);
672 }
673
674 void MASK_OT_add_vertex(wmOperatorType *ot)
675 {
676   /* identifiers */
677   ot->name = "Add Vertex";
678   ot->description = "Add vertex to active spline";
679   ot->idname = "MASK_OT_add_vertex";
680
681   /* api callbacks */
682   ot->exec = add_vertex_exec;
683   ot->invoke = add_vertex_invoke;
684   ot->poll = ED_operator_mask;
685
686   /* flags */
687   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
688
689   /* properties */
690   RNA_def_float_vector(ot->srna,
691                        "location",
692                        2,
693                        NULL,
694                        -FLT_MAX,
695                        FLT_MAX,
696                        "Location",
697                        "Location of vertex in normalized space",
698                        -1.0f,
699                        1.0f);
700 }
701
702 /******************** add feather vertex *********************/
703
704 static int add_feather_vertex_exec(bContext *C, wmOperator *op)
705 {
706   Mask *mask = CTX_data_edit_mask(C);
707   MaskLayer *masklay;
708   MaskSpline *spline;
709   MaskSplinePoint *point = NULL;
710   const float threshold = 9;
711   float co[2], u;
712
713   RNA_float_get_array(op->ptr, "location", co);
714
715   point = ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL);
716   if (point) {
717     return OPERATOR_FINISHED;
718   }
719
720   if (ED_mask_find_nearest_diff_point(
721           C, mask, co, threshold, true, NULL, true, true, &masklay, &spline, &point, &u, NULL)) {
722     float w = BKE_mask_point_weight(spline, point, u);
723     float weight_scalar = BKE_mask_point_weight_scalar(spline, point, u);
724
725     if (weight_scalar != 0.0f) {
726       w = w / weight_scalar;
727     }
728
729     BKE_mask_point_add_uw(point, u, w);
730
731     DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
732
733     WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
734
735     return OPERATOR_FINISHED;
736   }
737
738   return OPERATOR_CANCELLED;
739 }
740
741 static int add_feather_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
742 {
743   ScrArea *sa = CTX_wm_area(C);
744   ARegion *ar = CTX_wm_region(C);
745
746   float co[2];
747
748   ED_mask_mouse_pos(sa, ar, event->mval, co);
749
750   RNA_float_set_array(op->ptr, "location", co);
751
752   return add_feather_vertex_exec(C, op);
753 }
754
755 void MASK_OT_add_feather_vertex(wmOperatorType *ot)
756 {
757   /* identifiers */
758   ot->name = "Add Feather Vertex";
759   ot->description = "Add vertex to feather";
760   ot->idname = "MASK_OT_add_feather_vertex";
761
762   /* api callbacks */
763   ot->exec = add_feather_vertex_exec;
764   ot->invoke = add_feather_vertex_invoke;
765   ot->poll = ED_maskedit_mask_poll;
766
767   /* flags */
768   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
769
770   /* properties */
771   RNA_def_float_vector(ot->srna,
772                        "location",
773                        2,
774                        NULL,
775                        -FLT_MAX,
776                        FLT_MAX,
777                        "Location",
778                        "Location of vertex in normalized space",
779                        -1.0f,
780                        1.0f);
781 }
782
783 /******************** common primitive functions *********************/
784
785 static int create_primitive_from_points(
786     bContext *C, wmOperator *op, const float (*points)[2], int num_points, char handle_type)
787 {
788   ScrArea *sa = CTX_wm_area(C);
789   Mask *mask;
790   MaskLayer *mask_layer;
791   MaskSpline *new_spline;
792   float scale, location[2], frame_size[2];
793   int i, width, height;
794   int size = RNA_float_get(op->ptr, "size");
795
796   ED_mask_get_size(sa, &width, &height);
797   scale = (float)size / max_ii(width, height);
798
799   /* Get location in mask space. */
800   frame_size[0] = width;
801   frame_size[1] = height;
802   RNA_float_get_array(op->ptr, "location", location);
803   location[0] /= width;
804   location[1] /= height;
805   BKE_mask_coord_from_frame(location, location, frame_size);
806
807   /* Make it so new primitive is centered to mouse location. */
808   location[0] -= 0.5f * scale;
809   location[1] -= 0.5f * scale;
810
811   bool added_mask = false;
812   mask_layer = ED_mask_layer_ensure(C, &added_mask);
813   mask = CTX_data_edit_mask(C);
814
815   ED_mask_select_toggle_all(mask, SEL_DESELECT);
816
817   new_spline = BKE_mask_spline_add(mask_layer);
818   new_spline->flag = MASK_SPLINE_CYCLIC | SELECT;
819   new_spline->points = MEM_recallocN(new_spline->points, sizeof(MaskSplinePoint) * num_points);
820
821   mask_layer->act_spline = new_spline;
822   mask_layer->act_point = NULL;
823
824   const int spline_index = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
825
826   for (i = 0; i < num_points; i++) {
827     new_spline->tot_point = i + 1;
828
829     MaskSplinePoint *new_point = &new_spline->points[i];
830     BKE_mask_parent_init(&new_point->parent);
831
832     copy_v2_v2(new_point->bezt.vec[1], points[i]);
833     mul_v2_fl(new_point->bezt.vec[1], scale);
834     add_v2_v2(new_point->bezt.vec[1], location);
835
836     new_point->bezt.h1 = handle_type;
837     new_point->bezt.h2 = handle_type;
838     BKE_mask_point_select_set(new_point, true);
839
840     if (mask_layer->splines_shapes.first) {
841       BKE_mask_layer_shape_changed_add(mask_layer, spline_index + i, true, true);
842     }
843   }
844
845   if (added_mask) {
846     WM_event_add_notifier(C, NC_MASK | NA_ADDED, NULL);
847   }
848   WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
849
850   DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
851
852   return OPERATOR_FINISHED;
853 }
854
855 static int primitive_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
856 {
857   ScrArea *sa = CTX_wm_area(C);
858   float cursor[2];
859   int width, height;
860
861   ED_mask_get_size(sa, &width, &height);
862   ED_mask_cursor_location_get(sa, cursor);
863
864   cursor[0] *= width;
865   cursor[1] *= height;
866
867   RNA_float_set_array(op->ptr, "location", cursor);
868
869   return op->type->exec(C, op);
870 }
871
872 static void define_primitive_add_properties(wmOperatorType *ot)
873 {
874   RNA_def_float(
875       ot->srna, "size", 100, -FLT_MAX, FLT_MAX, "Size", "Size of new circle", -FLT_MAX, FLT_MAX);
876   RNA_def_float_vector(ot->srna,
877                        "location",
878                        2,
879                        NULL,
880                        -FLT_MAX,
881                        FLT_MAX,
882                        "Location",
883                        "Location of new circle",
884                        -FLT_MAX,
885                        FLT_MAX);
886 }
887
888 /******************** primitive add circle *********************/
889
890 static int primitive_circle_add_exec(bContext *C, wmOperator *op)
891 {
892   const float points[4][2] = {{0.0f, 0.5f}, {0.5f, 1.0f}, {1.0f, 0.5f}, {0.5f, 0.0f}};
893   int num_points = sizeof(points) / (2 * sizeof(float));
894
895   create_primitive_from_points(C, op, points, num_points, HD_AUTO);
896
897   return OPERATOR_FINISHED;
898 }
899
900 void MASK_OT_primitive_circle_add(wmOperatorType *ot)
901 {
902   /* identifiers */
903   ot->name = "Add Circle";
904   ot->description = "Add new circle-shaped spline";
905   ot->idname = "MASK_OT_primitive_circle_add";
906
907   /* api callbacks */
908   ot->exec = primitive_circle_add_exec;
909   ot->invoke = primitive_add_invoke;
910   ot->poll = ED_operator_mask;
911
912   /* flags */
913   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
914
915   /* properties */
916   define_primitive_add_properties(ot);
917 }
918
919 /******************** primitive add suqare *********************/
920
921 static int primitive_square_add_exec(bContext *C, wmOperator *op)
922 {
923   const float points[4][2] = {{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}};
924   int num_points = sizeof(points) / (2 * sizeof(float));
925
926   create_primitive_from_points(C, op, points, num_points, HD_VECT);
927
928   return OPERATOR_FINISHED;
929 }
930
931 void MASK_OT_primitive_square_add(wmOperatorType *ot)
932 {
933   /* identifiers */
934   ot->name = "Add Square";
935   ot->description = "Add new square-shaped spline";
936   ot->idname = "MASK_OT_primitive_square_add";
937
938   /* api callbacks */
939   ot->exec = primitive_square_add_exec;
940   ot->invoke = primitive_add_invoke;
941   ot->poll = ED_operator_mask;
942
943   /* flags */
944   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
945
946   /* properties */
947   define_primitive_add_properties(ot);
948 }