Renaming: sl > scene_layer
[blender.git] / source / blender / editors / curve / editcurve_paint.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/curve/editcurve_paint.c
22  *  \ingroup edcurve
23  */
24
25 #include "DNA_object_types.h"
26 #include "DNA_scene_types.h"
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_blenlib.h"
31 #include "BLI_math.h"
32 #include "BLI_mempool.h"
33
34 #include "BKE_context.h"
35 #include "BKE_curve.h"
36 #include "BKE_depsgraph.h"
37 #include "BKE_fcurve.h"
38 #include "BKE_report.h"
39
40 #include "WM_api.h"
41 #include "WM_types.h"
42
43 #include "ED_space_api.h"
44 #include "ED_screen.h"
45 #include "ED_view3d.h"
46 #include "ED_curve.h"
47
48 #include "BIF_gl.h"
49
50 #include "GPU_batch.h"
51 #include "GPU_immediate.h"
52 #include "GPU_immediate_util.h"
53 #include "GPU_matrix.h"
54
55 #include "curve_intern.h"
56
57 #include "UI_resources.h"
58
59 #include "RNA_access.h"
60 #include "RNA_define.h"
61
62 #include "RNA_enum_types.h"
63
64 #define USE_SPLINE_FIT
65
66 #ifdef USE_SPLINE_FIT
67 #include "curve_fit_nd.h"
68 #endif
69
70 /* Distance between input samples */
71 #define STROKE_SAMPLE_DIST_MIN_PX 1
72 #define STROKE_SAMPLE_DIST_MAX_PX 3
73
74 /* Distance between start/end points to consider cyclic */
75 #define STROKE_CYCLIC_DIST_PX     8
76
77
78 /* -------------------------------------------------------------------- */
79
80 /** \name Depth Utilities
81  * \{ */
82
83
84 static float depth_read_zbuf(const ViewContext *vc, int x, int y)
85 {
86         ViewDepths *vd = vc->rv3d->depths;
87
88         if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
89                 return vd->depths[y * vd->w + x];
90         else
91                 return -1.0f;
92 }
93
94 static bool depth_unproject(
95         const ARegion *ar,
96         const int mval[2], const double depth,
97         float r_location_world[3])
98 {
99         float centx = (float)mval[0] + 0.5f;
100         float centy = (float)mval[1] + 0.5f;
101         return ED_view3d_unproject(ar, centx, centy, depth, r_location_world);
102 }
103
104 static bool depth_read_normal(
105         const ViewContext *vc, const int mval[2],
106         float r_normal[3])
107 {
108         /* pixels surrounding */
109         bool  depths_valid[9] = {false};
110         float coords[9][3] = {{0}};
111
112         ARegion *ar = vc->ar;
113         const ViewDepths *depths = vc->rv3d->depths;
114
115         for (int x = 0, i = 0; x < 2; x++) {
116                 for (int y = 0; y < 2; y++) {
117                         const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)};
118
119                         const double depth = (double)depth_read_zbuf(vc, mval_ofs[0], mval_ofs[1]);
120                         if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
121                                 if (depth_unproject(ar, mval_ofs, depth, coords[i])) {
122                                         depths_valid[i] = true;
123                                 }
124                         }
125                         i++;
126                 }
127         }
128
129         const int edges[2][6][2] = {
130             /* x edges */
131             {{0, 1}, {1, 2},
132              {3, 4}, {4, 5},
133              {6, 7}, {7, 8}},
134             /* y edges */
135             {{0, 3}, {3, 6},
136              {1, 4}, {4, 7},
137              {2, 5}, {5, 8}},
138         };
139
140         float cross[2][3] = {{0.0f}};
141
142         for (int i = 0; i < 6; i++) {
143                 for (int axis = 0; axis < 2; axis++) {
144                         if (depths_valid[edges[axis][i][0]] && depths_valid[edges[axis][i][1]]) {
145                                 float delta[3];
146                                 sub_v3_v3v3(delta, coords[edges[axis][i][0]], coords[edges[axis][i][1]]);
147                                 add_v3_v3(cross[axis], delta);
148                         }
149                 }
150         }
151
152         cross_v3_v3v3(r_normal, cross[0], cross[1]);
153
154         if (normalize_v3(r_normal) != 0.0f) {
155                 return true;
156         }
157         else {
158                 return false;
159         }
160 }
161
162 /** \} */
163
164
165 /* -------------------------------------------------------------------- */
166
167 /** \name StrokeElem / #RNA_OperatorStrokeElement Conversion Functions
168  * \{ */
169
170 struct StrokeElem {
171         float mval[2];
172         float location_world[3];
173         float location_local[3];
174
175         /* surface normal, may be zero'd */
176         float normal_world[3];
177         float normal_local[3];
178
179         float pressure;
180 };
181
182 struct CurveDrawData {
183         short init_event_type;
184         short curve_type;
185
186         /* projecting 2D into 3D space */
187         struct {
188                 /* use a plane or project to the surface */
189                 bool use_plane;
190                 float    plane[4];
191
192                 /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */
193                 bool use_depth;
194
195                 /* offset projection by this value */
196                 bool use_offset;
197                 float    offset[3];  /* worldspace */
198                 float    surface_offset;
199                 bool     use_surface_offset_absolute;
200         } project;
201
202         /* cursor sampling */
203         struct {
204                 /* use substeps, needed for nicely interpolating depth */
205                 bool use_substeps;
206         } sample;
207
208         struct {
209                 float min, max, range;
210         } radius;
211
212         struct {
213                 float mouse[2];
214                 /* used incase we can't calculate the depth */
215                 float location_world[3];
216
217                 float location_world_valid[3];
218
219                 const struct StrokeElem *selem;
220         } prev;
221
222         ViewContext vc;
223         enum {
224                 CURVE_DRAW_IDLE = 0,
225                 CURVE_DRAW_PAINTING = 1,
226         } state;
227
228         /* StrokeElem */
229         BLI_mempool *stroke_elem_pool;
230
231         void *draw_handle_view;
232 };
233
234 static float stroke_elem_radius_from_pressure(const struct CurveDrawData *cdd, const float pressure)
235 {
236         const Curve *cu = cdd->vc.obedit->data;
237         return ((pressure * cdd->radius.range) + cdd->radius.min) * cu->ext2;
238 }
239
240 static float stroke_elem_radius(const struct CurveDrawData *cdd, const struct StrokeElem *selem)
241 {
242         return stroke_elem_radius_from_pressure(cdd, selem->pressure);
243 }
244
245 static void stroke_elem_pressure_set(const struct CurveDrawData *cdd, struct StrokeElem *selem, float pressure)
246 {
247         if ((cdd->project.surface_offset != 0.0f) &&
248             !cdd->project.use_surface_offset_absolute &&
249             !is_zero_v3(selem->normal_local))
250         {
251                 const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) -
252                                      stroke_elem_radius_from_pressure(cdd, selem->pressure);
253                 madd_v3_v3fl(selem->location_local, selem->normal_local, adjust);
254                 mul_v3_m4v3(selem->location_world, cdd->vc.obedit->obmat, selem->location_local);
255         }
256         selem->pressure = pressure;
257 }
258
259 static void stroke_elem_interp(
260         struct StrokeElem *selem_out,
261         const struct StrokeElem *selem_a,  const struct StrokeElem *selem_b, float t)
262 {
263         interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
264         interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
265         interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
266         selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
267 }
268
269
270 /**
271  * Sets the depth from #StrokeElem.mval
272  */
273 static bool stroke_elem_project(
274         const struct CurveDrawData *cdd,
275         const int mval_i[2], const float mval_fl[2],
276         float surface_offset, const float radius,
277         float r_location_world[3], float r_normal_world[3])
278 {
279         View3D *v3d = cdd->vc.v3d;
280         ARegion *ar = cdd->vc.ar;
281         RegionView3D *rv3d = cdd->vc.rv3d;
282
283         bool is_location_world_set = false;
284
285         /* project to 'location_world' */
286         if (cdd->project.use_plane) {
287                 /* get the view vector to 'location' */
288                 float ray_origin[3], ray_direction[3];
289                 ED_view3d_win_to_ray(cdd->vc.ar, v3d, mval_fl, ray_origin, ray_direction, false);
290
291                 float lambda;
292                 if (isect_ray_plane_v3(ray_origin, ray_direction, cdd->project.plane, &lambda, true)) {
293                         madd_v3_v3v3fl(r_location_world, ray_origin, ray_direction, lambda);
294                         if (r_normal_world) {
295                                 zero_v3(r_normal_world);
296                         }
297                         is_location_world_set = true;
298                 }
299         }
300         else {
301                 const ViewDepths *depths = rv3d->depths;
302                 if (depths &&
303                     ((unsigned int)mval_i[0] < depths->w) &&
304                     ((unsigned int)mval_i[1] < depths->h))
305                 {
306                         const double depth = (double)depth_read_zbuf(&cdd->vc, mval_i[0], mval_i[1]);
307                         if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
308                                 if (depth_unproject(ar, mval_i, depth, r_location_world)) {
309                                         is_location_world_set = true;
310                                         if (r_normal_world) {
311                                                 zero_v3(r_normal_world);
312                                         }
313
314                                         if (surface_offset != 0.0f) {
315                                                 const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius;
316                                                 float normal[3];
317                                                 if (depth_read_normal(&cdd->vc, mval_i, normal)) {
318                                                         madd_v3_v3fl(r_location_world, normal, offset * surface_offset);
319                                                         if (r_normal_world) {
320                                                                 copy_v3_v3(r_normal_world, normal);
321                                                         }
322                                                 }
323                                         }
324                                 }
325                         }
326                 }
327         }
328
329         if (is_location_world_set) {
330                 if (cdd->project.use_offset) {
331                         add_v3_v3(r_location_world, cdd->project.offset);
332                 }
333         }
334
335         return is_location_world_set;
336 }
337
338 static bool stroke_elem_project_fallback(
339         const struct CurveDrawData *cdd,
340         const int mval_i[2], const float mval_fl[2],
341         const float surface_offset, const float radius,
342         const float location_fallback_depth[3],
343         float r_location_world[3], float r_location_local[3],
344         float r_normal_world[3], float r_normal_local[3])
345 {
346         bool is_depth_found = stroke_elem_project(
347                 cdd, mval_i, mval_fl,
348                 surface_offset, radius,
349                 r_location_world, r_normal_world);
350         if (is_depth_found == false) {
351                 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.ar, location_fallback_depth, mval_fl, r_location_world);
352                 zero_v3(r_normal_local);
353         }
354         mul_v3_m4v3(r_location_local, cdd->vc.obedit->imat, r_location_world);
355
356         if (!is_zero_v3(r_normal_world)) {
357                 copy_v3_v3(r_normal_local, r_normal_world);
358                 mul_transposed_mat3_m4_v3(cdd->vc.obedit->obmat, r_normal_local);
359                 normalize_v3(r_normal_local);
360         }
361         else {
362                 zero_v3(r_normal_local);
363         }
364
365         return is_depth_found;
366 }
367
368 /**
369  * \note #StrokeElem.mval & #StrokeElem.pressure must be set first.
370  */
371 static bool stroke_elem_project_fallback_elem(
372         const struct CurveDrawData *cdd,
373         const float location_fallback_depth[3],
374         struct StrokeElem *selem)
375 {
376         const int mval_i[2] = {UNPACK2(selem->mval)};
377         const float radius = stroke_elem_radius(cdd, selem);
378         return stroke_elem_project_fallback(
379                 cdd, mval_i, selem->mval,
380                 cdd->project.surface_offset, radius,
381                 location_fallback_depth,
382                 selem->location_world, selem->location_local,
383                 selem->normal_world, selem->normal_local);
384 }
385
386 /** \} */
387
388
389 /* -------------------------------------------------------------------- */
390
391 /** \name Operator/Stroke Conversion
392  * \{ */
393
394 static void curve_draw_stroke_to_operator_elem(
395         wmOperator *op, const struct StrokeElem *selem)
396 {
397         PointerRNA itemptr;
398         RNA_collection_add(op->ptr, "stroke", &itemptr);
399
400         RNA_float_set_array(&itemptr, "mouse", selem->mval);
401         RNA_float_set_array(&itemptr, "location", selem->location_world);
402         RNA_float_set(&itemptr, "pressure", selem->pressure);
403 }
404
405 static void curve_draw_stroke_from_operator_elem(
406         wmOperator *op, PointerRNA *itemptr)
407 {
408         struct CurveDrawData *cdd = op->customdata;
409
410         struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
411
412         RNA_float_get_array(itemptr, "mouse", selem->mval);
413         RNA_float_get_array(itemptr, "location", selem->location_world);
414         mul_v3_m4v3(selem->location_local, cdd->vc.obedit->imat, selem->location_world);
415         selem->pressure = RNA_float_get(itemptr, "pressure");
416 }
417
418 static void curve_draw_stroke_to_operator(wmOperator *op)
419 {
420         struct CurveDrawData *cdd = op->customdata;
421
422         BLI_mempool_iter iter;
423         const struct StrokeElem *selem;
424
425         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
426         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
427                 curve_draw_stroke_to_operator_elem(op, selem);
428         }
429 }
430
431 static void curve_draw_stroke_from_operator(wmOperator *op)
432 {
433         RNA_BEGIN (op->ptr, itemptr, "stroke")
434         {
435                 curve_draw_stroke_from_operator_elem(op, &itemptr);
436         }
437         RNA_END;
438 }
439
440 /** \} */
441
442
443 /* -------------------------------------------------------------------- */
444
445 /** \name Operator Callbacks & Helpers
446  * \{ */
447
448 static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
449 {
450         wmOperator *op = arg;
451         struct CurveDrawData *cdd = op->customdata;
452
453         const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
454
455         if (stroke_len == 0) {
456                 return;
457         }
458
459         View3D *v3d = cdd->vc.v3d;
460         Object *obedit = cdd->vc.obedit;
461         Curve *cu = obedit->data;
462
463         if (cu->ext2 > 0.0f) {
464                 BLI_mempool_iter iter;
465                 const struct StrokeElem *selem;
466
467                 const float  location_zero[3] = {0};
468                 const float *location_prev = location_zero;
469
470                 float color[3];
471                 UI_GetThemeColor3fv(TH_WIRE, color);
472
473                 Batch *sphere = Batch_get_sphere(0);
474                 Batch_set_builtin_program(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
475                 Batch_Uniform3fv(sphere, "color", color);
476
477                 /* scale to edit-mode space */
478                 gpuPushMatrix();
479                 gpuMultMatrix3D(obedit->obmat);
480
481                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
482                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
483                         gpuTranslate3f(
484                                 selem->location_local[0] - location_prev[0],
485                                 selem->location_local[1] - location_prev[1],
486                                 selem->location_local[2] - location_prev[2]);
487                         location_prev = selem->location_local;
488
489                         const float radius = stroke_elem_radius(cdd, selem);
490
491                         gpuPushMatrix();
492                         gpuScaleUniform(radius);
493                         Batch_draw(sphere);
494                         gpuPopMatrix();
495
496                         location_prev = selem->location_local;
497                 }
498
499                 gpuPopMatrix();
500         }
501
502         if (stroke_len > 1) {
503                 float (*coord_array)[3] = MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__);
504
505                 {
506                         BLI_mempool_iter iter;
507                         const struct StrokeElem *selem;
508                         int i;
509                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
510                         for (selem = BLI_mempool_iterstep(&iter), i = 0; selem; selem = BLI_mempool_iterstep(&iter), i++) {
511                                 copy_v3_v3(coord_array[i], selem->location_world);
512                         }
513                 }
514
515                 {
516                         VertexFormat *format = immVertexFormat();
517                         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 3, KEEP_FLOAT);
518                         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
519
520                         glEnable(GL_BLEND);
521                         glEnable(GL_LINE_SMOOTH);
522
523                         imm_cpack(0x0);
524                         immBegin(PRIM_LINE_STRIP, stroke_len);
525                         glLineWidth(3.0f);
526
527                         if (v3d->zbuf) {
528                                 glDisable(GL_DEPTH_TEST);
529                         }
530
531                         for (int i = 0; i < stroke_len; i++) {
532                                 immVertex3fv(pos, coord_array[i]);
533                         }
534
535                         immEnd();
536
537                         imm_cpack(0xffffffff);
538                         immBegin(PRIM_LINE_STRIP, stroke_len);
539                         glLineWidth(1.0f);
540
541                         for (int i = 0; i < stroke_len; i++) {
542                                 immVertex3fv(pos, coord_array[i]);
543                         }
544
545                         immEnd();
546
547                         if (v3d->zbuf) {
548                                 glEnable(GL_DEPTH_TEST);
549                         }
550
551                         glDisable(GL_BLEND);
552                         glDisable(GL_LINE_SMOOTH);
553
554                         immUnbindProgram();
555                 }
556
557                 MEM_freeN(coord_array);
558         }
559 }
560
561 static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
562 {
563         struct CurveDrawData *cdd = op->customdata;
564         Object *obedit = cdd->vc.obedit;
565
566         invert_m4_m4(obedit->imat, obedit->obmat);
567
568         struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
569
570         ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
571
572         /* handle pressure sensitivity (which is supplied by tablets) */
573         if (event->tablet_data) {
574                 const wmTabletData *wmtab = event->tablet_data;
575                 selem->pressure = wmtab->Pressure;
576         }
577         else {
578                 selem->pressure = 1.0f;
579         }
580
581         bool is_depth_found = stroke_elem_project_fallback_elem(
582                 cdd, cdd->prev.location_world_valid, selem);
583
584         if (is_depth_found) {
585                 /* use the depth if a fallback wasn't used */
586                 copy_v3_v3(cdd->prev.location_world_valid, selem->location_world);
587         }
588         copy_v3_v3(cdd->prev.location_world, selem->location_world);
589
590         float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval);
591         copy_v2_v2(cdd->prev.mouse, selem->mval);
592
593         if (cdd->sample.use_substeps && cdd->prev.selem) {
594                 const struct StrokeElem selem_target = *selem;
595                 struct StrokeElem *selem_new_last = selem;
596                 if (len_sq >= SQUARE(STROKE_SAMPLE_DIST_MAX_PX)) {
597                         int n = (int)ceil(sqrt((double)len_sq)) / STROKE_SAMPLE_DIST_MAX_PX ;
598
599                         for (int i = 1; i < n; i++) {
600                                 struct StrokeElem *selem_new = selem_new_last;
601                                 stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, (float)i / n);
602
603                                 const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
604                                         cdd, cdd->prev.location_world_valid, selem_new);
605                                 if (is_depth_found == false) {
606                                         if (is_depth_found_substep) {
607                                                 copy_v3_v3(cdd->prev.location_world_valid, selem_new->location_world);
608                                         }
609                                 }
610
611                                 selem_new_last = BLI_mempool_calloc(cdd->stroke_elem_pool);
612                         }
613                 }
614                 selem = selem_new_last;
615                 *selem_new_last = selem_target;
616         }
617
618         cdd->prev.selem = selem;
619
620         ED_region_tag_redraw(cdd->vc.ar);
621 }
622
623 static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
624 {
625         struct CurveDrawData *cdd = op->customdata;
626         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
627
628         /* add first point */
629         curve_draw_event_add(op, event);
630
631         if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && cdd->project.use_depth &&
632             (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS))
633         {
634                 RegionView3D *rv3d = cdd->vc.rv3d;
635
636                 cdd->project.use_depth = false;
637                 cdd->project.use_plane = true;
638
639                 float normal[3] = {0.0f};
640                 if (ELEM(cps->surface_plane,
641                          CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW,
642                          CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE))
643                 {
644                         if (depth_read_normal(&cdd->vc, event->mval, normal)) {
645                                 if (cps->surface_plane == CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW) {
646                                         float cross_a[3], cross_b[3];
647                                         cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
648                                         cross_v3_v3v3(cross_b, normal, cross_a);
649                                         copy_v3_v3(normal, cross_b);
650                                 }
651                         }
652                 }
653
654                 /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
655                 if (is_zero_v3(normal)) {
656                         copy_v3_v3(normal, rv3d->viewinv[2]);
657                 }
658
659                 normalize_v3_v3(cdd->project.plane, normal);
660                 cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid);
661
662                 /* Special case for when we only have offset applied on the first-hit,
663                  * the remaining stroke must be offset too. */
664                 if (cdd->project.surface_offset != 0.0f) {
665                         const float mval_fl[2] = {UNPACK2(event->mval)};
666
667                         float location_no_offset[3];
668
669                         if (stroke_elem_project(
670                                 cdd, event->mval, mval_fl, 0.0f, 0.0f,
671                                 location_no_offset, NULL))
672                         {
673                                 sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
674                                 if (!is_zero_v3(cdd->project.offset)) {
675                                         cdd->project.use_offset = true;
676                                 }
677                         }
678                 }
679                 /* end special case */
680
681         }
682
683         cdd->init_event_type = event->type;
684         cdd->state = CURVE_DRAW_PAINTING;
685 }
686
687 static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
688 {
689         BLI_assert(op->customdata == NULL);
690
691         struct CurveDrawData *cdd = MEM_callocN(sizeof(*cdd), __func__);
692
693         if (is_invoke) {
694                 view3d_set_viewcontext(C, &cdd->vc);
695                 if (ELEM(NULL, cdd->vc.ar, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
696                         MEM_freeN(cdd);
697                         BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport");
698                         return false;
699                 }
700         }
701         else {
702                 cdd->vc.scene = CTX_data_scene(C);
703                 cdd->vc.scene_layer = CTX_data_scene_layer(C);
704                 cdd->vc.obedit = CTX_data_edit_object(C);
705         }
706
707         op->customdata = cdd;
708
709         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
710
711         cdd->curve_type = cps->curve_type;
712
713         cdd->radius.min = cps->radius_min;
714         cdd->radius.max = cps->radius_max;
715         cdd->radius.range = cps->radius_max - cps->radius_min;
716         cdd->project.surface_offset = cps->surface_offset;
717         cdd->project.use_surface_offset_absolute = (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS) != 0;
718
719         cdd->stroke_elem_pool = BLI_mempool_create(
720                 sizeof(struct StrokeElem), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
721
722         return true;
723 }
724
725
726 static void curve_draw_exit(wmOperator *op)
727 {
728         struct CurveDrawData *cdd = op->customdata;
729         if (cdd) {
730                 if (cdd->draw_handle_view) {
731                         ED_region_draw_cb_exit(cdd->vc.ar->type, cdd->draw_handle_view);
732                         WM_cursor_modal_restore(cdd->vc.win);
733                 }
734
735                 if (cdd->stroke_elem_pool) {
736                         BLI_mempool_destroy(cdd->stroke_elem_pool);
737                 }
738
739                 MEM_freeN(cdd);
740                 op->customdata = NULL;
741         }
742 }
743
744 /**
745  * Initialize values before calling 'exec' (when running interactively).
746  */
747 static void curve_draw_exec_precalc(wmOperator *op)
748 {
749         struct CurveDrawData *cdd = op->customdata;
750         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
751         PropertyRNA *prop;
752
753         prop = RNA_struct_find_property(op->ptr, "fit_method");
754         if (!RNA_property_is_set(op->ptr, prop)) {
755                 RNA_property_enum_set(op->ptr, prop, cps->fit_method);
756         }
757
758         prop = RNA_struct_find_property(op->ptr, "corner_angle");
759         if (!RNA_property_is_set(op->ptr, prop)) {
760                 const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : (float)M_PI;
761                 RNA_property_float_set(op->ptr, prop, corner_angle);
762         }
763
764         prop = RNA_struct_find_property(op->ptr, "error_threshold");
765         if (!RNA_property_is_set(op->ptr, prop)) {
766
767                 /* error isnt set so we'll have to calculate it from the pixel values */
768                 BLI_mempool_iter iter;
769                 const struct StrokeElem *selem, *selem_prev;
770
771                 float len_3d = 0.0f, len_2d = 0.0f;
772                 float scale_px;  /* pixel to local space scale */
773
774                 int i = 0;
775                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
776                 selem_prev = BLI_mempool_iterstep(&iter);
777                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
778                         len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
779                         len_2d += len_v2v2(selem->mval, selem_prev->mval);
780                         selem_prev = selem;
781                 }
782                 scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ?  (len_3d / len_2d) : 0.0f;
783                 float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px;
784                 RNA_property_float_set(op->ptr, prop, error_threshold);
785         }
786
787         prop = RNA_struct_find_property(op->ptr, "use_cyclic");
788         if (!RNA_property_is_set(op->ptr, prop)) {
789                 bool use_cyclic = false;
790
791                 if (BLI_mempool_count(cdd->stroke_elem_pool) > 2) {
792                         BLI_mempool_iter iter;
793                         const struct StrokeElem *selem, *selem_first, *selem_last;
794
795                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
796                         selem_first = BLI_mempool_iterstep(&iter);
797                         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
798                                 selem_last = selem;
799                         }
800
801                         if (len_squared_v2v2(
802                                 selem_first->mval,
803                                 selem_last->mval) <= SQUARE(STROKE_CYCLIC_DIST_PX * U.pixelsize))
804                         {
805                                 use_cyclic = true;
806                         }
807                 }
808
809                 RNA_property_boolean_set(op->ptr, prop, use_cyclic);
810         }
811
812
813         if ((cps->radius_taper_start != 0.0f) ||
814             (cps->radius_taper_end   != 0.0f))
815         {
816                 /* note, we could try to de-duplicate the length calculations above */
817                 const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
818
819                 BLI_mempool_iter iter;
820                 struct StrokeElem *selem, *selem_prev;
821
822                 float *lengths = MEM_mallocN(sizeof(float) * stroke_len, __func__);
823                 struct StrokeElem **selem_array = MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__);
824                 lengths[0] = 0.0f;
825
826                 float len_3d = 0.0f;
827
828                 int i = 1;
829                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
830                 selem_prev = BLI_mempool_iterstep(&iter);
831                 selem_array[0] = selem_prev;
832                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
833                         const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
834                         len_3d += len_3d_segment;
835                         lengths[i] = len_3d;
836                         selem_array[i] = selem;
837                         selem_prev = selem;
838                 }
839
840                 if (cps->radius_taper_start != 0.0f) {
841                         const float len_taper_max = cps->radius_taper_start * len_3d;
842                         for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) {
843                                 const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max);
844                                 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
845                         }
846                 }
847
848                 if (cps->radius_taper_end != 0.0f) {
849                         const float len_taper_max = cps->radius_taper_end * len_3d;
850                         const float len_taper_min = len_3d - len_taper_max;
851                         for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) {
852                                 const float pressure_new = selem_array[i]->pressure * ((len_3d - lengths[i]) / len_taper_max);
853                                 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
854                         }
855                 }
856
857                 MEM_freeN(lengths);
858                 MEM_freeN(selem_array);
859         }
860 }
861
862 static int curve_draw_exec(bContext *C, wmOperator *op)
863 {
864         if (op->customdata == NULL) {
865                 if (!curve_draw_init(C, op, false)) {
866                         return OPERATOR_CANCELLED;
867                 }
868         }
869
870         struct CurveDrawData *cdd = op->customdata;
871
872         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
873         Object *obedit = cdd->vc.scene->obedit;
874         Curve *cu = obedit->data;
875         ListBase *nurblist = object_editcurve_get(obedit);
876
877         int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
878
879         const bool is_3d = (cu->flag & CU_3D) != 0;
880         invert_m4_m4(obedit->imat, obedit->obmat);
881
882         if (BLI_mempool_count(cdd->stroke_elem_pool) == 0) {
883                 curve_draw_stroke_from_operator(op);
884                 stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
885         }
886
887         ED_curve_deselect_all(cu->editnurb);
888
889         const float radius_min = cps->radius_min;
890         const float radius_max = cps->radius_max;
891         const float radius_range = cps->radius_max - cps->radius_min;
892
893         Nurb *nu = MEM_callocN(sizeof(Nurb), __func__);
894         nu->pntsv = 1;
895         nu->resolu = cu->resolu;
896         nu->resolv = cu->resolv;
897         nu->flag |= CU_SMOOTH;
898
899         const bool use_pressure_radius =
900                 (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
901                 ((cps->radius_taper_start != 0.0f) ||
902                  (cps->radius_taper_end   != 0.0f));
903
904         if (cdd->curve_type == CU_BEZIER) {
905                 nu->type = CU_BEZIER;
906
907 #ifdef USE_SPLINE_FIT
908
909                 /* Allow to interpolate multiple channels */
910                 int dims = 3;
911                 struct {
912                         int radius;
913                 } coords_indices;
914                 coords_indices.radius = use_pressure_radius ? dims++ : -1;
915
916                 float *coords = MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__);
917
918                 float       *cubic_spline = NULL;
919                 unsigned int cubic_spline_len = 0;
920
921                 /* error in object local space */
922                 const int fit_method = RNA_enum_get(op->ptr, "fit_method");
923                 const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
924                 const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
925                 const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
926
927                 {
928                         BLI_mempool_iter iter;
929                         const struct StrokeElem *selem;
930                         float *co = coords;
931
932                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
933                         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), co += dims) {
934                                 copy_v3_v3(co, selem->location_local);
935                                 if (coords_indices.radius != -1) {
936                                         co[coords_indices.radius] = selem->pressure;
937                                 }
938
939                                 /* remove doubles */
940                                 if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) {
941                                         co -= dims;
942                                         stroke_len--;
943                                 }
944                         }
945                 }
946
947                 unsigned int *corners = NULL;
948                 unsigned int  corners_len = 0;
949
950                 if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < (float)M_PI)) {
951                         /* this could be configurable... */
952                         const float corner_radius_min = error_threshold / 8;
953                         const float corner_radius_max = error_threshold * 2;
954                         const unsigned int samples_max = 16;
955
956                         curve_fit_corners_detect_fl(
957                                 coords, stroke_len, dims,
958                                 corner_radius_min, corner_radius_max,
959                                 samples_max, corner_angle,
960                                 &corners, &corners_len);
961                 }
962
963                 unsigned int *corners_index = NULL;
964                 unsigned int  corners_index_len = 0;
965                 unsigned int  calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
966
967                 if ((stroke_len > 2) && use_cyclic) {
968                         calc_flag |= CURVE_FIT_CALC_CYCLIC;
969                 }
970
971                 int result;
972                 if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
973                         result = curve_fit_cubic_to_points_refit_fl(
974                                 coords, stroke_len, dims, error_threshold, calc_flag,
975                                 NULL, 0, corner_angle,
976                                 &cubic_spline, &cubic_spline_len,
977                                 NULL,
978                                 &corners_index, &corners_index_len);
979                 }
980                 else {
981                         result = curve_fit_cubic_to_points_fl(
982                                 coords, stroke_len, dims, error_threshold, calc_flag,
983                                 corners, corners_len,
984                                 &cubic_spline, &cubic_spline_len,
985                                 NULL,
986                                 &corners_index, &corners_index_len);
987                 }
988
989                 MEM_freeN(coords);
990                 if (corners) {
991                         free(corners);
992                 }
993
994                 if (result == 0) {
995                         nu->pntsu = cubic_spline_len;
996                         nu->bezt = MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__);
997
998                         float *co = cubic_spline;
999                         BezTriple *bezt = nu->bezt;
1000                         for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
1001                                 const float *handle_l = co + (dims * 0);
1002                                 const float *pt       = co + (dims * 1);
1003                                 const float *handle_r = co + (dims * 2);
1004
1005                                 copy_v3_v3(bezt->vec[0], handle_l);
1006                                 copy_v3_v3(bezt->vec[1], pt);
1007                                 copy_v3_v3(bezt->vec[2], handle_r);
1008
1009                                 if (coords_indices.radius != -1) {
1010                                         bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
1011                                 }
1012                                 else {
1013                                         bezt->radius = radius_max;
1014                                 }
1015
1016                                 bezt->h1 = bezt->h2 = HD_ALIGN;  /* will set to free in second pass */
1017                                 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
1018                         }
1019
1020                         if (corners_index) {
1021                                 /* ignore the first and last */
1022                                 unsigned int i_start = 0, i_end = corners_index_len;
1023
1024                                 if ((corners_index_len >= 2) &&
1025                                     (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0)
1026                                 {
1027                                         i_start += 1;
1028                                         i_end   -= 1;
1029                                 }
1030
1031                                 for (unsigned int i = i_start; i < i_end; i++) {
1032                                         bezt = &nu->bezt[corners_index[i]];
1033                                         bezt->h1 = bezt->h2 = HD_FREE;
1034                                 }
1035                         }
1036
1037                         if (calc_flag & CURVE_FIT_CALC_CYCLIC) {
1038                                 nu->flagu |= CU_NURB_CYCLIC;
1039                         }
1040                 }
1041
1042                 if (corners_index) {
1043                         free(corners_index);
1044                 }
1045
1046                 if (cubic_spline) {
1047                         free(cubic_spline);
1048                 }
1049
1050 #else
1051                 nu->pntsu = stroke_len;
1052                 nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
1053
1054                 BezTriple *bezt = nu->bezt;
1055
1056                 {
1057                         BLI_mempool_iter iter;
1058                         const struct StrokeElem *selem;
1059
1060                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
1061                         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
1062                                 copy_v3_v3(bezt->vec[1], selem->location_local);
1063                                 if (!is_3d) {
1064                                         bezt->vec[1][2] = 0.0f;
1065                                 }
1066
1067                                 if (use_pressure_radius) {
1068                                         bezt->radius = selem->pressure;
1069                                 }
1070                                 else {
1071                                         bezt->radius = radius_max;
1072                                 }
1073
1074                                 bezt->h1 = bezt->h2 = HD_AUTO;
1075
1076                                 bezt->f1 |= SELECT;
1077                                 bezt->f2 |= SELECT;
1078                                 bezt->f3 |= SELECT;
1079
1080                                 bezt++;
1081                         }
1082                 }
1083 #endif
1084
1085                 BKE_nurb_handles_calc(nu);
1086         }
1087         else {  /* CU_POLY */
1088                 BLI_mempool_iter iter;
1089                 const struct StrokeElem *selem;
1090
1091                 nu->pntsu = stroke_len;
1092                 nu->type = CU_POLY;
1093                 nu->bp = MEM_callocN(nu->pntsu * sizeof(BPoint), __func__);
1094
1095                 BPoint *bp = nu->bp;
1096
1097                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
1098                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
1099                         copy_v3_v3(bp->vec, selem->location_local);
1100                         if (!is_3d) {
1101                                 bp->vec[2] = 0.0f;
1102                         }
1103
1104                         if (use_pressure_radius) {
1105                                 bp->radius = (selem->pressure * radius_range) + radius_min;
1106                         }
1107                         else {
1108                                 bp->radius = cps->radius_max;
1109                         }
1110                         bp->f1 = SELECT;
1111                         bp->vec[3] = 1.0f;
1112
1113                         bp++;
1114                 }
1115
1116                 BKE_nurb_knot_calc_u(nu);
1117         }
1118
1119         BLI_addtail(nurblist, nu);
1120
1121         BKE_curve_nurb_active_set(cu, nu);
1122         cu->actvert = nu->pntsu - 1;
1123
1124         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1125         DAG_id_tag_update(obedit->data, 0);
1126
1127         curve_draw_exit(op);
1128
1129         return OPERATOR_FINISHED;
1130 }
1131
1132 static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1133 {
1134         if (RNA_struct_property_is_set(op->ptr, "stroke")) {
1135                 return curve_draw_exec(C, op);
1136         }
1137
1138         if (!curve_draw_init(C, op, true)) {
1139                 return OPERATOR_CANCELLED;
1140         }
1141
1142         struct CurveDrawData *cdd = op->customdata;
1143
1144         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
1145
1146         const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1147
1148         /* fallback (incase we can't find the depth on first test) */
1149         {
1150                 const float mval_fl[2] = {UNPACK2(event->mval)};
1151                 float center[3];
1152                 negate_v3_v3(center, cdd->vc.rv3d->ofs);
1153                 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.ar, center, mval_fl, cdd->prev.location_world);
1154                 copy_v3_v3(cdd->prev.location_world_valid, cdd->prev.location_world);
1155         }
1156
1157         cdd->draw_handle_view = ED_region_draw_cb_activate(
1158                 cdd->vc.ar->type, curve_draw_stroke_3d, op, REGION_DRAW_POST_VIEW);
1159         WM_cursor_modal_set(cdd->vc.win, BC_PAINTBRUSHCURSOR);
1160
1161         {
1162                 View3D *v3d = cdd->vc.v3d;
1163                 RegionView3D *rv3d = cdd->vc.rv3d;
1164                 Object *obedit = cdd->vc.obedit;
1165                 Curve *cu = obedit->data;
1166
1167                 const float *plane_no = NULL;
1168                 const float *plane_co = NULL;
1169
1170                 if ((cu->flag & CU_3D) == 0) {
1171                         /* 2D overrides other options */
1172                         plane_co = obedit->obmat[3];
1173                         plane_no = obedit->obmat[2];
1174                         cdd->project.use_plane = true;
1175                 }
1176                 else {
1177                         if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) &&
1178                             (v3d->drawtype > OB_WIRE))
1179                         {
1180                                 /* needed or else the draw matrix can be incorrect */
1181                                 view3d_operator_needs_opengl(C);
1182
1183                                 ED_view3d_autodist_init(cdd->vc.scene, cdd->vc.ar, cdd->vc.v3d, 0);
1184
1185                                 if (cdd->vc.rv3d->depths) {
1186                                         cdd->vc.rv3d->depths->damaged = true;
1187                                 }
1188
1189                                 ED_view3d_depth_update(cdd->vc.ar);
1190
1191                                 if (cdd->vc.rv3d->depths != NULL) {
1192                                         cdd->project.use_depth = true;
1193                                 }
1194                                 else {
1195                                         BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
1196                                         cdd->project.use_depth = false;
1197                                 }
1198                         }
1199
1200                         /* use view plane (when set or as fallback when surface can't be found) */
1201                         if (cdd->project.use_depth == false) {
1202                                 plane_co = ED_view3d_cursor3d_get(cdd->vc.scene, v3d);
1203                                 plane_no = rv3d->viewinv[2];
1204                                 cdd->project.use_plane = true;
1205                         }
1206
1207                         if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
1208                                 cdd->sample.use_substeps = true;
1209                         }
1210                 }
1211
1212                 if (cdd->project.use_plane) {
1213                         normalize_v3_v3(cdd->project.plane, plane_no);
1214                         cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
1215                 }
1216         }
1217
1218         if (is_modal == false) {
1219                 curve_draw_event_add_first(op, event);
1220         }
1221
1222         /* add temp handler */
1223         WM_event_add_modal_handler(C, op);
1224
1225         return OPERATOR_RUNNING_MODAL;
1226 }
1227
1228 static void curve_draw_cancel(bContext *UNUSED(C), wmOperator *op)
1229 {
1230         curve_draw_exit(op);
1231 }
1232
1233
1234 /* Modal event handling of frame changing */
1235 static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
1236 {
1237         int ret = OPERATOR_RUNNING_MODAL;
1238         struct CurveDrawData *cdd = op->customdata;
1239
1240         UNUSED_VARS(C, op);
1241
1242         if (event->type == cdd->init_event_type) {
1243                 if (event->val == KM_RELEASE) {
1244                         ED_region_tag_redraw(cdd->vc.ar);
1245
1246                         curve_draw_exec_precalc(op);
1247
1248                         curve_draw_stroke_to_operator(op);
1249
1250                         curve_draw_exec(C, op);
1251
1252                         return OPERATOR_FINISHED;
1253                 }
1254         }
1255         else if (ELEM(event->type, ESCKEY, RIGHTMOUSE)) {
1256                 ED_region_tag_redraw(cdd->vc.ar);
1257                 curve_draw_cancel(C, op);
1258                 return OPERATOR_CANCELLED;
1259         }
1260         else if (ELEM(event->type, LEFTMOUSE)) {
1261                 if (event->val == KM_PRESS) {
1262                         curve_draw_event_add_first(op, event);
1263                 }
1264         }
1265         else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
1266                 if (cdd->state == CURVE_DRAW_PAINTING) {
1267                         const float mval_fl[2] = {UNPACK2(event->mval)};
1268                         if (len_squared_v2v2(mval_fl, cdd->prev.mouse) > SQUARE(STROKE_SAMPLE_DIST_MIN_PX)) {
1269                                 curve_draw_event_add(op, event);
1270                         }
1271                 }
1272         }
1273
1274         return ret;
1275 }
1276
1277 void CURVE_OT_draw(wmOperatorType *ot)
1278 {
1279         /* identifiers */
1280         ot->name = "Draw Curve";
1281         ot->idname = "CURVE_OT_draw";
1282         ot->description = "Draw a freehand spline";
1283
1284         /* api callbacks */
1285         ot->exec = curve_draw_exec;
1286         ot->invoke = curve_draw_invoke;
1287         ot->cancel = curve_draw_cancel;
1288         ot->modal = curve_draw_modal;
1289         ot->poll = ED_operator_editcurve;
1290
1291         /* flags */
1292         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1293
1294         /* properties */
1295         PropertyRNA *prop;
1296
1297         prop = RNA_def_float_distance(
1298                 ot->srna, "error_threshold", 0.0f, 0.0f, 10.0f, "Error",
1299                 "Error distance threshold (in object units)",
1300                 0.0001f, 10.0f);
1301         RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
1302
1303         RNA_def_enum(ot->srna, "fit_method", rna_enum_curve_fit_method_items, CURVE_PAINT_FIT_METHOD_REFIT,
1304                      "Fit Method", "");
1305
1306         prop = RNA_def_float_distance(
1307                 ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
1308         RNA_def_property_subtype(prop, PROP_ANGLE);
1309
1310         prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
1311         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1312
1313         prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1314         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
1315
1316         prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1317         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
1318 }
1319
1320 /** \} */