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