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