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