Blender V5.0
editcurve_paint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_object_types.h"
10#include "DNA_scene_types.h"
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_listbase.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_rotation.h"
17#include "BLI_math_vector.h"
18#include "BLI_mempool.h"
19
20#include "BLT_translation.hh"
21
22#include "BKE_context.hh"
23#include "BKE_curve.hh"
24#include "BKE_fcurve.hh"
25#include "BKE_object_types.hh"
26#include "BKE_report.hh"
27#include "BKE_screen.hh"
28
29#include "DEG_depsgraph.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "ED_curve.hh"
35#include "ED_screen.hh"
36#include "ED_space_api.hh"
37#include "ED_view3d.hh"
38
39#include "GPU_batch.hh"
40#include "GPU_batch_presets.hh"
41#include "GPU_immediate.hh"
42#include "GPU_immediate_util.hh"
43#include "GPU_matrix.hh"
44#include "GPU_state.hh"
45
46#include "curve_intern.hh"
47
48#include "UI_resources.hh"
49
50#include "RNA_access.hh"
51#include "RNA_define.hh"
52#include "RNA_prototypes.hh"
53
54#include "RNA_enum_types.hh"
55
56#define USE_SPLINE_FIT
57
58#ifdef USE_SPLINE_FIT
59extern "C" {
60# include "curve_fit_nd.h"
61}
62#endif
63
64/* Distance between input samples */
65#define STROKE_SAMPLE_DIST_MIN_PX 1
66#define STROKE_SAMPLE_DIST_MAX_PX 3
67
68/* Distance between start/end points to consider cyclic */
69#define STROKE_CYCLIC_DIST_PX 8
70
71/* -------------------------------------------------------------------- */
74
75struct StrokeElem {
76 float mval[2];
79
80 /* Surface normal, may be zeroed. */
81 float normal_world[3];
82 float normal_local[3];
83
84 float pressure;
85};
86
91
95
96 /* projecting 2D into 3D space */
97 struct {
98 /* use a plane or project to the surface */
100 float plane[4];
101
102 /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but that's OK. */
104
105 /* offset projection by this value */
107 float offset[3]; /* world-space */
111
112 /* cursor sampling */
113 struct {
114 /* use substeps, needed for nicely interpolating depth */
117
118 struct {
119 float min, max, range;
121
122 struct {
123 float mval[2];
124 /* Used in case we can't calculate the depth. */
126
128
131
135
136 /* StrokeElem */
138
140};
141
142static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
143{
144 const Curve *cu = static_cast<const Curve *>(cdd->vc.obedit->data);
145 return ((pressure * cdd->radius.range) + cdd->radius.min) * cu->bevel_radius;
146}
147
148static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
149{
150 return stroke_elem_radius_from_pressure(cdd, selem->pressure);
151}
152
153static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
154{
155 if ((cdd->project.surface_offset != 0.0f) && !cdd->project.use_surface_offset_absolute &&
156 !is_zero_v3(selem->normal_local))
157 {
158 const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) -
160 madd_v3_v3fl(selem->location_local, selem->normal_local, adjust);
162 selem->location_world, cdd->vc.obedit->object_to_world().ptr(), selem->location_local);
163 }
164 selem->pressure = pressure;
165}
166
167static void stroke_elem_interp(StrokeElem *selem_out,
168 const StrokeElem *selem_a,
169 const StrokeElem *selem_b,
170 float t)
171{
172 interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
173 interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
174 interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
175 selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
176}
177
181static bool stroke_elem_project(const CurveDrawData *cdd,
182 const int mval_i[2],
183 const float mval_fl[2],
184 float surface_offset,
185 const float radius,
186 float r_location_world[3],
187 float r_normal_world[3])
188{
189 ARegion *region = cdd->vc.region;
190
191 bool is_location_world_set = false;
192
193 /* project to 'location_world' */
194 if (cdd->project.use_plane) {
195 /* get the view vector to 'location' */
196 if (ED_view3d_win_to_3d_on_plane(region, cdd->project.plane, mval_fl, true, r_location_world))
197 {
198 if (r_normal_world) {
199 zero_v3(r_normal_world);
200 }
201 is_location_world_set = true;
202 }
203 }
204 else {
205 const ViewDepths *depths = cdd->depths;
206 if (depths && (uint(mval_i[0]) < depths->w) && (uint(mval_i[1]) < depths->h)) {
207 float depth_fl = 1.0f;
208 ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl);
209 const double depth = double(depth_fl);
210 if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
211 if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) {
212 is_location_world_set = true;
213 if (r_normal_world) {
214 zero_v3(r_normal_world);
215 }
216
217 if (surface_offset != 0.0f) {
218 const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius;
219 float normal[3];
220 if (ED_view3d_depth_read_cached_normal(region, depths, mval_i, normal)) {
221 madd_v3_v3fl(r_location_world, normal, offset * surface_offset);
222 if (r_normal_world) {
223 copy_v3_v3(r_normal_world, normal);
224 }
225 }
226 }
227 }
228 }
229 }
230 }
231
232 if (is_location_world_set) {
233 if (cdd->project.use_offset) {
234 add_v3_v3(r_location_world, cdd->project.offset);
235 }
236 }
237
238 return is_location_world_set;
239}
240
242 const int mval_i[2],
243 const float mval_fl[2],
244 const float surface_offset,
245 const float radius,
246 const float location_fallback_depth[3],
247 float r_location_world[3],
248 float r_location_local[3],
249 float r_normal_world[3],
250 float r_normal_local[3])
251{
252 bool is_depth_found = stroke_elem_project(
253 cdd, mval_i, mval_fl, surface_offset, radius, r_location_world, r_normal_world);
254 if (is_depth_found == false) {
256 cdd->vc.v3d, cdd->vc.region, location_fallback_depth, mval_fl, r_location_world);
257 zero_v3(r_normal_local);
258 }
259 mul_v3_m4v3(r_location_local, cdd->vc.obedit->world_to_object().ptr(), r_location_world);
260
261 if (!is_zero_v3(r_normal_world)) {
262 copy_v3_v3(r_normal_local, r_normal_world);
263 mul_transposed_mat3_m4_v3(cdd->vc.obedit->object_to_world().ptr(), r_normal_local);
264 normalize_v3(r_normal_local);
265 }
266 else {
267 zero_v3(r_normal_local);
268 }
269
270 return is_depth_found;
271}
272
277 const float location_fallback_depth[3],
278 StrokeElem *selem)
279{
280 const int mval_i[2] = {int(selem->mval[0]), int(selem->mval[1])};
281 const float radius = stroke_elem_radius(cdd, selem);
283 mval_i,
284 selem->mval,
286 radius,
287 location_fallback_depth,
288 selem->location_world,
289 selem->location_local,
290 selem->normal_world,
291 selem->normal_local);
292}
293
295
296/* -------------------------------------------------------------------- */
299
301{
302 PointerRNA itemptr;
303 RNA_collection_add(op->ptr, "stroke", &itemptr);
304
305 RNA_float_set_array(&itemptr, "mouse", selem->mval);
306 RNA_float_set_array(&itemptr, "location", selem->location_world);
307 RNA_float_set(&itemptr, "pressure", selem->pressure);
308}
309
311{
312 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
313
314 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
315
316 RNA_float_get_array(itemptr, "mouse", selem->mval);
317 RNA_float_get_array(itemptr, "location", selem->location_world);
319 selem->location_local, cdd->vc.obedit->world_to_object().ptr(), selem->location_world);
320 selem->pressure = RNA_float_get(itemptr, "pressure");
321}
322
324{
325 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
326
327 BLI_mempool_iter iter;
328 const StrokeElem *selem;
329
331 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
332 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
333 {
335 }
336}
337
339{
340 RNA_BEGIN (op->ptr, itemptr, "stroke") {
342 }
343 RNA_END;
344}
345
347
348/* -------------------------------------------------------------------- */
351
352static void curve_draw_stroke_3d(const bContext * /*C*/, ARegion * /*region*/, void *arg)
353{
354 wmOperator *op = static_cast<wmOperator *>(arg);
355 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
356
357 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
358
359 if (stroke_len == 0) {
360 return;
361 }
362
363 Object *obedit = cdd->vc.obedit;
364 Curve *cu = static_cast<Curve *>(obedit->data);
365
366 if (cu->bevel_radius > 0.0f) {
367 BLI_mempool_iter iter;
368 const StrokeElem *selem;
369
370 const float location_zero[3] = {0};
371 const float *location_prev = location_zero;
372
373 float color[3];
375
376 blender::gpu::Batch *sphere = GPU_batch_preset_sphere(0);
378 GPU_batch_uniform_3fv(sphere, "color", color);
379
380 /* scale to edit-mode space */
382 GPU_matrix_mul(obedit->object_to_world().ptr());
383
385 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
386 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
387 {
388 GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0],
389 selem->location_local[1] - location_prev[1],
390 selem->location_local[2] - location_prev[2]);
391
392 const float radius = stroke_elem_radius(cdd, selem);
393
395 GPU_matrix_scale_1f(radius);
396 GPU_batch_draw(sphere);
398
399 location_prev = selem->location_local;
400 }
401
403 }
404
405 if (stroke_len > 1) {
406 float (*coord_array)[3] = static_cast<float (*)[3]>(
407 MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__));
408
409 {
410 BLI_mempool_iter iter;
411 const StrokeElem *selem;
412 int i;
414 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i = 0; selem;
415 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
416 {
417 copy_v3_v3(coord_array[i], selem->location_world);
418 }
419 }
420
421 {
424 format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
426
429 GPU_line_smooth(true);
430 GPU_line_width(3.0f);
431
432 imm_cpack(0x0);
433 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
434 for (int i = 0; i < stroke_len; i++) {
435 immVertex3fv(pos, coord_array[i]);
436 }
437 immEnd();
438
439 GPU_line_width(1.0f);
440
441 imm_cpack(0xffffffff);
442 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
443 for (int i = 0; i < stroke_len; i++) {
444 immVertex3fv(pos, coord_array[i]);
445 }
446 immEnd();
447
448 /* Reset defaults */
451 GPU_line_smooth(false);
452
454 }
455
456 MEM_freeN(coord_array);
457 }
458}
459
460static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
461{
462 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
463 Object *obedit = cdd->vc.obedit;
464
465 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
466
467 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
468
469 ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
470
471 /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */
472 selem->pressure = event->tablet.pressure;
473
474 bool is_depth_found = stroke_elem_project_fallback_elem(
475 cdd, cdd->prev.location_world_valid, selem);
476
477 if (is_depth_found) {
478 /* use the depth if a fallback wasn't used */
480 }
482
483 float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval);
484 copy_v2_v2(cdd->prev.mval, selem->mval);
485
486 if (cdd->sample.use_substeps && cdd->prev.selem) {
487 const StrokeElem selem_target = *selem;
488 StrokeElem *selem_new_last = selem;
489 if (len_sq >= square_f(STROKE_SAMPLE_DIST_MAX_PX)) {
490 int n = int(ceil(sqrt(double(len_sq)))) / STROKE_SAMPLE_DIST_MAX_PX;
491
492 for (int i = 1; i < n; i++) {
493 StrokeElem *selem_new = selem_new_last;
494 stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, float(i) / n);
495
496 const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
497 cdd, cdd->prev.location_world_valid, selem_new);
498 if (is_depth_found == false) {
499 if (is_depth_found_substep) {
501 }
502 }
503
504 selem_new_last = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
505 }
506 }
507 selem = selem_new_last;
508 *selem_new_last = selem_target;
509 }
510
511 cdd->prev.selem = selem;
512
514}
515
516static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
517{
518 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
520
521 /* add first point */
522 curve_draw_event_add(op, event);
523
526 {
527 RegionView3D *rv3d = cdd->vc.rv3d;
528
529 cdd->project.use_depth = false;
530 cdd->project.use_plane = true;
531
532 float normal[3] = {0.0f};
533 if (ELEM(cps->surface_plane,
536 {
537 if (ED_view3d_depth_read_cached_normal(cdd->vc.region, cdd->depths, event->mval, normal)) {
539 float cross_a[3], cross_b[3];
540 cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
541 cross_v3_v3v3(cross_b, normal, cross_a);
542 copy_v3_v3(normal, cross_b);
543 }
544 }
545 }
546
547 /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
548 if (is_zero_v3(normal)) {
549 copy_v3_v3(normal, rv3d->viewinv[2]);
550 }
551
552 normalize_v3_v3(cdd->project.plane, normal);
554
555 /* Special case for when we only have offset applied on the first-hit,
556 * the remaining stroke must be offset too. */
557 if (cdd->project.surface_offset != 0.0f) {
558 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
559
560 float location_no_offset[3];
561
562 if (stroke_elem_project(cdd, event->mval, mval_fl, 0.0f, 0.0f, location_no_offset, nullptr))
563 {
564 sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
565 if (!is_zero_v3(cdd->project.offset)) {
566 cdd->project.use_offset = true;
567 }
568 }
569 }
570 /* end special case */
571 }
572
573 cdd->init_event_type = event->type;
575}
576
577static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
578{
579 BLI_assert(op->customdata == nullptr);
580
583
584 if (is_invoke) {
586 if (ELEM(nullptr, cdd->vc.region, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
587 MEM_freeN(cdd);
588 BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport");
589 return false;
590 }
591 }
592 else {
593 cdd->vc.bmain = CTX_data_main(C);
594 cdd->vc.depsgraph = depsgraph;
595 cdd->vc.scene = CTX_data_scene(C);
598
599 /* Using an empty stroke complicates logic later,
600 * it's simplest to disallow early on (see: #94085). */
601 if (RNA_collection_is_empty(op->ptr, "stroke")) {
602 MEM_freeN(cdd);
603 BKE_report(op->reports, RPT_ERROR, "The \"stroke\" cannot be empty");
604 return false;
605 }
606 }
607
608 op->customdata = cdd;
609
611
612 cdd->curve_type = cps->curve_type;
613
614 cdd->radius.min = cps->radius_min;
615 cdd->radius.max = cps->radius_max;
616 cdd->radius.range = cps->radius_max - cps->radius_min;
620
622
623 return true;
624}
625
627{
628 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
629 if (cdd) {
630 if (cdd->draw_handle_view) {
633 }
634
635 if (cdd->stroke_elem_pool) {
637 }
638
639 if (cdd->depths) {
641 }
642 MEM_freeN(cdd);
643 op->customdata = nullptr;
644 }
645}
646
651{
652 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
654 PropertyRNA *prop;
655
656 prop = RNA_struct_find_property(op->ptr, "fit_method");
657 if (!RNA_property_is_set(op->ptr, prop)) {
658 RNA_property_enum_set(op->ptr, prop, cps->fit_method);
659 }
660
661 prop = RNA_struct_find_property(op->ptr, "corner_angle");
662 if (!RNA_property_is_set(op->ptr, prop)) {
663 const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle :
664 float(M_PI);
665 RNA_property_float_set(op->ptr, prop, corner_angle);
666 }
667
668 prop = RNA_struct_find_property(op->ptr, "error_threshold");
669 if (!RNA_property_is_set(op->ptr, prop)) {
670
671 /* Error isn't set so we'll have to calculate it from the pixel values. */
672 BLI_mempool_iter iter;
673 const StrokeElem *selem, *selem_prev;
674
675 float len_3d = 0.0f, len_2d = 0.0f;
676 float scale_px; /* pixel to local space scale */
677
678 int i = 0;
680 selem_prev = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
681 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
682 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
683 {
684 len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
685 len_2d += len_v2v2(selem->mval, selem_prev->mval);
686 selem_prev = selem;
687 }
688 scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
689 float error_threshold = (cps->error_threshold * UI_SCALE_FAC) * scale_px;
690 RNA_property_float_set(op->ptr, prop, error_threshold);
691 }
692
693 prop = RNA_struct_find_property(op->ptr, "use_cyclic");
694 if (!RNA_property_is_set(op->ptr, prop)) {
695 bool use_cyclic = false;
696
697 if (BLI_mempool_len(cdd->stroke_elem_pool) > 2) {
698 BLI_mempool_iter iter;
699 const StrokeElem *selem, *selem_first, *selem_last;
700
702 selem_first = selem_last = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
703 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
704 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
705 {
706 selem_last = selem;
707 }
708
709 if (len_squared_v2v2(selem_first->mval, selem_last->mval) <=
711 {
712 use_cyclic = true;
713 }
714 }
715
716 RNA_property_boolean_set(op->ptr, prop, use_cyclic);
717 }
718
719 if ((cps->radius_taper_start != 0.0f) || (cps->radius_taper_end != 0.0f)) {
720 /* NOTE: we could try to de-duplicate the length calculations above. */
721 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
722
723 BLI_mempool_iter iter;
724 StrokeElem *selem, *selem_prev;
725
726 float *lengths = MEM_malloc_arrayN<float>(stroke_len, __func__);
727 StrokeElem **selem_array = static_cast<StrokeElem **>(
728 MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__));
729 lengths[0] = 0.0f;
730
731 float len_3d = 0.0f;
732
733 int i = 1;
735 selem_prev = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter));
736 selem_array[0] = selem_prev;
737 for (selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
738 selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
739 {
740 const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
741 len_3d += len_3d_segment;
742 lengths[i] = len_3d;
743 selem_array[i] = selem;
744 selem_prev = selem;
745 }
746
747 if (cps->radius_taper_start != 0.0f) {
748 const float len_taper_max = cps->radius_taper_start * len_3d;
749 for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) {
750 const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max);
751 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
752 }
753 }
754
755 if (cps->radius_taper_end != 0.0f) {
756 const float len_taper_max = cps->radius_taper_end * len_3d;
757 const float len_taper_min = len_3d - len_taper_max;
758 for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) {
759 const float pressure_new = selem_array[i]->pressure *
760 ((len_3d - lengths[i]) / len_taper_max);
761 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
762 }
763 }
764
765 MEM_freeN(lengths);
766 MEM_freeN(selem_array);
767 }
768}
769
771{
772 if (op->customdata == nullptr) {
773 if (!curve_draw_init(C, op, false)) {
774 return OPERATOR_CANCELLED;
775 }
776 }
777
778 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
779
781 Object *obedit = cdd->vc.obedit;
782 Curve *cu = static_cast<Curve *>(obedit->data);
783 ListBase *nurblist = object_editcurve_get(obedit);
784
785 int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
786
787 const bool is_3d = (cu->flag & CU_3D) != 0;
788 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
789
790 if (BLI_mempool_len(cdd->stroke_elem_pool) == 0) {
792 stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
793 }
794
795 /* Deselect all existing curves. */
797
798 const float radius_min = cps->radius_min;
799 const float radius_max = cps->radius_max;
800 const float radius_range = cps->radius_max - cps->radius_min;
801
802 Nurb *nu = MEM_callocN<Nurb>(__func__);
803 nu->pntsv = 0;
804 nu->resolu = cu->resolu;
805 nu->resolv = cu->resolv;
806 nu->flag |= CU_SMOOTH;
807
808 const bool use_pressure_radius = (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
809 ((cps->radius_taper_start != 0.0f) ||
810 (cps->radius_taper_end != 0.0f));
811
812 if (cdd->curve_type == CU_BEZIER) {
813 nu->type = CU_BEZIER;
814
815#ifdef USE_SPLINE_FIT
816
817 /* Allow to interpolate multiple channels */
818 int dims = 3;
819 struct {
820 int radius;
821 } coords_indices;
822 coords_indices.radius = use_pressure_radius ? dims++ : -1;
823
824 float *coords = MEM_malloc_arrayN<float>(stroke_len * dims, __func__);
825
826 float *cubic_spline = nullptr;
827 uint cubic_spline_len = 0;
828
829 /* error in object local space */
830 const int fit_method = RNA_enum_get(op->ptr, "fit_method");
831 const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
832 const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
833 const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
834
835 {
836 BLI_mempool_iter iter;
837 const StrokeElem *selem;
838 float *co = coords;
839
841 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
842 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), co += dims)
843 {
844 copy_v3_v3(co, selem->location_local);
845 if (coords_indices.radius != -1) {
846 co[coords_indices.radius] = selem->pressure;
847 }
848
849 /* remove doubles */
850 if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) {
851 co -= dims;
852 stroke_len--;
853 }
854 }
855 }
856
857 uint *corners = nullptr;
858 uint corners_len = 0;
859
860 if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < float(M_PI))) {
861 /* this could be configurable... */
862 const float corner_radius_min = error_threshold / 8;
863 const float corner_radius_max = error_threshold * 2;
864 const uint samples_max = 16;
865
866 curve_fit_corners_detect_fl(coords,
867 stroke_len,
868 dims,
869 corner_radius_min,
870 corner_radius_max,
871 samples_max,
872 corner_angle,
873 &corners,
874 &corners_len);
875 }
876
877 uint *corners_index = nullptr;
878 uint corners_index_len = 0;
879 uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
880
881 if ((stroke_len > 2) && use_cyclic) {
882 calc_flag |= CURVE_FIT_CALC_CYCLIC;
883 }
884
885 int result;
886 if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
887 result = curve_fit_cubic_to_points_refit_fl(coords,
888 stroke_len,
889 dims,
890 error_threshold,
891 calc_flag,
892 nullptr,
893 0,
894 corner_angle,
895 &cubic_spline,
896 &cubic_spline_len,
897 nullptr,
898 &corners_index,
899 &corners_index_len);
900 }
901 else {
902 result = curve_fit_cubic_to_points_fl(coords,
903 stroke_len,
904 dims,
905 error_threshold,
906 calc_flag,
907 corners,
908 corners_len,
909 &cubic_spline,
910 &cubic_spline_len,
911 nullptr,
912 &corners_index,
913 &corners_index_len);
914 }
915
916 MEM_freeN(coords);
917 if (corners) {
918 free(corners);
919 }
920
921 if (result == 0) {
922 nu->pntsu = cubic_spline_len;
923 nu->bezt = MEM_calloc_arrayN<BezTriple>(nu->pntsu, __func__);
924
925 float *co = cubic_spline;
926 BezTriple *bezt = nu->bezt;
927 for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
928 const float *handle_l = co + (dims * 0);
929 const float *pt = co + (dims * 1);
930 const float *handle_r = co + (dims * 2);
931
932 copy_v3_v3(bezt->vec[0], handle_l);
933 copy_v3_v3(bezt->vec[1], pt);
934 copy_v3_v3(bezt->vec[2], handle_r);
935
936 if (coords_indices.radius != -1) {
937 bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
938 }
939 else {
940 bezt->radius = radius_max;
941 }
942
943 bezt->h1 = bezt->h2 = HD_ALIGN; /* will set to free in second pass */
944 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
945 }
946
947 if (corners_index) {
948 /* ignore the first and last */
949 uint i_start = 0, i_end = corners_index_len;
950
951 if ((corners_index_len >= 2) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
952 i_start += 1;
953 i_end -= 1;
954 }
955
956 for (uint i = i_start; i < i_end; i++) {
957 bezt = &nu->bezt[corners_index[i]];
958 bezt->h1 = bezt->h2 = HD_FREE;
959 }
960 }
961
962 if (calc_flag & CURVE_FIT_CALC_CYCLIC) {
963 nu->flagu |= CU_NURB_CYCLIC;
964 }
965 }
966
967 if (corners_index) {
968 free(corners_index);
969 }
970
971 if (cubic_spline) {
972 free(cubic_spline);
973 }
974
975#else
976 nu->pntsu = stroke_len;
977 nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
978
979 BezTriple *bezt = nu->bezt;
980
981 {
982 BLI_mempool_iter iter;
983 const struct StrokeElem *selem;
984
986 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
987 copy_v3_v3(bezt->vec[1], selem->location_local);
988 if (!is_3d) {
989 bezt->vec[1][2] = 0.0f;
990 }
991
992 if (use_pressure_radius) {
993 bezt->radius = selem->pressure;
994 }
995 else {
996 bezt->radius = radius_max;
997 }
998
999 bezt->h1 = bezt->h2 = HD_AUTO;
1000
1001 bezt->f1 |= SELECT;
1002 bezt->f2 |= SELECT;
1003 bezt->f3 |= SELECT;
1004
1005 bezt++;
1006 }
1007 }
1008#endif
1009
1011 }
1012 else { /* CU_POLY */
1013 BLI_mempool_iter iter;
1014 const StrokeElem *selem;
1015
1016 nu->pntsu = stroke_len;
1017 nu->pntsv = 1;
1018 nu->type = CU_POLY;
1019 nu->bp = MEM_calloc_arrayN<BPoint>(nu->pntsu, __func__);
1020
1021 /* Misc settings. */
1022 nu->resolu = cu->resolu;
1023 nu->resolv = 1;
1024 nu->orderu = 4;
1025 nu->orderv = 1;
1026
1027 BPoint *bp = nu->bp;
1028
1030 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
1031 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
1032 {
1033 copy_v3_v3(bp->vec, selem->location_local);
1034 if (!is_3d) {
1035 bp->vec[2] = 0.0f;
1036 }
1037
1038 if (use_pressure_radius) {
1039 bp->radius = (selem->pressure * radius_range) + radius_min;
1040 }
1041 else {
1042 bp->radius = cps->radius_max;
1043 }
1044 bp->f1 = SELECT;
1045 bp->vec[3] = 1.0f;
1046
1047 bp++;
1048 }
1049
1051 }
1052
1053 BLI_addtail(nurblist, nu);
1054
1056 cu->actvert = nu->pntsu - 1;
1057
1059 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1060
1061 curve_draw_exit(op);
1062
1063 return OPERATOR_FINISHED;
1064}
1065
1067{
1068 if (RNA_struct_property_is_set(op->ptr, "stroke")) {
1069 return curve_draw_exec(C, op);
1070 }
1071
1072 if (!curve_draw_init(C, op, true)) {
1073 return OPERATOR_CANCELLED;
1074 }
1075
1076 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1077
1079
1080 const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1081
1082 /* Fallback (in case we can't find the depth on first test). */
1083 {
1084 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1085 float center[3];
1086 negate_v3_v3(center, cdd->vc.rv3d->ofs);
1087 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.region, center, mval_fl, cdd->prev.location_world);
1089 }
1090
1094
1095 {
1096 View3D *v3d = cdd->vc.v3d;
1097 RegionView3D *rv3d = cdd->vc.rv3d;
1098 Object *obedit = cdd->vc.obedit;
1099 Curve *cu = static_cast<Curve *>(obedit->data);
1100
1101 const float *plane_no = nullptr;
1102 const float *plane_co = nullptr;
1103
1104 if (CU_IS_2D(cu)) {
1105 /* 2D overrides other options */
1106 plane_co = obedit->object_to_world().location();
1107 plane_no = obedit->object_to_world().ptr()[2];
1108 cdd->project.use_plane = true;
1109 }
1110 else {
1111 if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && (v3d->shading.type > OB_WIRE)) {
1112 /* needed or else the draw matrix can be incorrect */
1114
1117 depth_mode = V3D_DEPTH_SELECTED_ONLY;
1118 }
1119
1121 cdd->vc.region,
1122 cdd->vc.v3d,
1123 nullptr,
1124 depth_mode,
1125 false,
1126 &cdd->depths);
1127
1128 if (cdd->depths != nullptr) {
1129 cdd->project.use_depth = true;
1130 }
1131 else {
1132 BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
1133 cdd->project.use_depth = false;
1134 }
1135 }
1136
1137 /* use view plane (when set or as a fallback when surface can't be found) */
1138 if (cdd->project.use_depth == false) {
1139 plane_co = cdd->vc.scene->cursor.location;
1140 plane_no = rv3d->viewinv[2];
1141 cdd->project.use_plane = true;
1142 }
1143
1144 if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
1145 cdd->sample.use_substeps = true;
1146 }
1147 }
1148
1149 if (cdd->project.use_plane) {
1150 normalize_v3_v3(cdd->project.plane, plane_no);
1151 cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
1152 }
1153 }
1154
1155 if (is_modal == false) {
1156 curve_draw_event_add_first(op, event);
1157 }
1158
1159 /* add temp handler */
1161
1163}
1164
1165static void curve_draw_cancel(bContext * /*C*/, wmOperator *op)
1166{
1167 curve_draw_exit(op);
1168}
1169
1170/* Modal event handling of frame changing */
1172{
1174 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1175
1176 UNUSED_VARS(C, op);
1177
1178 if (event->type == cdd->init_event_type) {
1179 if (event->val == KM_RELEASE) {
1181
1183
1185
1186 curve_draw_exec(C, op);
1187
1188 return OPERATOR_FINISHED;
1189 }
1190 }
1191 else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
1193 curve_draw_cancel(C, op);
1194 return OPERATOR_CANCELLED;
1195 }
1196 else if (ELEM(event->type, LEFTMOUSE)) {
1197 if (event->val == KM_PRESS) {
1198 curve_draw_event_add_first(op, event);
1199 }
1200 }
1201 else if (ISMOUSE_MOTION(event->type)) {
1202 if (cdd->state == CURVE_DRAW_PAINTING) {
1203 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1205 curve_draw_event_add(op, event);
1206 }
1207 }
1208 }
1209
1210 return ret;
1211}
1212
1214{
1215 /* identifiers */
1216 ot->name = "Draw Curve";
1217 ot->idname = "CURVE_OT_draw";
1218 ot->description = "Draw a freehand spline";
1219
1220 /* API callbacks. */
1221 ot->exec = curve_draw_exec;
1222 ot->invoke = curve_draw_invoke;
1223 ot->cancel = curve_draw_cancel;
1224 ot->modal = curve_draw_modal;
1225 ot->poll = ED_operator_editcurve;
1226
1227 /* flags */
1228 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1229
1230 /* properties */
1231 PropertyRNA *prop;
1232
1233 prop = RNA_def_float_distance(ot->srna,
1234 "error_threshold",
1235 0.0f,
1236 0.0f,
1237 10.0f,
1238 "Error",
1239 "Error distance threshold (in object units)",
1240 0.0001f,
1241 10.0f);
1243 RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
1244
1245 RNA_def_enum(ot->srna,
1246 "fit_method",
1249 "Fit Method",
1250 "");
1251
1253 ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
1255
1256 prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
1258
1259 prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1261
1262 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1264}
1265
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_nurb_handles_calc(Nurb *nu)
Definition curve.cc:3957
#define CU_IS_2D(cu)
Definition BKE_curve.hh:89
void BKE_curve_nurb_active_set(Curve *cu, const Nurb *nu)
Definition curve.cc:4976
void BKE_nurb_knot_calc_u(Nurb *nu)
Definition curve.cc:1189
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_kdtree_nd_ free(KDTree *tree)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE float square_f(float a)
MINLINE float interpf(float target, float origin, float t)
#define DEG2RADF(_deg)
#define M_PI
void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL()
void * BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
@ BLI_MEMPOOL_ALLOW_ITER
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1)
void * BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
unsigned int uint
#define UNUSED_VARS(...)
#define ARRAY_SET_ITEMS(...)
#define UNLIKELY(x)
#define ELEM(...)
#define BLT_I18NCONTEXT_AMOUNT
void DEG_id_tag_update(ID *id, unsigned int flags)
@ CU_NURB_CYCLIC
@ CU_SMOOTH
@ CU_3D
@ CU_BEZIER
@ CU_POLY
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
@ OB_WIRE
Object is a sort of wrapper for general info.
@ CURVE_PAINT_PROJECT_SURFACE
@ CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE
@ CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW
@ CURVE_PAINT_FIT_METHOD_REFIT
@ CURVE_PAINT_FIT_METHOD_SPLIT
@ CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS
@ CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS
@ CURVE_PAINT_FLAG_CORNERS_DETECT
@ CURVE_PAINT_FLAG_PRESSURE_RADIUS
@ CURVE_PAINT_FLAG_DEPTH_ONLY_SELECTED
#define UI_SCALE_FAC
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
bool ED_operator_editcurve(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
bool ED_view3d_depth_read_cached_normal(const ARegion *region, const ViewDepths *depths, const int mval[2], float r_normal[3])
void ED_view3d_depths_free(ViewDepths *depths)
eV3DDepthOverrideMode
Definition ED_view3d.hh:188
@ V3D_DEPTH_ALL
Definition ED_view3d.hh:190
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:198
bool ED_view3d_win_to_3d_on_plane(const ARegion *region, const float plane[4], const float mval[2], bool do_clip, float r_out[3])
void view3d_operator_needs_gpu(const bContext *C)
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, GPUBuiltinShader shader_id)
#define GPU_batch_uniform_3fv(batch, name, val)
Definition GPU_batch.hh:278
void GPU_batch_draw(blender::gpu::Batch *batch)
blender::gpu::Batch * GPU_batch_preset_sphere(int lod) ATTR_WARN_UNUSED_RESULT
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void imm_cpack(uint x)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_scale_1f(float factor)
void GPU_matrix_translate_3f(float x, float y, float z)
void GPU_matrix_pop()
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_depth_test(GPUDepthTest test)
Definition gpu_state.cc:68
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
@ PROP_ANGLE
Definition RNA_types.hh:252
#define C
Definition RandGen.cpp:29
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_WIRE
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
BPy_StructRNA * depsgraph
nullptr float
#define SELECT
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:92
#define STROKE_CYCLIC_DIST_PX
static void curve_draw_exec_precalc(wmOperator *op)
static bool stroke_elem_project(const CurveDrawData *cdd, const int mval_i[2], const float mval_fl[2], float surface_offset, const float radius, float r_location_world[3], float r_normal_world[3])
CurveDrawState
@ CURVE_DRAW_IDLE
@ CURVE_DRAW_PAINTING
static wmOperatorStatus curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
#define STROKE_SAMPLE_DIST_MIN_PX
static wmOperatorStatus curve_draw_exec(bContext *C, wmOperator *op)
static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
void CURVE_OT_draw(wmOperatorType *ot)
static void curve_draw_stroke_to_operator_elem(wmOperator *op, const StrokeElem *selem)
static wmOperatorStatus curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void curve_draw_stroke_3d(const bContext *, ARegion *, void *arg)
static void curve_draw_stroke_from_operator_elem(wmOperator *op, PointerRNA *itemptr)
static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
static void curve_draw_stroke_to_operator(wmOperator *op)
static bool stroke_elem_project_fallback_elem(const CurveDrawData *cdd, const float location_fallback_depth[3], StrokeElem *selem)
static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
static void curve_draw_stroke_from_operator(wmOperator *op)
#define STROKE_SAMPLE_DIST_MAX_PX
static void stroke_elem_interp(StrokeElem *selem_out, const StrokeElem *selem_a, const StrokeElem *selem_b, float t)
static bool stroke_elem_project_fallback(const CurveDrawData *cdd, const int mval_i[2], const float mval_fl[2], const float surface_offset, const float radius, const float location_fallback_depth[3], float r_location_world[3], float r_location_local[3], float r_normal_world[3], float r_normal_local[3])
static void curve_draw_cancel(bContext *, wmOperator *op)
static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
static void curve_draw_exit(wmOperator *op)
bool ED_curve_deselect_all_multi(bContext *C)
uint pos
#define ceil
#define sqrt
format
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
vector project(vector v, vector v_proj)
Definition node_math.h:91
return ret
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_collection_is_empty(PointerRNA *ptr, const char *name)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_distance(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
const EnumPropertyItem rna_enum_curve_fit_method_items[]
Definition rna_scene.cc:244
ARegionRuntimeHandle * runtime
uint8_t f1
float vec[4]
float vec[3][3]
struct CurveDrawData::@270127056136216262034353304331261047214266114044 sample
bool use_surface_offset_absolute
ViewDepths * depths
struct CurveDrawData::@077347015340263173243271320324210235330050231347 prev
float location_world[3]
CurveDrawState state
const StrokeElem * selem
float location_world_valid[3]
struct CurveDrawData::@257263115213167121144350234037375062150117026312 project
struct CurveDrawData::@155335004334342007240073166327134202216224024355 radius
BLI_mempool * stroke_elem_pool
short resolv
short resolu
float bevel_radius
Definition DNA_ID.h:414
short flagu
short orderu
short orderv
short flag
short type
BezTriple * bezt
BPoint * bp
short resolu
short resolv
ObjectRuntimeHandle * runtime
float viewinv[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
float normal_world[3]
float normal_local[3]
float location_local[3]
float location_world[3]
struct CurvePaintSettings curve_paint_settings
View3DShading shading
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
Scene * scene
Definition ED_view3d.hh:73
Main * bmain
Definition ED_view3d.hh:67
wmWindow * win
Definition ED_view3d.hh:79
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
Object * obedit
Definition ED_view3d.hh:76
Depsgraph * depsgraph
Definition ED_view3d.hh:72
unsigned short w
Definition ED_view3d.hh:86
double depth_range[2]
Definition ED_view3d.hh:90
unsigned short h
Definition ED_view3d.hh:86
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_PAINT_BRUSH
Definition wm_cursors.hh:34
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_MOTION(event_type)
@ RIGHTMOUSE
@ LEFTMOUSE
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4237