Code cleanup: remove unused code
[blender.git] / source / blender / editors / mask / mask_add.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_add.c
29  *  \ingroup edmask
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_math.h"
35
36 #include "BKE_context.h"
37 #include "BKE_depsgraph.h"
38 #include "BKE_mask.h"
39
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_mask_types.h"
43 #include "DNA_object_types.h"  /* SELECT */
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "ED_mask.h"  /* own include */
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "mask_intern.h"  /* own include */
54
55
56 static int find_nearest_diff_point(const bContext *C, Mask *mask, const float normal_co[2], int threshold, int feather,
57                                    MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
58                                    float *u_r, float tangent[2],
59                                    const short use_deform)
60 {
61         ScrArea *sa = CTX_wm_area(C);
62         ARegion *ar = CTX_wm_region(C);
63
64         MaskLayer *masklay, *point_masklay;
65         MaskSpline *point_spline;
66         MaskSplinePoint *point = NULL;
67         float dist, co[2];
68         int width, height;
69         float u;
70         float scalex, scaley;
71
72         ED_mask_get_size(sa, &width, &height);
73         ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
74
75         co[0] = normal_co[0] * scalex;
76         co[1] = normal_co[1] * scaley;
77
78         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
79                 MaskSpline *spline;
80
81                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
82                         continue;
83                 }
84
85                 for (spline = masklay->splines.first; spline; spline = spline->next) {
86                         int i;
87                         MaskSplinePoint *cur_point;
88
89                         for (i = 0, cur_point = use_deform ? spline->points_deform : spline->points;
90                              i < spline->tot_point;
91                              i++, cur_point++)
92                         {
93                                 float *diff_points;
94                                 int tot_diff_point;
95
96                                 diff_points = BKE_mask_point_segment_diff_with_resolution(spline, cur_point, width, height,
97                                                                                           &tot_diff_point);
98
99                                 if (diff_points) {
100                                         int i, tot_feather_point, tot_point;
101                                         float *feather_points = NULL, *points;
102
103                                         if (feather) {
104                                                 feather_points = BKE_mask_point_segment_feather_diff_with_resolution(spline, cur_point,
105                                                                                                                      width, height,
106                                                                                                                      &tot_feather_point);
107
108                                                 points = feather_points;
109                                                 tot_point = tot_feather_point;
110                                         }
111                                         else {
112                                                 points = diff_points;
113                                                 tot_point = tot_diff_point;
114                                         }
115
116                                         for (i = 0; i < tot_point - 1; i++) {
117                                                 float cur_dist, a[2], b[2];
118
119                                                 a[0] = points[2 * i] * scalex;
120                                                 a[1] = points[2 * i + 1] * scaley;
121
122                                                 b[0] = points[2 * i + 2] * scalex;
123                                                 b[1] = points[2 * i + 3] * scaley;
124
125                                                 cur_dist = dist_to_line_segment_v2(co, a, b);
126
127                                                 if (point == NULL || cur_dist < dist) {
128                                                         if (tangent)
129                                                                 sub_v2_v2v2(tangent, &diff_points[2 * i + 2], &diff_points[2 * i]);
130
131                                                         point_masklay = masklay;
132                                                         point_spline = spline;
133                                                         point = use_deform ? &spline->points[(cur_point - spline->points_deform)] : cur_point;
134                                                         dist = cur_dist;
135                                                         u = (float)i / tot_point;
136
137                                                 }
138                                         }
139
140                                         if (feather_points)
141                                                 MEM_freeN(feather_points);
142
143                                         MEM_freeN(diff_points);
144                                 }
145                         }
146                 }
147         }
148
149         if (point && dist < threshold) {
150                 if (masklay_r)
151                         *masklay_r = point_masklay;
152
153                 if (spline_r)
154                         *spline_r = point_spline;
155
156                 if (point_r)
157                         *point_r = point;
158
159                 if (u_r) {
160                         u = BKE_mask_spline_project_co(point_spline, point, u, normal_co, MASK_PROJ_ANY);
161
162                         *u_r = u;
163                 }
164
165                 return TRUE;
166         }
167
168         if (masklay_r)
169                 *masklay_r = NULL;
170
171         if (spline_r)
172                 *spline_r = NULL;
173
174         if (point_r)
175                 *point_r = NULL;
176
177         return FALSE;
178 }
179
180 /******************** add vertex *********************/
181
182 static void setup_vertex_point(const bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point,
183                                const float point_co[2], const float tangent[2], const float u,
184                                MaskSplinePoint *reference_point, const short reference_adjacent)
185 {
186         ScrArea *sa = CTX_wm_area(C);
187
188         MaskSplinePoint *prev_point = NULL;
189         MaskSplinePoint *next_point = NULL;
190         BezTriple *bezt;
191         int width, height;
192         float co[3];
193         const float len = 20.0; /* default length of handle in pixel space */
194
195         copy_v2_v2(co, point_co);
196         co[2] = 0.0f;
197
198         ED_mask_get_size(sa, &width, &height);
199
200         /* point coordinate */
201         bezt = &new_point->bezt;
202
203         bezt->h1 = bezt->h2 = HD_ALIGN;
204
205         if (reference_point) {
206                 bezt->h1 = bezt->h2 = MAX2(reference_point->bezt.h2, reference_point->bezt.h1);
207         }
208         else if (reference_adjacent) {
209                 if (spline->tot_point != 1) {
210                         int index = (int)(new_point - spline->points);
211                         prev_point = &spline->points[(index - 1) % spline->tot_point];
212                         next_point = &spline->points[(index + 1) % spline->tot_point];
213
214                         bezt->h1 = bezt->h2 = MAX2(prev_point->bezt.h2, next_point->bezt.h1);
215
216                         /* note, we may want to copy other attributes later, radius? pressure? color? */
217                 }
218         }
219
220         copy_v3_v3(bezt->vec[0], co);
221         copy_v3_v3(bezt->vec[1], co);
222         copy_v3_v3(bezt->vec[2], co);
223
224         /* initial offset for handles */
225         if (spline->tot_point == 1) {
226                 /* first point of splien is aligned horizontally */
227                 bezt->vec[0][0] -= len / width;
228                 bezt->vec[2][0] += len / width;
229         }
230         else if (tangent) {
231                 float vec[2];
232
233                 copy_v2_v2(vec, tangent);
234
235                 vec[0] *= width;
236                 vec[1] *= height;
237
238                 mul_v2_fl(vec, len / len_v2(vec));
239
240                 vec[0] /= width;
241                 vec[1] /= height;
242
243                 sub_v2_v2(bezt->vec[0], vec);
244                 add_v2_v2(bezt->vec[2], vec);
245
246                 if (reference_adjacent) {
247                         BKE_mask_calc_handle_adjacent_interp(spline, new_point, u);
248                 }
249         }
250         else {
251
252                 /* calculating auto handles works much nicer */
253 #if 0
254                 /* next points are aligning in the direction of previous/next point */
255                 MaskSplinePoint *point;
256                 float v1[2], v2[2], vec[2];
257                 float dir = 1.0f;
258
259                 if (new_point == spline->points) {
260                         point = new_point + 1;
261                         dir = -1.0f;
262                 }
263                 else
264                         point = new_point - 1;
265
266                 if (spline->tot_point < 3) {
267                         v1[0] = point->bezt.vec[1][0] * width;
268                         v1[1] = point->bezt.vec[1][1] * height;
269
270                         v2[0] = new_point->bezt.vec[1][0] * width;
271                         v2[1] = new_point->bezt.vec[1][1] * height;
272                 }
273                 else {
274                         if (new_point == spline->points) {
275                                 v1[0] = spline->points[1].bezt.vec[1][0] * width;
276                                 v1[1] = spline->points[1].bezt.vec[1][1] * height;
277
278                                 v2[0] = spline->points[spline->tot_point - 1].bezt.vec[1][0] * width;
279                                 v2[1] = spline->points[spline->tot_point - 1].bezt.vec[1][1] * height;
280                         }
281                         else {
282                                 v1[0] = spline->points[0].bezt.vec[1][0] * width;
283                                 v1[1] = spline->points[0].bezt.vec[1][1] * height;
284
285                                 v2[0] = spline->points[spline->tot_point - 2].bezt.vec[1][0] * width;
286                                 v2[1] = spline->points[spline->tot_point - 2].bezt.vec[1][1] * height;
287                         }
288                 }
289
290                 sub_v2_v2v2(vec, v1, v2);
291                 mul_v2_fl(vec, len * dir / len_v2(vec));
292
293                 vec[0] /= width;
294                 vec[1] /= height;
295
296                 add_v2_v2(bezt->vec[0], vec);
297                 sub_v2_v2(bezt->vec[2], vec);
298 #else
299                 BKE_mask_calc_handle_point_auto(spline, new_point, TRUE);
300                 BKE_mask_calc_handle_adjacent_interp(spline, new_point, u);
301
302 #endif
303         }
304
305         BKE_mask_parent_init(&new_point->parent);
306
307         /* select new point */
308         MASKPOINT_SEL_ALL(new_point);
309         ED_mask_select_flush_all(mask);
310 }
311
312
313 /* **** add extrude vertex **** */
314
315 static void finSelectedSplinePoint(MaskLayer *masklay, MaskSpline **spline, MaskSplinePoint **point, short check_active)
316 {
317         MaskSpline *cur_spline = masklay->splines.first;
318
319         *spline = NULL;
320         *point = NULL;
321
322         if (check_active) {
323                 if (masklay->act_spline && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) {
324                         *spline = masklay->act_spline;
325                         *point = masklay->act_point;
326                         return;
327                 }
328         }
329
330         while (cur_spline) {
331                 int i;
332
333                 for (i = 0; i < cur_spline->tot_point; i++) {
334                         MaskSplinePoint *cur_point = &cur_spline->points[i];
335
336                         if (MASKPOINT_ISSEL_ANY(cur_point)) {
337                                 if (*spline != NULL && *spline != cur_spline) {
338                                         *spline = NULL;
339                                         *point = NULL;
340                                         return;
341                                 }
342                                 else if (*point) {
343                                         *point = NULL;
344                                 }
345                                 else {
346                                         *spline = cur_spline;
347                                         *point = cur_point;
348                                 }
349                         }
350                 }
351
352                 cur_spline = cur_spline->next;
353         }
354 }
355
356 /* **** add subdivide vertex **** */
357
358 static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index)
359 {
360         MaskSplinePoint *new_point_array;
361
362         new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1), "add mask vert points");
363
364         memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * (point_index + 1));
365         memcpy(new_point_array + point_index + 2, spline->points + point_index + 1,
366                sizeof(MaskSplinePoint) * (spline->tot_point - point_index - 1));
367
368         MEM_freeN(spline->points);
369         spline->points = new_point_array;
370         spline->tot_point++;
371 }
372
373 static int add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2])
374 {
375         MaskLayer *masklay;
376         MaskSpline *spline;
377         MaskSplinePoint *point = NULL;
378         const float threshold = 9;
379         float tangent[2];
380         float u;
381
382         if (find_nearest_diff_point(C, mask, co, threshold, FALSE, &masklay, &spline, &point, &u, tangent, TRUE)) {
383                 MaskSplinePoint *new_point;
384                 int point_index = point - spline->points;
385
386                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
387
388                 mask_spline_add_point_at_index(spline, point_index);
389
390                 new_point = &spline->points[point_index + 1];
391
392                 setup_vertex_point(C, mask, spline, new_point, co, tangent, u, NULL, TRUE);
393
394                 /* TODO - we could pass the spline! */
395                 BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index + 1, TRUE, TRUE);
396
397                 masklay->act_point = new_point;
398
399                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
400
401                 return TRUE;
402         }
403
404         return FALSE;
405 }
406
407 static int add_vertex_extrude(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
408 {
409         MaskSpline *spline;
410         MaskSplinePoint *point;
411         MaskSplinePoint *new_point = NULL, *ref_point = NULL;
412
413         /* check on which side we want to add the point */
414         int point_index;
415         float tangent_point[2];
416         float tangent_co[2];
417         int do_cyclic_correct = FALSE;
418         int do_recalc_src = FALSE;  /* when extruding from endpoints only */
419         int do_prev;                /* use prev point rather then next?? */
420
421         if (!masklay) {
422                 return FALSE;
423         }
424         else {
425                 finSelectedSplinePoint(masklay, &spline, &point, TRUE);
426         }
427
428         ED_mask_select_toggle_all(mask, SEL_DESELECT);
429
430         point_index = (point - spline->points);
431
432         MASKPOINT_DESEL_ALL(point);
433
434         if ((spline->flag & MASK_SPLINE_CYCLIC) ||
435             (point_index > 0 && point_index != spline->tot_point - 1))
436         {
437                 BKE_mask_calc_tangent_polyline(spline, point, tangent_point);
438                 sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]);
439
440                 if (dot_v2v2(tangent_point, tangent_co) < 0.0f) {
441                         do_prev = TRUE;
442                 }
443                 else {
444                         do_prev = FALSE;
445                 }
446         }
447         else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) {
448                 do_prev = TRUE;
449                 do_recalc_src = TRUE;
450         }
451         else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) {
452                 do_prev = FALSE;
453                 do_recalc_src = TRUE;
454         }
455         else {
456                 do_prev = FALSE;  /* quiet warning */
457                 /* should never get here */
458                 BLI_assert(0);
459         }
460
461         /* use the point before the active one */
462         if (do_prev) {
463                 point_index--;
464                 if (point_index < 0) {
465                         point_index += spline->tot_point; /* wrap index */
466                         if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) {
467                                 do_cyclic_correct = TRUE;
468                                 point_index = 0;
469                         }
470                 }
471         }
472
473 //              print_v2("", tangent_point);
474 //              printf("%d\n", point_index);
475
476         mask_spline_add_point_at_index(spline, point_index);
477
478         if (do_cyclic_correct) {
479                 ref_point = &spline->points[point_index + 1];
480                 new_point = &spline->points[point_index];
481                 *ref_point = *new_point;
482                 memset(new_point, 0, sizeof(*new_point));
483         }
484         else {
485                 ref_point = &spline->points[point_index];
486                 new_point = &spline->points[point_index + 1];
487         }
488
489         masklay->act_point = new_point;
490
491         setup_vertex_point(C, mask, spline, new_point, co, NULL, 0.5f, ref_point, FALSE);
492
493         if (masklay->splines_shapes.first) {
494                 point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
495                 BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE);
496         }
497
498         if (do_recalc_src) {
499                 /* TODO, update keyframes in time */
500                 BKE_mask_calc_handle_point_auto(spline, ref_point, FALSE);
501         }
502
503         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
504
505         return TRUE;
506 }
507
508 static int add_vertex_new(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
509 {
510         MaskSpline *spline;
511         MaskSplinePoint *point;
512         MaskSplinePoint *new_point = NULL, *ref_point = NULL;
513
514         if (!masklay) {
515                 /* if there's no masklay currently operationg on, create new one */
516                 masklay = BKE_mask_layer_new(mask, "");
517                 mask->masklay_act = mask->masklay_tot - 1;
518                 spline = NULL;
519                 point = NULL;
520         }
521         else {
522                 finSelectedSplinePoint(masklay, &spline, &point, TRUE);
523         }
524
525         ED_mask_select_toggle_all(mask, SEL_DESELECT);
526
527         if (!spline) {
528                 /* no selected splines in active masklay, create new spline */
529                 spline = BKE_mask_spline_add(masklay);
530         }
531
532         masklay->act_spline = spline;
533         new_point = spline->points;
534
535         masklay->act_point = new_point;
536
537         setup_vertex_point(C, mask, spline, new_point, co, NULL, 0.5f, ref_point, FALSE);
538
539         {
540                 int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
541                 BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE);
542         }
543
544         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
545
546         return TRUE;
547 }
548
549 static int add_vertex_exec(bContext *C, wmOperator *op)
550 {
551         Mask *mask = CTX_data_edit_mask(C);
552         MaskLayer *masklay;
553
554         float co[2];
555
556         masklay = BKE_mask_layer_active(mask);
557
558         if (masklay && masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
559                 masklay = NULL;
560         }
561
562         RNA_float_get_array(op->ptr, "location", co);
563
564         if (masklay && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) {
565
566                 /* cheap trick - double click for cyclic */
567                 MaskSpline *spline = masklay->act_spline;
568                 MaskSplinePoint *point = masklay->act_point;
569
570                 int is_sta = (point == spline->points);
571                 int is_end = (point == &spline->points[spline->tot_point - 1]);
572
573                 /* then check are we overlapping the mouse */
574                 if ((is_sta || is_end) && equals_v2v2(co, point->bezt.vec[1])) {
575                         if (spline->flag & MASK_SPLINE_CYCLIC) {
576                                 /* nothing to do */
577                                 return OPERATOR_CANCELLED;
578                         }
579                         else {
580                                 /* recalc the connecting point as well to make a nice even curve */
581                                 MaskSplinePoint *point_other = is_end ? spline->points : &spline->points[spline->tot_point - 1];
582                                 spline->flag |= MASK_SPLINE_CYCLIC;
583
584                                 /* TODO, update keyframes in time */
585                                 BKE_mask_calc_handle_point_auto(spline, point, FALSE);
586                                 BKE_mask_calc_handle_point_auto(spline, point_other, FALSE);
587
588                                 /* TODO: only update this spline */
589                                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
590
591                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
592                                 return OPERATOR_FINISHED;
593                         }
594                 }
595
596                 if (!add_vertex_subdivide(C, mask, co)) {
597                         if (!add_vertex_extrude(C, mask, masklay, co)) {
598                                 return OPERATOR_CANCELLED;
599                         }
600                 }
601         }
602         else {
603                 if (!add_vertex_subdivide(C, mask, co)) {
604                         if (!add_vertex_new(C, mask, masklay, co)) {
605                                 return OPERATOR_CANCELLED;
606                         }
607                 }
608         }
609
610         /* TODO: only update this spline */
611         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
612
613         return OPERATOR_FINISHED;
614 }
615
616 static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
617 {
618         ScrArea *sa = CTX_wm_area(C);
619         ARegion *ar = CTX_wm_region(C);
620
621         float co[2];
622
623         ED_mask_mouse_pos(sa, ar, event->mval, co);
624
625         RNA_float_set_array(op->ptr, "location", co);
626
627         return add_vertex_exec(C, op);
628 }
629
630 void MASK_OT_add_vertex(wmOperatorType *ot)
631 {
632         /* identifiers */
633         ot->name = "Add Vertex";
634         ot->description = "Add vertex to active spline";
635         ot->idname = "MASK_OT_add_vertex";
636
637         /* api callbacks */
638         ot->exec = add_vertex_exec;
639         ot->invoke = add_vertex_invoke;
640         ot->poll = ED_maskedit_mask_poll;
641
642         /* flags */
643         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
644
645         /* properties */
646         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
647                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
648 }
649
650 /******************** add feather vertex *********************/
651
652 static int add_feather_vertex_exec(bContext *C, wmOperator *op)
653 {
654         Mask *mask = CTX_data_edit_mask(C);
655         MaskLayer *masklay;
656         MaskSpline *spline;
657         MaskSplinePoint *point = NULL;
658         const float threshold = 9;
659         float co[2], u;
660
661         RNA_float_get_array(op->ptr, "location", co);
662
663         point = ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL);
664         if (point)
665                 return OPERATOR_FINISHED;
666
667         if (find_nearest_diff_point(C, mask, co, threshold, TRUE, &masklay, &spline, &point, &u, NULL, TRUE)) {
668                 Scene *scene = CTX_data_scene(C);
669                 float w = BKE_mask_point_weight(spline, point, u);
670                 float weight_scalar = BKE_mask_point_weight_scalar(spline, point, u);
671
672                 if (weight_scalar != 0.0f) {
673                         w = w / weight_scalar;
674                 }
675
676                 BKE_mask_point_add_uw(point, u, w);
677
678                 BKE_mask_update_display(mask, scene->r.cfra);
679
680                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
681
682                 DAG_id_tag_update(&mask->id, 0);
683
684                 return OPERATOR_FINISHED;
685         }
686
687         return OPERATOR_CANCELLED;
688 }
689
690 static int add_feather_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
691 {
692         ScrArea *sa = CTX_wm_area(C);
693         ARegion *ar = CTX_wm_region(C);
694
695         float co[2];
696
697         ED_mask_mouse_pos(sa, ar, event->mval, co);
698
699         RNA_float_set_array(op->ptr, "location", co);
700
701         return add_feather_vertex_exec(C, op);
702 }
703
704 void MASK_OT_add_feather_vertex(wmOperatorType *ot)
705 {
706         /* identifiers */
707         ot->name = "Add feather Vertex";
708         ot->description = "Add vertex to feather";
709         ot->idname = "MASK_OT_add_feather_vertex";
710
711         /* api callbacks */
712         ot->exec = add_feather_vertex_exec;
713         ot->invoke = add_feather_vertex_invoke;
714         ot->poll = ED_maskedit_mask_poll;
715
716         /* flags */
717         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
718
719         /* properties */
720         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
721                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
722 }