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