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