Blender V5.0
curves_draw.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
5#include "DNA_curve_types.h"
6
7#include "BLI_math_matrix.h"
8#include "BLI_math_rotation.h"
9#include "BLI_math_vector.h"
10#include "BLI_mempool.h"
11
12#include "BLT_translation.hh"
13
14#include "BKE_attribute.hh"
15#include "BKE_context.hh"
16#include "BKE_curves.hh"
17#include "BKE_object_types.hh"
18#include "BKE_report.hh"
19#include "BKE_screen.hh"
20
21#include "DEG_depsgraph.hh"
22
23#include "WM_api.hh"
24
25#include "ED_curves.hh"
26#include "ED_screen.hh"
27#include "ED_space_api.hh"
28#include "ED_view3d.hh"
29
30#include "GPU_batch.hh"
31#include "GPU_batch_presets.hh"
32#include "GPU_immediate.hh"
33#include "GPU_immediate_util.hh"
34#include "GPU_matrix.hh"
35#include "GPU_state.hh"
36
37#include "UI_resources.hh"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41#include "RNA_enum_types.hh"
42#include "RNA_prototypes.hh"
43
44extern "C" {
45#include "curve_fit_nd.h"
46}
47
48namespace blender::ed::curves {
49
50/* Distance between input samples */
51#define STROKE_SAMPLE_DIST_MIN_PX 1
52#define STROKE_SAMPLE_DIST_MAX_PX 3
53
54/* Distance between start/end points to consider cyclic */
55#define STROKE_CYCLIC_DIST_PX 8
56
57/* -------------------------------------------------------------------- */
60
61struct StrokeElem {
62 float mval[2];
65
66 /* Surface normal, may be zeroed. */
67 float normal_world[3];
68 float normal_local[3];
69
70 float pressure;
71};
72
77
83
84 /* projecting 2D into 3D space */
85 struct {
86 /* use a plane or project to the surface */
88 float plane[4];
89
90 /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but that's OK. */
92
93 /* offset projection by this value */
95 float offset[3]; /* world-space */
99
100 /* cursor sampling */
101 struct {
102 /* use substeps, needed for nicely interpolating depth */
105
106 struct {
107 float min, max, range;
109
110 struct {
111 float mval[2];
112 /* Used in case we can't calculate the depth. */
114
116
119
123
124 /* StrokeElem */
126
128};
129
130static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
131{
132 return ((pressure * cdd->radius.range) + cdd->radius.min) * cdd->bevel_radius;
133}
134
135static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
136{
137 return stroke_elem_radius_from_pressure(cdd, selem->pressure);
138}
139
140static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
141{
142 if ((cdd->project.surface_offset != 0.0f) && !cdd->project.use_surface_offset_absolute &&
143 !is_zero_v3(selem->normal_local))
144 {
145 const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) -
147 madd_v3_v3fl(selem->location_local, selem->normal_local, adjust);
149 selem->location_world, cdd->vc.obedit->object_to_world().ptr(), selem->location_local);
150 }
151 selem->pressure = pressure;
152}
153
154static void stroke_elem_interp(StrokeElem *selem_out,
155 const StrokeElem *selem_a,
156 const StrokeElem *selem_b,
157 float t)
158{
159 interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
160 interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
161 interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
162 selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
163}
164
168static bool stroke_elem_project(const CurveDrawData *cdd,
169 const int mval_i[2],
170 const float mval_fl[2],
171 float surface_offset,
172 const float radius,
173 float r_location_world[3],
174 float r_normal_world[3])
175{
176 ARegion *region = cdd->vc.region;
177
178 bool is_location_world_set = false;
179
180 /* project to 'location_world' */
181 if (cdd->project.use_plane) {
182 /* get the view vector to 'location' */
183 if (ED_view3d_win_to_3d_on_plane(region, cdd->project.plane, mval_fl, true, r_location_world))
184 {
185 if (r_normal_world) {
186 zero_v3(r_normal_world);
187 }
188 is_location_world_set = true;
189 }
190 }
191 else {
192 const ViewDepths *depths = cdd->depths;
193 if (depths && (uint(mval_i[0]) < depths->w) && (uint(mval_i[1]) < depths->h)) {
194 float depth_fl = 1.0f;
195 ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl);
196 const double depth = double(depth_fl);
197 if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
198 if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) {
199 is_location_world_set = true;
200 if (r_normal_world) {
201 zero_v3(r_normal_world);
202 }
203
204 if (surface_offset != 0.0f) {
205 const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius;
206 float normal[3];
207 if (ED_view3d_depth_read_cached_normal(region, depths, mval_i, normal)) {
208 madd_v3_v3fl(r_location_world, normal, offset * surface_offset);
209 if (r_normal_world) {
210 copy_v3_v3(r_normal_world, normal);
211 }
212 }
213 }
214 }
215 }
216 }
217 }
218
219 if (is_location_world_set) {
220 if (cdd->project.use_offset) {
221 add_v3_v3(r_location_world, cdd->project.offset);
222 }
223 }
224
225 return is_location_world_set;
226}
227
229 const int mval_i[2],
230 const float mval_fl[2],
231 const float surface_offset,
232 const float radius,
233 const float location_fallback_depth[3],
234 float r_location_world[3],
235 float r_location_local[3],
236 float r_normal_world[3],
237 float r_normal_local[3])
238{
239 bool is_depth_found = stroke_elem_project(
240 cdd, mval_i, mval_fl, surface_offset, radius, r_location_world, r_normal_world);
241 if (is_depth_found == false) {
243 cdd->vc.v3d, cdd->vc.region, location_fallback_depth, mval_fl, r_location_world);
244 zero_v3(r_normal_local);
245 }
246 mul_v3_m4v3(r_location_local, cdd->vc.obedit->world_to_object().ptr(), r_location_world);
247
248 if (!is_zero_v3(r_normal_world)) {
249 copy_v3_v3(r_normal_local, r_normal_world);
250 mul_transposed_mat3_m4_v3(cdd->vc.obedit->object_to_world().ptr(), r_normal_local);
251 normalize_v3(r_normal_local);
252 }
253 else {
254 zero_v3(r_normal_local);
255 }
256
257 return is_depth_found;
258}
259
264 const float location_fallback_depth[3],
265 StrokeElem *selem)
266{
267 const int mval_i[2] = {int(selem->mval[0]), int(selem->mval[1])};
268 const float radius = stroke_elem_radius(cdd, selem);
270 mval_i,
271 selem->mval,
273 radius,
274 location_fallback_depth,
275 selem->location_world,
276 selem->location_local,
277 selem->normal_world,
278 selem->normal_local);
279}
280
282
283/* -------------------------------------------------------------------- */
286
288{
289 PointerRNA itemptr;
290 RNA_collection_add(op->ptr, "stroke", &itemptr);
291
292 RNA_float_set_array(&itemptr, "mouse", selem->mval);
293 RNA_float_set_array(&itemptr, "location", selem->location_world);
294 RNA_float_set(&itemptr, "pressure", selem->pressure);
295}
296
298{
299 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
300
301 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
302
303 RNA_float_get_array(itemptr, "mouse", selem->mval);
304 RNA_float_get_array(itemptr, "location", selem->location_world);
306 selem->location_local, cdd->vc.obedit->world_to_object().ptr(), selem->location_world);
307 selem->pressure = RNA_float_get(itemptr, "pressure");
308}
309
311{
312 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
313
314 BLI_mempool_iter iter;
315 const StrokeElem *selem;
316
318 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
319 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
320 {
322 }
323}
324
326{
327 RNA_BEGIN (op->ptr, itemptr, "stroke") {
329 }
330 RNA_END;
331}
332
334
335static void curve_draw_stroke_3d(const bContext * /*C*/, ARegion * /*region*/, void *arg)
336{
337 wmOperator *op = static_cast<wmOperator *>(arg);
338 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
339
340 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
341
342 if (stroke_len == 0) {
343 return;
344 }
345
346 Object *obedit = cdd->vc.obedit;
347
348 /* Disabled: not representative in enough cases, and curves draw shape is not per object yet.
349 * In the future this could be enabled when the object's draw shape is "strand" or "3D". */
350 if (false && cdd->bevel_radius > 0.0f) {
351 BLI_mempool_iter iter;
352 const StrokeElem *selem;
353
354 const float location_zero[3] = {0};
355 const float *location_prev = location_zero;
356
357 float color[3];
359
360 gpu::Batch *sphere = GPU_batch_preset_sphere(0);
362 GPU_batch_uniform_3fv(sphere, "color", color);
363
364 /* scale to edit-mode space */
366 GPU_matrix_mul(obedit->object_to_world().ptr());
367
369 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
370 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
371 {
372 GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0],
373 selem->location_local[1] - location_prev[1],
374 selem->location_local[2] - location_prev[2]);
375
376 const float radius = stroke_elem_radius(cdd, selem);
377
379 GPU_matrix_scale_1f(radius);
380 GPU_batch_draw(sphere);
382
383 location_prev = selem->location_local;
384 }
385
387 }
388
389 if (stroke_len > 1) {
390 float (*coord_array)[3] = static_cast<float (*)[3]>(
391 MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__));
392
393 {
394 BLI_mempool_iter iter;
395 const StrokeElem *selem;
396 int i;
398 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i = 0; selem;
399 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
400 {
401 copy_v3_v3(coord_array[i], selem->location_world);
402 }
403 }
404
405 {
408 format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
410
413 GPU_line_smooth(true);
414 GPU_line_width(3.0f);
415
416 imm_cpack(0x0);
417 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
418 for (int i = 0; i < stroke_len; i++) {
419 immVertex3fv(pos, coord_array[i]);
420 }
421 immEnd();
422
423 GPU_line_width(1.0f);
424
425 imm_cpack(0xffffffff);
426 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
427 for (int i = 0; i < stroke_len; i++) {
428 immVertex3fv(pos, coord_array[i]);
429 }
430 immEnd();
431
432 /* Reset defaults */
435 GPU_line_smooth(false);
436
438 }
439
440 MEM_freeN(coord_array);
441 }
442}
443
444static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
445{
446 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
447 Object *obedit = cdd->vc.obedit;
448
449 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
450
451 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
452
453 ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
454
455 /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */
456 selem->pressure = event->tablet.pressure;
457
458 bool is_depth_found = stroke_elem_project_fallback_elem(
459 cdd, cdd->prev.location_world_valid, selem);
460
461 if (is_depth_found) {
462 /* use the depth if a fallback wasn't used */
464 }
466
467 float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval);
468 copy_v2_v2(cdd->prev.mval, selem->mval);
469
470 if (cdd->sample.use_substeps && cdd->prev.selem) {
471 const StrokeElem selem_target = *selem;
472 StrokeElem *selem_new_last = selem;
473 if (len_sq >= square_f(STROKE_SAMPLE_DIST_MAX_PX)) {
474 int n = int(ceil(sqrt(double(len_sq)))) / STROKE_SAMPLE_DIST_MAX_PX;
475
476 for (int i = 1; i < n; i++) {
477 StrokeElem *selem_new = selem_new_last;
478 stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, float(i) / n);
479
480 const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
481 cdd, cdd->prev.location_world_valid, selem_new);
482 if (is_depth_found == false) {
483 if (is_depth_found_substep) {
485 }
486 }
487
488 selem_new_last = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
489 }
490 }
491 selem = selem_new_last;
492 *selem_new_last = selem_target;
493 }
494
495 cdd->prev.selem = selem;
496
498}
499
500static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
501{
502 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
504
505 /* add first point */
506 curve_draw_event_add(op, event);
507
510 {
511 RegionView3D *rv3d = cdd->vc.rv3d;
512
513 cdd->project.use_depth = false;
514 cdd->project.use_plane = true;
515
516 float normal[3] = {0.0f};
517 if (ELEM(cps->surface_plane,
520 {
521 if (ED_view3d_depth_read_cached_normal(cdd->vc.region, cdd->depths, event->mval, normal)) {
523 float cross_a[3], cross_b[3];
524 cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
525 cross_v3_v3v3(cross_b, normal, cross_a);
526 copy_v3_v3(normal, cross_b);
527 }
528 }
529 }
530
531 /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
532 if (is_zero_v3(normal)) {
533 copy_v3_v3(normal, rv3d->viewinv[2]);
534 }
535
536 normalize_v3_v3(cdd->project.plane, normal);
538
539 /* Special case for when we only have offset applied on the first-hit,
540 * the remaining stroke must be offset too. */
541 if (cdd->project.surface_offset != 0.0f) {
542 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
543
544 float location_no_offset[3];
545
546 if (stroke_elem_project(cdd, event->mval, mval_fl, 0.0f, 0.0f, location_no_offset, nullptr))
547 {
548 sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
549 if (!is_zero_v3(cdd->project.offset)) {
550 cdd->project.use_offset = true;
551 }
552 }
553 }
554 /* end special case */
555 }
556
557 cdd->init_event_type = event->type;
559}
560
562{
563 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
564 if (cdd) {
565 if (cdd->draw_handle_view) {
568 }
569
570 if (cdd->stroke_elem_pool) {
572 }
573
574 if (cdd->depths) {
576 }
577 MEM_freeN(cdd);
578 op->customdata = nullptr;
579 }
580}
581
582static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
583{
584 BLI_assert(op->customdata == nullptr);
585
587
589
590 if (is_invoke) {
592 if (ELEM(nullptr, cdd->vc.region, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
593 MEM_freeN(cdd);
594 BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport");
595 return false;
596 }
597 }
598 else {
599 cdd->vc.bmain = CTX_data_main(C);
600 cdd->vc.depsgraph = depsgraph;
601 cdd->vc.scene = CTX_data_scene(C);
604
605 /* Using an empty stroke complicates logic later,
606 * it's simplest to disallow early on (see: #94085). */
607 if (RNA_collection_is_empty(op->ptr, "stroke")) {
608 MEM_freeN(cdd);
609 BKE_report(op->reports, RPT_ERROR, "The \"stroke\" cannot be empty");
610 return false;
611 }
612 }
613
614 op->customdata = cdd;
615 cdd->bevel_radius = 1.0f;
616 cdd->is_curve_2d = RNA_boolean_get(op->ptr, "is_curve_2d");
617
619
620 cdd->curve_type = cps->curve_type;
621
622 cdd->radius.min = cps->radius_min;
623 cdd->radius.max = cps->radius_max;
624 cdd->radius.range = cps->radius_max - cps->radius_min;
628
630
631 return true;
632}
633
636 const CurveDrawData *cdd,
637 const int curve_index,
638 const bool is_cyclic,
639 const uint cubic_spline_len,
640 const int dims,
641 const int radius_index,
642 const float radius_max,
643 const float *cubic_spline,
644 const uint *corners_index,
645 const uint corners_index_len)
646{
647 curves.resize(curves.points_num() + cubic_spline_len, curve_index + 1);
648
649 MutableSpan<float3> positions = curves.positions_for_write();
650 MutableSpan<float3> handle_positions_l = curves.handle_positions_left_for_write();
651 MutableSpan<float3> handle_positions_r = curves.handle_positions_right_for_write();
652 MutableSpan<int8_t> handle_types_l = curves.handle_types_left_for_write();
653 MutableSpan<int8_t> handle_types_r = curves.handle_types_right_for_write();
654
655 const IndexRange new_points = curves.points_by_curve()[curve_index];
656
658 "radius", bke::AttrDomain::Point);
659
660 const float *co = cubic_spline;
661
662 for (const int64_t i : new_points) {
663 const float *handle_l = co + (dims * 0);
664 const float *pt = co + (dims * 1);
665 const float *handle_r = co + (dims * 2);
666
667 copy_v3_v3(handle_positions_l[i], handle_l);
668 copy_v3_v3(positions[i], pt);
669 copy_v3_v3(handle_positions_r[i], handle_r);
670
671 const float radius = (radius_index != -1) ?
672 (pt[radius_index] * cdd->radius.range) + cdd->radius.min :
673 radius_max;
674 radii.span[i] = radius;
675
676 handle_types_l[i] = BEZIER_HANDLE_ALIGN;
677 handle_types_r[i] = BEZIER_HANDLE_ALIGN;
678 co += (dims * 3);
679 }
680
681 if (corners_index) {
682 /* ignore the first and last */
683 uint i_start = 0, i_end = corners_index_len;
684
685 if ((corners_index_len >= 2) && !is_cyclic) {
686 i_start += 1;
687 i_end -= 1;
688 }
689
690 for (const auto i : IndexRange(i_start, i_end - i_start)) {
691 const int64_t corner_i = new_points[corners_index[i]];
692 handle_types_l[corner_i] = BEZIER_HANDLE_FREE;
693 handle_types_r[corner_i] = BEZIER_HANDLE_FREE;
694 }
695 }
696
697 radii.finish();
698}
699
702 const CurveDrawData *cdd,
703 const int curve_index,
704 const bool is_cyclic,
705 const uint cubic_spline_len,
706 const int dims,
707 const int radius_index,
708 const float radius_max,
709 const float *cubic_spline)
710{
711 const int point_num = (cubic_spline_len - 2) * 3 + 4 + (is_cyclic ? 2 : 0);
712 curves.resize(curves.points_num() + point_num, curve_index + 1);
713
714 MutableSpan<float3> positions = curves.positions_for_write();
715 MutableSpan<float> weights = curves.nurbs_weights_for_write();
716
717 const IndexRange new_points = curves.points_by_curve()[curve_index];
718
720 "radius", bke::AttrDomain::Point);
721 /* If cyclic shows to first left handle else first control point. */
722 const float *pt = cubic_spline + (is_cyclic ? 0 : dims);
723
724 for (const int64_t i : new_points) {
725 const float radius = (radius_index != -1) ?
726 (pt[radius_index] * cdd->radius.range) + cdd->radius.min :
727 radius_max;
728 copy_v3_v3(positions[i], pt);
729 weights[i] = 1.0f;
730 radii.span[i] = radius;
731
732 pt += dims;
733 }
734
735 radii.finish();
736}
737
739{
740 if (op->customdata == nullptr) {
741 if (!curve_draw_init(C, op, false)) {
742 return OPERATOR_CANCELLED;
743 }
744 }
745
746 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
747
749 Object *obedit = cdd->vc.obedit;
750
751 int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
752
753 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
754
755 if (BLI_mempool_len(cdd->stroke_elem_pool) == 0) {
757 stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
758 }
759
760 /* error in object local space */
761 const int fit_method = RNA_enum_get(op->ptr, "fit_method");
762 const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
763 const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
764 const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
765 const bool bezier_as_nurbs = RNA_boolean_get(op->ptr, "bezier_as_nurbs");
766 bool is_cyclic = (stroke_len > 2) && use_cyclic;
767
768 const float radius_min = cps->radius_min;
769 const float radius_max = cps->radius_max;
770 const float radius_range = cps->radius_max - cps->radius_min;
771
772 Curves *curves_id = static_cast<Curves *>(obedit->data);
773 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
774 const int curve_index = curves.curves_num();
775
776 const bool use_pressure_radius = (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
777 ((cps->radius_taper_start != 0.0f) ||
778 (cps->radius_taper_end != 0.0f));
779
780 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
782 remove_selection_attributes(attributes, selection_attribute_names);
783
784 if (cdd->curve_type == CU_BEZIER) {
785 /* Allow to interpolate multiple channels */
786 int dims = 3;
787 const int radius_index = use_pressure_radius ? dims++ : -1;
788
789 float *coords = MEM_malloc_arrayN<float>(stroke_len * dims, __func__);
790
791 float *cubic_spline = nullptr;
792 uint cubic_spline_len = 0;
793
794 {
795 BLI_mempool_iter iter;
796 const StrokeElem *selem;
797 float *co = coords;
798
800 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
801 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), co += dims)
802 {
803 copy_v3_v3(co, selem->location_local);
804 if (radius_index != -1) {
805 co[radius_index] = selem->pressure;
806 }
807
808 /* remove doubles */
809 if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) {
810 co -= dims;
811 stroke_len--;
812 }
813 }
814 }
815
816 uint *corners = nullptr;
817 uint corners_len = 0;
818
819 if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < float(M_PI))) {
820 /* this could be configurable... */
821 const float corner_radius_min = error_threshold / 8;
822 const float corner_radius_max = error_threshold * 2;
823 const uint samples_max = 16;
824
825 curve_fit_corners_detect_fl(coords,
826 stroke_len,
827 dims,
828 corner_radius_min,
829 corner_radius_max,
830 samples_max,
831 corner_angle,
832 &corners,
833 &corners_len);
834 }
835
836 uint *corners_index = nullptr;
837 uint corners_index_len = 0;
838 uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
839
840 if ((stroke_len > 2) && use_cyclic) {
841 calc_flag |= CURVE_FIT_CALC_CYCLIC;
842 }
843 else {
844 /* Might need this update if stroke_len <= 2 after removing doubles. */
845 is_cyclic = false;
846 }
847
848 int result;
849 if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
850 result = curve_fit_cubic_to_points_refit_fl(coords,
851 stroke_len,
852 dims,
853 error_threshold,
854 calc_flag,
855 nullptr,
856 0,
857 corner_angle,
858 &cubic_spline,
859 &cubic_spline_len,
860 nullptr,
861 &corners_index,
862 &corners_index_len);
863 }
864 else {
865 result = curve_fit_cubic_to_points_fl(coords,
866 stroke_len,
867 dims,
868 error_threshold,
869 calc_flag,
870 corners,
871 corners_len,
872 &cubic_spline,
873 &cubic_spline_len,
874 nullptr,
875 &corners_index,
876 &corners_index_len);
877 }
878
879 MEM_freeN(coords);
880 if (corners) {
881 free(corners);
882 }
883
884 if (result == 0) {
885 int8_t knots_mode;
886 int8_t order;
887 CurveType curve_type;
888 if (bezier_as_nurbs) {
889 bool is_cyclic_curve = calc_flag & CURVE_FIT_CALC_CYCLIC;
891 attributes,
892 cdd,
893 curve_index,
894 is_cyclic_curve,
895 cubic_spline_len,
896 dims,
897 radius_index,
898 radius_max,
899 cubic_spline);
900 order = 4;
901 knots_mode = is_cyclic_curve ? NURBS_KNOT_MODE_BEZIER : NURBS_KNOT_MODE_ENDPOINT_BEZIER;
902 curve_type = CURVE_TYPE_NURBS;
903 }
904 else {
906 attributes,
907 cdd,
908 curve_index,
909 is_cyclic,
910 cubic_spline_len,
911 dims,
912 radius_index,
913 radius_max,
914 cubic_spline,
915 corners_index,
916 corners_index_len);
917 order = 0;
918 knots_mode = 0;
919 curve_type = CURVE_TYPE_BEZIER;
920 }
921 curves.nurbs_knots_modes_for_write()[curve_index] = knots_mode;
922 curves.nurbs_orders_for_write()[curve_index] = order;
923 curves.fill_curve_types(IndexRange(curve_index, 1), curve_type);
924
925 /* If Bezier curve is being added, loop through all three names, otherwise through ones in
926 * `selection_attribute_names`. */
927 for (const StringRef selection_name :
928 (bezier_as_nurbs ? selection_attribute_names :
930 {
931 bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
932 selection_name, bke::AttrDomain::Curve);
933 if (selection_name == ".selection" || !bezier_as_nurbs) {
934 selection.varray.set(curve_index, true);
935 }
936 selection.finish();
937 }
938
939 if (attributes.contains("resolution")) {
940 curves.resolution_for_write()[curve_index] = 12;
941 }
943 attributes,
946 "radius",
947 "handle_left",
948 "handle_right",
949 "handle_type_left",
950 "handle_type_right",
951 "nurbs_weight",
952 ".selection",
953 ".selection_handle_left",
954 ".selection_handle_right"}),
955 curves.points_by_curve()[curve_index]);
957 attributes,
960 "resolution",
961 "cyclic",
962 "nurbs_order",
963 "knots_mode",
964 ".selection",
965 ".selection_handle_left",
966 ".selection_handle_right"}),
967 IndexRange(curve_index, 1));
968 }
969
970 if (corners_index) {
971 free(corners_index);
972 }
973
974 if (cubic_spline) {
975 free(cubic_spline);
976 }
977 }
978 else { /* CU_POLY */
979 curves.resize(curves.points_num() + stroke_len, curve_index + 1);
980 curves.fill_curve_types(IndexRange(curve_index, 1), CURVE_TYPE_POLY);
981
982 MutableSpan<float3> positions = curves.positions_for_write();
984 "radius", bke::AttrDomain::Point);
985
986 const IndexRange new_points = curves.points_by_curve()[curve_index];
987
988 IndexRange::Iterator points_iter = new_points.begin();
989
990 BLI_mempool_iter iter;
992 for (const auto *selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
993 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), points_iter++)
994 {
995 const int64_t i = *points_iter;
996 copy_v3_v3(positions[i], selem->location_local);
997 if (cdd->is_curve_2d) {
998 positions[i][2] = 0.0f;
999 }
1000
1001 radii.span[i] = use_pressure_radius ? (selem->pressure * radius_range) + radius_min :
1002 cps->radius_max;
1003 }
1004
1005 radii.finish();
1006
1007 bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
1008 ".selection", bke::AttrDomain::Curve);
1009 selection.varray.set(curve_index, true);
1010 selection.finish();
1011
1012 /* Creates ".selection_handle_left" and ".selection_handle_right" attributes, otherwise all
1013 * existing Bezier handles would be treated as selected. */
1014 for (const StringRef selection_name : get_curves_bezier_selection_attribute_names(curves)) {
1015 bke::AttributeWriter<bool> selection = attributes.lookup_or_add_for_write<bool>(
1016 selection_name, bke::AttrDomain::Curve);
1017 selection.finish();
1018 }
1019
1021 attributes,
1024 "radius",
1025 ".selection",
1026 ".selection_handle_left",
1027 ".selection_handle_right"}),
1028 new_points);
1030 attributes,
1033 {"curve_type", ".selection", ".selection_handle_left", ".selection_handle_right"}),
1034 IndexRange(curve_index, 1));
1035 }
1036
1037 if (is_cyclic) {
1038 curves.cyclic_for_write()[curve_index] = true;
1039 }
1040
1042 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1043
1044 curve_draw_exit(op);
1045
1046 return OPERATOR_FINISHED;
1047}
1048
1050{
1051 if (RNA_struct_property_is_set(op->ptr, "stroke")) {
1052 return curves_draw_exec(C, op);
1053 }
1054
1055 if (!curve_draw_init(C, op, true)) {
1056 return OPERATOR_CANCELLED;
1057 }
1058
1059 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1060
1062
1063 const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1064
1065 /* Fallback (in case we can't find the depth on first test). */
1066 {
1067 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1068 float center[3];
1069 negate_v3_v3(center, cdd->vc.rv3d->ofs);
1070 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.region, center, mval_fl, cdd->prev.location_world);
1072 }
1073
1077
1078 {
1079 View3D *v3d = cdd->vc.v3d;
1080 RegionView3D *rv3d = cdd->vc.rv3d;
1081 Object *obedit = cdd->vc.obedit;
1082
1083 const float *plane_no = nullptr;
1084 const float *plane_co = nullptr;
1085
1086 if (cdd->is_curve_2d) {
1087 /* 2D overrides other options */
1088 plane_co = obedit->object_to_world().location();
1089 plane_no = obedit->object_to_world().ptr()[2];
1090 cdd->project.use_plane = true;
1091 }
1092 else {
1093 if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && (v3d->shading.type > OB_WIRE)) {
1094 /* needed or else the draw matrix can be incorrect */
1096
1099 depth_mode = V3D_DEPTH_SELECTED_ONLY;
1100 }
1101
1103 cdd->vc.region,
1104 cdd->vc.v3d,
1105 nullptr,
1106 depth_mode,
1107 false,
1108 &cdd->depths);
1109
1110 if (cdd->depths != nullptr) {
1111 cdd->project.use_depth = true;
1112 }
1113 else {
1114 BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
1115 cdd->project.use_depth = false;
1116 }
1117 }
1118
1119 /* use view plane (when set or as a fallback when surface can't be found) */
1120 if (cdd->project.use_depth == false) {
1121 plane_co = cdd->vc.scene->cursor.location;
1122 plane_no = rv3d->viewinv[2];
1123 cdd->project.use_plane = true;
1124 }
1125
1126 if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
1127 cdd->sample.use_substeps = true;
1128 }
1129 }
1130
1131 if (cdd->project.use_plane) {
1132 normalize_v3_v3(cdd->project.plane, plane_no);
1133 cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
1134 }
1135 }
1136
1137 if (is_modal == false) {
1138 curve_draw_event_add_first(op, event);
1139 }
1140
1141 /* add temp handler */
1143
1145}
1146
1147static void curve_draw_cancel(bContext * /*C*/, wmOperator *op)
1148{
1149 curve_draw_exit(op);
1150}
1151
1156{
1157 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1159 PropertyRNA *prop;
1160
1161 prop = RNA_struct_find_property(op->ptr, "fit_method");
1162 if (!RNA_property_is_set(op->ptr, prop)) {
1163 RNA_property_enum_set(op->ptr, prop, cps->fit_method);
1164 }
1165
1166 prop = RNA_struct_find_property(op->ptr, "corner_angle");
1167 if (!RNA_property_is_set(op->ptr, prop)) {
1168 const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle :
1169 float(M_PI);
1170 RNA_property_float_set(op->ptr, prop, corner_angle);
1171 }
1172
1173 prop = RNA_struct_find_property(op->ptr, "error_threshold");
1174 if (!RNA_property_is_set(op->ptr, prop)) {
1175
1176 /* Error isn't set so we'll have to calculate it from the pixel values. */
1177 BLI_mempool_iter iter;
1178 const StrokeElem *selem, *selem_prev;
1179
1180 float len_3d = 0.0f, len_2d = 0.0f;
1181 float scale_px; /* pixel to local space scale */
1182
1183 int i = 0;
1185 selem_prev = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
1186 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
1187 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
1188 {
1189 len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
1190 len_2d += len_v2v2(selem->mval, selem_prev->mval);
1191 selem_prev = selem;
1192 }
1193 scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
1194 float error_threshold = (cps->error_threshold * UI_SCALE_FAC) * scale_px;
1195 RNA_property_float_set(op->ptr, prop, error_threshold);
1196 }
1197
1198 prop = RNA_struct_find_property(op->ptr, "use_cyclic");
1199 if (!RNA_property_is_set(op->ptr, prop)) {
1200 bool use_cyclic = false;
1201
1202 if (BLI_mempool_len(cdd->stroke_elem_pool) > 2) {
1203 BLI_mempool_iter iter;
1204 const StrokeElem *selem, *selem_first, *selem_last;
1205
1207 selem_first = selem_last = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
1208 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
1209 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
1210 {
1211 selem_last = selem;
1212 }
1213
1214 if (len_squared_v2v2(selem_first->mval, selem_last->mval) <=
1216 {
1217 use_cyclic = true;
1218 }
1219 }
1220
1221 RNA_property_boolean_set(op->ptr, prop, use_cyclic);
1222 }
1223
1224 if ((cps->radius_taper_start != 0.0f) || (cps->radius_taper_end != 0.0f)) {
1225 /* NOTE: we could try to de-duplicate the length calculations above. */
1226 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
1227
1228 BLI_mempool_iter iter;
1229 StrokeElem *selem, *selem_prev;
1230
1231 float *lengths = MEM_malloc_arrayN<float>(stroke_len, __func__);
1232 StrokeElem **selem_array = static_cast<StrokeElem **>(
1233 MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__));
1234 lengths[0] = 0.0f;
1235
1236 float len_3d = 0.0f;
1237
1238 int i = 1;
1240 selem_prev = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter));
1241 selem_array[0] = selem_prev;
1242 for (selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
1243 selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
1244 {
1245 const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
1246 len_3d += len_3d_segment;
1247 lengths[i] = len_3d;
1248 selem_array[i] = selem;
1249 selem_prev = selem;
1250 }
1251
1252 if (cps->radius_taper_start != 0.0f) {
1253 const float len_taper_max = cps->radius_taper_start * len_3d;
1254 for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) {
1255 const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max);
1256 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
1257 }
1258 }
1259
1260 if (cps->radius_taper_end != 0.0f) {
1261 const float len_taper_max = cps->radius_taper_end * len_3d;
1262 const float len_taper_min = len_3d - len_taper_max;
1263 for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) {
1264 const float pressure_new = selem_array[i]->pressure *
1265 ((len_3d - lengths[i]) / len_taper_max);
1266 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
1267 }
1268 }
1269
1270 MEM_freeN(lengths);
1271 MEM_freeN(selem_array);
1272 }
1273}
1274
1276{
1278 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1279
1280 UNUSED_VARS(C, op);
1281
1282 if (event->type == cdd->init_event_type) {
1283 if (event->val == KM_RELEASE) {
1285
1287
1289
1290 curves_draw_exec(C, op);
1291
1292 return OPERATOR_FINISHED;
1293 }
1294 }
1295 else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
1297 curve_draw_cancel(C, op);
1298 return OPERATOR_CANCELLED;
1299 }
1300 else if (ELEM(event->type, LEFTMOUSE)) {
1301 if (event->val == KM_PRESS) {
1302 curve_draw_event_add_first(op, event);
1303 }
1304 }
1305 else if (ISMOUSE_MOTION(event->type)) {
1306 if (cdd->state == CURVE_DRAW_PAINTING) {
1307 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1309 curve_draw_event_add(op, event);
1310 }
1311 }
1312 }
1313
1314 return ret;
1315}
1316
1318{
1319 ot->name = "Draw Curves";
1320 ot->idname = __func__;
1321 ot->description = "Draw a freehand curve";
1322
1323 ot->exec = curves_draw_exec;
1324 ot->invoke = curves_draw_invoke;
1325 ot->modal = curves_draw_modal;
1327
1328 /* flags */
1329 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1330
1331 /* properties */
1332 PropertyRNA *prop;
1333
1334 prop = RNA_def_float_distance(ot->srna,
1335 "error_threshold",
1336 0.0f,
1337 0.0f,
1338 10.0f,
1339 "Error",
1340 "Error distance threshold (in object units)",
1341 0.0001f,
1342 10.0f);
1344 RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
1345
1346 RNA_def_enum(ot->srna,
1347 "fit_method",
1350 "Fit Method",
1351 "");
1352
1354 ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
1356
1357 prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
1359
1360 prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1362
1363 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1365
1366 prop = RNA_def_boolean(ot->srna, "is_curve_2d", false, "Curve 2D", "");
1368
1369 prop = RNA_def_boolean(ot->srna, "bezier_as_nurbs", false, "As NURBS", "");
1371}
1372
1373} // namespace blender::ed::curves
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)
Low-level operations for curves.
@ 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)
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_BEZIER
@ CU_POLY
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_ALIGN
@ NURBS_KNOT_MODE_BEZIER
@ NURBS_KNOT_MODE_ENDPOINT_BEZIER
@ OB_WIRE
@ 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
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)
#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
long long int int64_t
bool contains(StringRef attribute_id) const
GAttributeWriter lookup_or_add_for_write(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
nullptr float
#define STROKE_CYCLIC_DIST_PX
#define STROKE_SAMPLE_DIST_MIN_PX
#define STROKE_SAMPLE_DIST_MAX_PX
static bool is_cyclic(const Nurb *nu)
uint pos
#define ceil
#define sqrt
format
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
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_stroke_from_operator_elem(wmOperator *op, PointerRNA *itemptr)
void CURVES_OT_draw(wmOperatorType *ot)
static void create_Bezier(bke::CurvesGeometry &curves, bke::MutableAttributeAccessor &attributes, const CurveDrawData *cdd, const int curve_index, const bool is_cyclic, const uint cubic_spline_len, const int dims, const int radius_index, const float radius_max, const float *cubic_spline, const uint *corners_index, const uint corners_index_len)
void remove_selection_attributes(bke::MutableAttributeAccessor &attributes, Span< StringRef > selection_attribute_names)
static void create_NURBS(bke::CurvesGeometry &curves, bke::MutableAttributeAccessor &attributes, const CurveDrawData *cdd, const int curve_index, const bool is_cyclic, const uint cubic_spline_len, const int dims, const int radius_index, const float radius_max, const float *cubic_spline)
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])
static wmOperatorStatus curves_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
static void curve_draw_exit(wmOperator *op)
Span< StringRef > get_curves_bezier_selection_attribute_names(const bke::CurvesGeometry &curves)
bool editable_curves_in_edit_mode_poll(bContext *C)
static void curve_draw_stroke_from_operator(wmOperator *op)
static void curve_draw_stroke_to_operator(wmOperator *op)
static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
static void curve_draw_cancel(bContext *, wmOperator *op)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
static void stroke_elem_interp(StrokeElem *selem_out, const StrokeElem *selem_a, const StrokeElem *selem_b, float t)
static void curve_draw_stroke_3d(const bContext *, ARegion *, void *arg)
Span< StringRef > get_curves_all_selection_attribute_names()
static wmOperatorStatus curves_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus curves_draw_exec(bContext *C, wmOperator *op)
static void curve_draw_stroke_to_operator_elem(wmOperator *op, const StrokeElem *selem)
static bool stroke_elem_project_fallback_elem(const CurveDrawData *cdd, const float location_fallback_depth[3], StrokeElem *selem)
static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
static void curve_draw_exec_precalc(wmOperator *op)
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
CurvesGeometry geometry
Definition DNA_ID.h:414
ObjectRuntimeHandle * runtime
float viewinv[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
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
struct blender::ed::curves::CurveDrawData::@175246207353257224352227301246272011101366271076 prev
struct blender::ed::curves::CurveDrawData::@237244354150274172272027025121317243126034014247 project
struct blender::ed::curves::CurveDrawData::@361247236032141064107036117265232027335307252324 sample
struct blender::ed::curves::CurveDrawData::@061073017047173027345205355323276272343022262023 radius
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