Blender V4.3
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_math_matrix.h"
15#include "BLI_math_rotation.h"
16#include "BLI_mempool.h"
17
18#include "BKE_context.hh"
19#include "BKE_curve.hh"
20#include "BKE_fcurve.hh"
21#include "BKE_object_types.hh"
22#include "BKE_report.hh"
23
24#include "DEG_depsgraph.hh"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "ED_curve.hh"
30#include "ED_screen.hh"
31#include "ED_space_api.hh"
32#include "ED_view3d.hh"
33
34#include "GPU_batch.hh"
35#include "GPU_batch_presets.hh"
36#include "GPU_immediate.hh"
37#include "GPU_immediate_util.hh"
38#include "GPU_matrix.hh"
39#include "GPU_state.hh"
40
41#include "curve_intern.hh"
42
43#include "UI_resources.hh"
44
45#include "RNA_access.hh"
46#include "RNA_define.hh"
47#include "RNA_prototypes.hh"
48
49#include "RNA_enum_types.hh"
50
51#define USE_SPLINE_FIT
52
53#ifdef USE_SPLINE_FIT
54extern "C" {
55# include "curve_fit_nd.h"
56}
57#endif
58
59/* Distance between input samples */
60#define STROKE_SAMPLE_DIST_MIN_PX 1
61#define STROKE_SAMPLE_DIST_MAX_PX 3
62
63/* Distance between start/end points to consider cyclic */
64#define STROKE_CYCLIC_DIST_PX 8
65
66/* -------------------------------------------------------------------- */
69
70struct StrokeElem {
71 float mval[2];
74
75 /* surface normal, may be zero'd */
76 float normal_world[3];
77 float normal_local[3];
78
79 float pressure;
80};
81
86
90
91 /* projecting 2D into 3D space */
92 struct {
93 /* use a plane or project to the surface */
95 float plane[4];
96
97 /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but that's OK. */
99
100 /* offset projection by this value */
102 float offset[3]; /* world-space */
106
107 /* cursor sampling */
108 struct {
109 /* use substeps, needed for nicely interpolating depth */
112
113 struct {
114 float min, max, range;
116
117 struct {
118 float mval[2];
119 /* Used in case we can't calculate the depth. */
121
123
126
130
131 /* StrokeElem */
133
135};
136
137static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
138{
139 const Curve *cu = static_cast<const Curve *>(cdd->vc.obedit->data);
140 return ((pressure * cdd->radius.range) + cdd->radius.min) * cu->bevel_radius;
141}
142
143static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
144{
145 return stroke_elem_radius_from_pressure(cdd, selem->pressure);
146}
147
148static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
149{
150 if ((cdd->project.surface_offset != 0.0f) && !cdd->project.use_surface_offset_absolute &&
151 !is_zero_v3(selem->normal_local))
152 {
153 const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) -
155 madd_v3_v3fl(selem->location_local, selem->normal_local, adjust);
157 selem->location_world, cdd->vc.obedit->object_to_world().ptr(), selem->location_local);
158 }
159 selem->pressure = pressure;
160}
161
162static void stroke_elem_interp(StrokeElem *selem_out,
163 const StrokeElem *selem_a,
164 const StrokeElem *selem_b,
165 float t)
166{
167 interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
168 interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
169 interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
170 selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
171}
172
176static bool stroke_elem_project(const CurveDrawData *cdd,
177 const int mval_i[2],
178 const float mval_fl[2],
179 float surface_offset,
180 const float radius,
181 float r_location_world[3],
182 float r_normal_world[3])
183{
184 ARegion *region = cdd->vc.region;
185
186 bool is_location_world_set = false;
187
188 /* project to 'location_world' */
189 if (cdd->project.use_plane) {
190 /* get the view vector to 'location' */
191 if (ED_view3d_win_to_3d_on_plane(region, cdd->project.plane, mval_fl, true, r_location_world))
192 {
193 if (r_normal_world) {
194 zero_v3(r_normal_world);
195 }
196 is_location_world_set = true;
197 }
198 }
199 else {
200 const ViewDepths *depths = cdd->depths;
201 if (depths && (uint(mval_i[0]) < depths->w) && (uint(mval_i[1]) < depths->h)) {
202 float depth_fl = 1.0f;
203 ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl);
204 const double depth = double(depth_fl);
205 if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
206 if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) {
207 is_location_world_set = true;
208 if (r_normal_world) {
209 zero_v3(r_normal_world);
210 }
211
212 if (surface_offset != 0.0f) {
213 const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius;
214 float normal[3];
215 if (ED_view3d_depth_read_cached_normal(region, depths, mval_i, normal)) {
216 madd_v3_v3fl(r_location_world, normal, offset * surface_offset);
217 if (r_normal_world) {
218 copy_v3_v3(r_normal_world, normal);
219 }
220 }
221 }
222 }
223 }
224 }
225 }
226
227 if (is_location_world_set) {
228 if (cdd->project.use_offset) {
229 add_v3_v3(r_location_world, cdd->project.offset);
230 }
231 }
232
233 return is_location_world_set;
234}
235
237 const int mval_i[2],
238 const float mval_fl[2],
239 const float surface_offset,
240 const float radius,
241 const float location_fallback_depth[3],
242 float r_location_world[3],
243 float r_location_local[3],
244 float r_normal_world[3],
245 float r_normal_local[3])
246{
247 bool is_depth_found = stroke_elem_project(
248 cdd, mval_i, mval_fl, surface_offset, radius, r_location_world, r_normal_world);
249 if (is_depth_found == false) {
251 cdd->vc.v3d, cdd->vc.region, location_fallback_depth, mval_fl, r_location_world);
252 zero_v3(r_normal_local);
253 }
254 mul_v3_m4v3(r_location_local, cdd->vc.obedit->world_to_object().ptr(), r_location_world);
255
256 if (!is_zero_v3(r_normal_world)) {
257 copy_v3_v3(r_normal_local, r_normal_world);
258 mul_transposed_mat3_m4_v3(cdd->vc.obedit->object_to_world().ptr(), r_normal_local);
259 normalize_v3(r_normal_local);
260 }
261 else {
262 zero_v3(r_normal_local);
263 }
264
265 return is_depth_found;
266}
267
272 const float location_fallback_depth[3],
273 StrokeElem *selem)
274{
275 const int mval_i[2] = {int(selem->mval[0]), int(selem->mval[1])};
276 const float radius = stroke_elem_radius(cdd, selem);
278 mval_i,
279 selem->mval,
281 radius,
282 location_fallback_depth,
283 selem->location_world,
284 selem->location_local,
285 selem->normal_world,
286 selem->normal_local);
287}
288
290
291/* -------------------------------------------------------------------- */
294
296{
297 PointerRNA itemptr;
298 RNA_collection_add(op->ptr, "stroke", &itemptr);
299
300 RNA_float_set_array(&itemptr, "mouse", selem->mval);
301 RNA_float_set_array(&itemptr, "location", selem->location_world);
302 RNA_float_set(&itemptr, "pressure", selem->pressure);
303}
304
306{
307 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
308
309 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
310
311 RNA_float_get_array(itemptr, "mouse", selem->mval);
312 RNA_float_get_array(itemptr, "location", selem->location_world);
314 selem->location_local, cdd->vc.obedit->world_to_object().ptr(), selem->location_world);
315 selem->pressure = RNA_float_get(itemptr, "pressure");
316}
317
319{
320 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
321
322 BLI_mempool_iter iter;
323 const StrokeElem *selem;
324
326 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
327 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
328 {
330 }
331}
332
334{
335 RNA_BEGIN (op->ptr, itemptr, "stroke") {
337 }
338 RNA_END;
339}
340
342
343/* -------------------------------------------------------------------- */
346
347static void curve_draw_stroke_3d(const bContext * /*C*/, ARegion * /*region*/, void *arg)
348{
349 wmOperator *op = static_cast<wmOperator *>(arg);
350 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
351
352 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
353
354 if (stroke_len == 0) {
355 return;
356 }
357
358 Object *obedit = cdd->vc.obedit;
359 Curve *cu = static_cast<Curve *>(obedit->data);
360
361 if (cu->bevel_radius > 0.0f) {
362 BLI_mempool_iter iter;
363 const StrokeElem *selem;
364
365 const float location_zero[3] = {0};
366 const float *location_prev = location_zero;
367
368 float color[3];
370
371 blender::gpu::Batch *sphere = GPU_batch_preset_sphere(0);
373 GPU_batch_uniform_3fv(sphere, "color", color);
374
375 /* scale to edit-mode space */
377 GPU_matrix_mul(obedit->object_to_world().ptr());
378
380 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
381 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
382 {
383 GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0],
384 selem->location_local[1] - location_prev[1],
385 selem->location_local[2] - location_prev[2]);
386
387 const float radius = stroke_elem_radius(cdd, selem);
388
390 GPU_matrix_scale_1f(radius);
391 GPU_batch_draw(sphere);
393
394 location_prev = selem->location_local;
395 }
396
398 }
399
400 if (stroke_len > 1) {
401 float(*coord_array)[3] = static_cast<float(*)[3]>(
402 MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__));
403
404 {
405 BLI_mempool_iter iter;
406 const StrokeElem *selem;
407 int i;
409 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i = 0; selem;
410 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
411 {
412 copy_v3_v3(coord_array[i], selem->location_world);
413 }
414 }
415
416 {
420
423 GPU_line_smooth(true);
424 GPU_line_width(3.0f);
425
426 imm_cpack(0x0);
427 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
428 for (int i = 0; i < stroke_len; i++) {
429 immVertex3fv(pos, coord_array[i]);
430 }
431 immEnd();
432
433 GPU_line_width(1.0f);
434
435 imm_cpack(0xffffffff);
436 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
437 for (int i = 0; i < stroke_len; i++) {
438 immVertex3fv(pos, coord_array[i]);
439 }
440 immEnd();
441
442 /* Reset defaults */
445 GPU_line_smooth(false);
446
448 }
449
450 MEM_freeN(coord_array);
451 }
452}
453
454static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
455{
456 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
457 Object *obedit = cdd->vc.obedit;
458
459 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
460
461 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
462
463 ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
464
465 /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */
466 selem->pressure = event->tablet.pressure;
467
468 bool is_depth_found = stroke_elem_project_fallback_elem(
469 cdd, cdd->prev.location_world_valid, selem);
470
471 if (is_depth_found) {
472 /* use the depth if a fallback wasn't used */
474 }
476
477 float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval);
478 copy_v2_v2(cdd->prev.mval, selem->mval);
479
480 if (cdd->sample.use_substeps && cdd->prev.selem) {
481 const StrokeElem selem_target = *selem;
482 StrokeElem *selem_new_last = selem;
483 if (len_sq >= square_f(STROKE_SAMPLE_DIST_MAX_PX)) {
484 int n = int(ceil(sqrt(double(len_sq)))) / STROKE_SAMPLE_DIST_MAX_PX;
485
486 for (int i = 1; i < n; i++) {
487 StrokeElem *selem_new = selem_new_last;
488 stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, float(i) / n);
489
490 const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
491 cdd, cdd->prev.location_world_valid, selem_new);
492 if (is_depth_found == false) {
493 if (is_depth_found_substep) {
495 }
496 }
497
498 selem_new_last = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
499 }
500 }
501 selem = selem_new_last;
502 *selem_new_last = selem_target;
503 }
504
505 cdd->prev.selem = selem;
506
508}
509
510static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
511{
512 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
514
515 /* add first point */
516 curve_draw_event_add(op, event);
517
520 {
521 RegionView3D *rv3d = cdd->vc.rv3d;
522
523 cdd->project.use_depth = false;
524 cdd->project.use_plane = true;
525
526 float normal[3] = {0.0f};
527 if (ELEM(cps->surface_plane,
530 {
531 if (ED_view3d_depth_read_cached_normal(cdd->vc.region, cdd->depths, event->mval, normal)) {
533 float cross_a[3], cross_b[3];
534 cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
535 cross_v3_v3v3(cross_b, normal, cross_a);
536 copy_v3_v3(normal, cross_b);
537 }
538 }
539 }
540
541 /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
542 if (is_zero_v3(normal)) {
543 copy_v3_v3(normal, rv3d->viewinv[2]);
544 }
545
546 normalize_v3_v3(cdd->project.plane, normal);
548
549 /* Special case for when we only have offset applied on the first-hit,
550 * the remaining stroke must be offset too. */
551 if (cdd->project.surface_offset != 0.0f) {
552 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
553
554 float location_no_offset[3];
555
556 if (stroke_elem_project(cdd, event->mval, mval_fl, 0.0f, 0.0f, location_no_offset, nullptr))
557 {
558 sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
559 if (!is_zero_v3(cdd->project.offset)) {
560 cdd->project.use_offset = true;
561 }
562 }
563 }
564 /* end special case */
565 }
566
567 cdd->init_event_type = event->type;
569}
570
571static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
572{
573 BLI_assert(op->customdata == nullptr);
574
575 CurveDrawData *cdd = static_cast<CurveDrawData *>(MEM_callocN(sizeof(*cdd), __func__));
577
578 if (is_invoke) {
580 if (ELEM(nullptr, cdd->vc.region, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
581 MEM_freeN(cdd);
582 BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport");
583 return false;
584 }
585 }
586 else {
587 cdd->vc.bmain = CTX_data_main(C);
588 cdd->vc.depsgraph = depsgraph;
589 cdd->vc.scene = CTX_data_scene(C);
592
593 /* Using an empty stroke complicates logic later,
594 * it's simplest to disallow early on (see: #94085). */
595 if (RNA_collection_is_empty(op->ptr, "stroke")) {
596 MEM_freeN(cdd);
597 BKE_report(op->reports, RPT_ERROR, "The \"stroke\" cannot be empty");
598 return false;
599 }
600 }
601
602 op->customdata = cdd;
603
605
606 cdd->curve_type = cps->curve_type;
607
608 cdd->radius.min = cps->radius_min;
609 cdd->radius.max = cps->radius_max;
610 cdd->radius.range = cps->radius_max - cps->radius_min;
614
616
617 return true;
618}
619
621{
622 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
623 if (cdd) {
624 if (cdd->draw_handle_view) {
627 }
628
629 if (cdd->stroke_elem_pool) {
631 }
632
633 if (cdd->depths) {
635 }
636 MEM_freeN(cdd);
637 op->customdata = nullptr;
638 }
639}
640
645{
646 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
648 PropertyRNA *prop;
649
650 prop = RNA_struct_find_property(op->ptr, "fit_method");
651 if (!RNA_property_is_set(op->ptr, prop)) {
652 RNA_property_enum_set(op->ptr, prop, cps->fit_method);
653 }
654
655 prop = RNA_struct_find_property(op->ptr, "corner_angle");
656 if (!RNA_property_is_set(op->ptr, prop)) {
657 const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle :
658 float(M_PI);
659 RNA_property_float_set(op->ptr, prop, corner_angle);
660 }
661
662 prop = RNA_struct_find_property(op->ptr, "error_threshold");
663 if (!RNA_property_is_set(op->ptr, prop)) {
664
665 /* Error isn't set so we'll have to calculate it from the pixel values. */
666 BLI_mempool_iter iter;
667 const StrokeElem *selem, *selem_prev;
668
669 float len_3d = 0.0f, len_2d = 0.0f;
670 float scale_px; /* pixel to local space scale */
671
672 int i = 0;
674 selem_prev = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
675 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
676 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
677 {
678 len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
679 len_2d += len_v2v2(selem->mval, selem_prev->mval);
680 selem_prev = selem;
681 }
682 scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
683 float error_threshold = (cps->error_threshold * UI_SCALE_FAC) * scale_px;
684 RNA_property_float_set(op->ptr, prop, error_threshold);
685 }
686
687 prop = RNA_struct_find_property(op->ptr, "use_cyclic");
688 if (!RNA_property_is_set(op->ptr, prop)) {
689 bool use_cyclic = false;
690
691 if (BLI_mempool_len(cdd->stroke_elem_pool) > 2) {
692 BLI_mempool_iter iter;
693 const StrokeElem *selem, *selem_first, *selem_last;
694
696 selem_first = selem_last = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
697 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
698 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
699 {
700 selem_last = selem;
701 }
702
703 if (len_squared_v2v2(selem_first->mval, selem_last->mval) <=
705 {
706 use_cyclic = true;
707 }
708 }
709
710 RNA_property_boolean_set(op->ptr, prop, use_cyclic);
711 }
712
713 if ((cps->radius_taper_start != 0.0f) || (cps->radius_taper_end != 0.0f)) {
714 /* NOTE: we could try to de-duplicate the length calculations above. */
715 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
716
717 BLI_mempool_iter iter;
718 StrokeElem *selem, *selem_prev;
719
720 float *lengths = static_cast<float *>(MEM_mallocN(sizeof(float) * stroke_len, __func__));
721 StrokeElem **selem_array = static_cast<StrokeElem **>(
722 MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__));
723 lengths[0] = 0.0f;
724
725 float len_3d = 0.0f;
726
727 int i = 1;
729 selem_prev = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter));
730 selem_array[0] = selem_prev;
731 for (selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
732 selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
733 {
734 const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
735 len_3d += len_3d_segment;
736 lengths[i] = len_3d;
737 selem_array[i] = selem;
738 selem_prev = selem;
739 }
740
741 if (cps->radius_taper_start != 0.0f) {
742 const float len_taper_max = cps->radius_taper_start * len_3d;
743 for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) {
744 const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max);
745 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
746 }
747 }
748
749 if (cps->radius_taper_end != 0.0f) {
750 const float len_taper_max = cps->radius_taper_end * len_3d;
751 const float len_taper_min = len_3d - len_taper_max;
752 for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) {
753 const float pressure_new = selem_array[i]->pressure *
754 ((len_3d - lengths[i]) / len_taper_max);
755 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
756 }
757 }
758
759 MEM_freeN(lengths);
760 MEM_freeN(selem_array);
761 }
762}
763
765{
766 if (op->customdata == nullptr) {
767 if (!curve_draw_init(C, op, false)) {
768 return OPERATOR_CANCELLED;
769 }
770 }
771
772 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
773
775 Object *obedit = cdd->vc.obedit;
776 Curve *cu = static_cast<Curve *>(obedit->data);
777 ListBase *nurblist = object_editcurve_get(obedit);
778
779 int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
780
781 const bool is_3d = (cu->flag & CU_3D) != 0;
782 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
783
784 if (BLI_mempool_len(cdd->stroke_elem_pool) == 0) {
786 stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
787 }
788
789 /* Deselect all existing curves. */
791
792 const float radius_min = cps->radius_min;
793 const float radius_max = cps->radius_max;
794 const float radius_range = cps->radius_max - cps->radius_min;
795
796 Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__));
797 nu->pntsv = 0;
798 nu->resolu = cu->resolu;
799 nu->resolv = cu->resolv;
800 nu->flag |= CU_SMOOTH;
801
802 const bool use_pressure_radius = (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
803 ((cps->radius_taper_start != 0.0f) ||
804 (cps->radius_taper_end != 0.0f));
805
806 if (cdd->curve_type == CU_BEZIER) {
807 nu->type = CU_BEZIER;
808
809#ifdef USE_SPLINE_FIT
810
811 /* Allow to interpolate multiple channels */
812 int dims = 3;
813 struct {
814 int radius;
815 } coords_indices;
816 coords_indices.radius = use_pressure_radius ? dims++ : -1;
817
818 float *coords = static_cast<float *>(
819 MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__));
820
821 float *cubic_spline = nullptr;
822 uint cubic_spline_len = 0;
823
824 /* error in object local space */
825 const int fit_method = RNA_enum_get(op->ptr, "fit_method");
826 const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
827 const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
828 const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
829
830 {
831 BLI_mempool_iter iter;
832 const StrokeElem *selem;
833 float *co = coords;
834
836 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
837 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), co += dims)
838 {
839 copy_v3_v3(co, selem->location_local);
840 if (coords_indices.radius != -1) {
841 co[coords_indices.radius] = selem->pressure;
842 }
843
844 /* remove doubles */
845 if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) {
846 co -= dims;
847 stroke_len--;
848 }
849 }
850 }
851
852 uint *corners = nullptr;
853 uint corners_len = 0;
854
855 if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < float(M_PI))) {
856 /* this could be configurable... */
857 const float corner_radius_min = error_threshold / 8;
858 const float corner_radius_max = error_threshold * 2;
859 const uint samples_max = 16;
860
861 curve_fit_corners_detect_fl(coords,
862 stroke_len,
863 dims,
864 corner_radius_min,
865 corner_radius_max,
866 samples_max,
867 corner_angle,
868 &corners,
869 &corners_len);
870 }
871
872 uint *corners_index = nullptr;
873 uint corners_index_len = 0;
874 uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
875
876 if ((stroke_len > 2) && use_cyclic) {
877 calc_flag |= CURVE_FIT_CALC_CYCLIC;
878 }
879
880 int result;
881 if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
882 result = curve_fit_cubic_to_points_refit_fl(coords,
883 stroke_len,
884 dims,
885 error_threshold,
886 calc_flag,
887 nullptr,
888 0,
889 corner_angle,
890 &cubic_spline,
891 &cubic_spline_len,
892 nullptr,
893 &corners_index,
894 &corners_index_len);
895 }
896 else {
897 result = curve_fit_cubic_to_points_fl(coords,
898 stroke_len,
899 dims,
900 error_threshold,
901 calc_flag,
902 corners,
903 corners_len,
904 &cubic_spline,
905 &cubic_spline_len,
906 nullptr,
907 &corners_index,
908 &corners_index_len);
909 }
910
911 MEM_freeN(coords);
912 if (corners) {
913 free(corners);
914 }
915
916 if (result == 0) {
917 nu->pntsu = cubic_spline_len;
918 nu->bezt = static_cast<BezTriple *>(MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__));
919
920 float *co = cubic_spline;
921 BezTriple *bezt = nu->bezt;
922 for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
923 const float *handle_l = co + (dims * 0);
924 const float *pt = co + (dims * 1);
925 const float *handle_r = co + (dims * 2);
926
927 copy_v3_v3(bezt->vec[0], handle_l);
928 copy_v3_v3(bezt->vec[1], pt);
929 copy_v3_v3(bezt->vec[2], handle_r);
930
931 if (coords_indices.radius != -1) {
932 bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
933 }
934 else {
935 bezt->radius = radius_max;
936 }
937
938 bezt->h1 = bezt->h2 = HD_ALIGN; /* will set to free in second pass */
939 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
940 }
941
942 if (corners_index) {
943 /* ignore the first and last */
944 uint i_start = 0, i_end = corners_index_len;
945
946 if ((corners_index_len >= 2) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
947 i_start += 1;
948 i_end -= 1;
949 }
950
951 for (uint i = i_start; i < i_end; i++) {
952 bezt = &nu->bezt[corners_index[i]];
953 bezt->h1 = bezt->h2 = HD_FREE;
954 }
955 }
956
957 if (calc_flag & CURVE_FIT_CALC_CYCLIC) {
958 nu->flagu |= CU_NURB_CYCLIC;
959 }
960 }
961
962 if (corners_index) {
963 free(corners_index);
964 }
965
966 if (cubic_spline) {
967 free(cubic_spline);
968 }
969
970#else
971 nu->pntsu = stroke_len;
972 nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
973
974 BezTriple *bezt = nu->bezt;
975
976 {
977 BLI_mempool_iter iter;
978 const struct StrokeElem *selem;
979
981 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
982 copy_v3_v3(bezt->vec[1], selem->location_local);
983 if (!is_3d) {
984 bezt->vec[1][2] = 0.0f;
985 }
986
987 if (use_pressure_radius) {
988 bezt->radius = selem->pressure;
989 }
990 else {
991 bezt->radius = radius_max;
992 }
993
994 bezt->h1 = bezt->h2 = HD_AUTO;
995
996 bezt->f1 |= SELECT;
997 bezt->f2 |= SELECT;
998 bezt->f3 |= SELECT;
999
1000 bezt++;
1001 }
1002 }
1003#endif
1004
1006 }
1007 else { /* CU_POLY */
1008 BLI_mempool_iter iter;
1009 const StrokeElem *selem;
1010
1011 nu->pntsu = stroke_len;
1012 nu->pntsv = 1;
1013 nu->type = CU_POLY;
1014 nu->bp = static_cast<BPoint *>(MEM_callocN(nu->pntsu * sizeof(BPoint), __func__));
1015
1016 /* Misc settings. */
1017 nu->resolu = cu->resolu;
1018 nu->resolv = 1;
1019 nu->orderu = 4;
1020 nu->orderv = 1;
1021
1022 BPoint *bp = nu->bp;
1023
1025 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
1026 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
1027 {
1028 copy_v3_v3(bp->vec, selem->location_local);
1029 if (!is_3d) {
1030 bp->vec[2] = 0.0f;
1031 }
1032
1033 if (use_pressure_radius) {
1034 bp->radius = (selem->pressure * radius_range) + radius_min;
1035 }
1036 else {
1037 bp->radius = cps->radius_max;
1038 }
1039 bp->f1 = SELECT;
1040 bp->vec[3] = 1.0f;
1041
1042 bp++;
1043 }
1044
1046 }
1047
1048 BLI_addtail(nurblist, nu);
1049
1051 cu->actvert = nu->pntsu - 1;
1052
1054 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1055
1056 curve_draw_exit(op);
1057
1058 return OPERATOR_FINISHED;
1059}
1060
1061static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1062{
1063 if (RNA_struct_property_is_set(op->ptr, "stroke")) {
1064 return curve_draw_exec(C, op);
1065 }
1066
1067 if (!curve_draw_init(C, op, true)) {
1068 return OPERATOR_CANCELLED;
1069 }
1070
1071 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1072
1074
1075 const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1076
1077 /* Fallback (in case we can't find the depth on first test). */
1078 {
1079 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1080 float center[3];
1081 negate_v3_v3(center, cdd->vc.rv3d->ofs);
1082 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.region, center, mval_fl, cdd->prev.location_world);
1084 }
1085
1089
1090 {
1091 View3D *v3d = cdd->vc.v3d;
1092 RegionView3D *rv3d = cdd->vc.rv3d;
1093 Object *obedit = cdd->vc.obedit;
1094 Curve *cu = static_cast<Curve *>(obedit->data);
1095
1096 const float *plane_no = nullptr;
1097 const float *plane_co = nullptr;
1098
1099 if (CU_IS_2D(cu)) {
1100 /* 2D overrides other options */
1101 plane_co = obedit->object_to_world().location();
1102 plane_no = obedit->object_to_world().ptr()[2];
1103 cdd->project.use_plane = true;
1104 }
1105 else {
1106 if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && (v3d->shading.type > OB_WIRE)) {
1107 /* needed or else the draw matrix can be incorrect */
1109
1112 depth_mode = V3D_DEPTH_SELECTED_ONLY;
1113 }
1114
1116 cdd->vc.region,
1117 cdd->vc.v3d,
1118 nullptr,
1119 depth_mode,
1120 false,
1121 &cdd->depths);
1122
1123 if (cdd->depths != nullptr) {
1124 cdd->project.use_depth = true;
1125 }
1126 else {
1127 BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
1128 cdd->project.use_depth = false;
1129 }
1130 }
1131
1132 /* use view plane (when set or as fallback when surface can't be found) */
1133 if (cdd->project.use_depth == false) {
1134 plane_co = cdd->vc.scene->cursor.location;
1135 plane_no = rv3d->viewinv[2];
1136 cdd->project.use_plane = true;
1137 }
1138
1139 if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
1140 cdd->sample.use_substeps = true;
1141 }
1142 }
1143
1144 if (cdd->project.use_plane) {
1145 normalize_v3_v3(cdd->project.plane, plane_no);
1146 cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
1147 }
1148 }
1149
1150 if (is_modal == false) {
1151 curve_draw_event_add_first(op, event);
1152 }
1153
1154 /* add temp handler */
1156
1158}
1159
1160static void curve_draw_cancel(bContext * /*C*/, wmOperator *op)
1161{
1162 curve_draw_exit(op);
1163}
1164
1165/* Modal event handling of frame changing */
1166static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
1167{
1169 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1170
1171 UNUSED_VARS(C, op);
1172
1173 if (event->type == cdd->init_event_type) {
1174 if (event->val == KM_RELEASE) {
1176
1178
1180
1181 curve_draw_exec(C, op);
1182
1183 return OPERATOR_FINISHED;
1184 }
1185 }
1186 else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
1188 curve_draw_cancel(C, op);
1189 return OPERATOR_CANCELLED;
1190 }
1191 else if (ELEM(event->type, LEFTMOUSE)) {
1192 if (event->val == KM_PRESS) {
1193 curve_draw_event_add_first(op, event);
1194 }
1195 }
1196 else if (ISMOUSE_MOTION(event->type)) {
1197 if (cdd->state == CURVE_DRAW_PAINTING) {
1198 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1200 curve_draw_event_add(op, event);
1201 }
1202 }
1203 }
1204
1205 return ret;
1206}
1207
1209{
1210 /* identifiers */
1211 ot->name = "Draw Curve";
1212 ot->idname = "CURVE_OT_draw";
1213 ot->description = "Draw a freehand spline";
1214
1215 /* api callbacks */
1216 ot->exec = curve_draw_exec;
1217 ot->invoke = curve_draw_invoke;
1218 ot->cancel = curve_draw_cancel;
1219 ot->modal = curve_draw_modal;
1220 ot->poll = ED_operator_editcurve;
1221
1222 /* flags */
1223 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1224
1225 /* properties */
1226 PropertyRNA *prop;
1227
1228 prop = RNA_def_float_distance(ot->srna,
1229 "error_threshold",
1230 0.0f,
1231 0.0f,
1232 10.0f,
1233 "Error",
1234 "Error distance threshold (in object units)",
1235 0.0001f,
1236 10.0f);
1237 RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
1238
1239 RNA_def_enum(ot->srna,
1240 "fit_method",
1243 "Fit Method",
1244 "");
1245
1247 ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
1249
1250 prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
1252
1253 prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1255
1256 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1258}
1259
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:3955
#define CU_IS_2D(cu)
Definition BKE_curve.hh:86
void BKE_curve_nurb_active_set(Curve *cu, const Nurb *nu)
Definition curve.cc:4983
void BKE_nurb_knot_calc_u(Nurb *nu)
Definition curve.cc:1181
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
sqrt(x)+1/max(0
void BLI_kdtree_nd_ free(KDTree *tree)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
MINLINE float square_f(float a)
MINLINE float interpf(float target, float origin, float t)
#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])
#define DEG2RADF(_deg)
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)
Definition math_vector.c:21
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)
Definition math_vector.c:36
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])
@ BLI_MEMPOOL_ALLOW_ITER
Definition BLI_mempool.h:95
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 * 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(...)
typedef double(DMatrix)[4][4]
void DEG_id_tag_update(ID *id, unsigned int flags)
@ CU_BEZIER
@ CU_POLY
@ CU_3D
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
@ CU_SMOOTH
@ CU_NURB_CYCLIC
@ OB_WIRE
Object is a sort of wrapper for general info.
@ 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
@ CURVE_PAINT_FIT_METHOD_REFIT
@ CURVE_PAINT_FIT_METHOD_SPLIT
@ CURVE_PAINT_PROJECT_SURFACE
@ CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE
@ CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW
#define UI_SCALE_FAC
@ OPERATOR_RUNNING_MODAL
bool ED_operator_editcurve(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
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 view3d_operator_needs_opengl(const bContext *C)
void ED_view3d_depths_free(ViewDepths *depths)
eV3DDepthOverrideMode
Definition ED_view3d.hh:184
@ V3D_DEPTH_ALL
Definition ED_view3d.hh:186
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:194
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])
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
#define GPU_batch_uniform_3fv(batch, name, val)
Definition GPU_batch.hh:306
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, eGPUBuiltinShader shader_id)
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(eGPUBuiltinShader 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_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:161
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:111
@ GPU_DEPTH_NONE
Definition GPU_state.hh:108
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_ANGLE
Definition RNA_types.hh:155
#define C
Definition RandGen.cpp:29
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_WIRE
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
BPy_StructRNA * depsgraph
#define SELECT
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:88
#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 int curve_draw_exec(bContext *C, wmOperator *op)
#define STROKE_SAMPLE_DIST_MIN_PX
static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 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 int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
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)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float3 ceil(const float3 a)
vector project(vector v, vector v_proj)
Definition node_math.h:59
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_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:250
struct ARegionType * type
uint8_t f1
float vec[4]
float vec[3][3]
bool use_surface_offset_absolute
ViewDepths * depths
struct CurveDrawData::@122053357257022202372332306253266340050352207025 prev
float location_world[3]
struct CurveDrawData::@245323005260216252031355354310256052310253267000 project
struct CurveDrawData::@312353161277223354351054131017041237303040206154 radius
CurveDrawState state
const StrokeElem * selem
float location_world_valid[3]
struct CurveDrawData::@306341371227123352063203344165060313364051346167 sample
BLI_mempool * stroke_elem_pool
short resolv
short resolu
float bevel_radius
Definition DNA_ID.h:413
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:76
ARegion * region
Definition ED_view3d.hh:73
Scene * scene
Definition ED_view3d.hh:69
Main * bmain
Definition ED_view3d.hh:63
wmWindow * win
Definition ED_view3d.hh:75
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
unsigned short w
Definition ED_view3d.hh:82
double depth_range[2]
Definition ED_view3d.hh:86
unsigned short h
Definition ED_view3d.hh:82
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
struct ReportList * reports
struct PointerRNA * ptr
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_PAINT_BRUSH
Definition wm_cursors.hh:33
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:4125