Blender V4.5
grease_pencil_paint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_action.hh"
6#include "BKE_attribute.hh"
7#include "BKE_brush.hh"
8#include "BKE_colortools.hh"
9#include "BKE_context.hh"
10#include "BKE_curves.hh"
11#include "BKE_deform.hh"
12#include "BKE_geometry_set.hh"
13#include "BKE_grease_pencil.hh"
15#include "BKE_lib_id.hh"
16#include "BKE_material.hh"
17#include "BKE_paint.hh"
18#include "BKE_scene.hh"
19
20#include "BLI_bounds.hh"
21#include "BLI_color.hh"
23#include "BLI_listbase.h"
24#include "BLI_math_base.hh"
25#include "BLI_math_color.h"
26#include "BLI_noise.hh"
27#include "BLI_rand.hh"
28#include "BLI_rect.h"
29#include "BLI_time.h"
30
32
33#include "DNA_brush_types.h"
34#include "DNA_material_types.h"
35#include "DNA_modifier_types.h"
36
37#include "DNA_scene_types.h"
38#include "ED_curves.hh"
39#include "ED_grease_pencil.hh"
40#include "ED_view3d.hh"
41
44#include "GEO_smooth_curves.hh"
45
46#include "WM_api.hh"
47#include "WM_types.hh"
48
50
51#include <numeric>
52#include <optional>
53
55
57 const Brush *brush,
58 const float3 pos)
59{
60 if ((brush->flag & BRUSH_LOCK_SIZE) != 0) {
61 const float pixel_size = ED_view3d_pixel_size(rv3d, pos);
62 return brush->unprojected_radius / pixel_size;
63 }
64 return float(brush->size);
65}
66
67template<typename T>
68static inline void linear_interpolation(const T &a,
69 const T &b,
71 const bool include_first_point)
72{
73 if (include_first_point) {
74 const float step = math::safe_rcp(float(dst.size() - 1));
75 for (const int i : dst.index_range()) {
76 dst[i] = bke::attribute_math::mix2(float(i) * step, a, b);
77 }
78 }
79 else {
80 const float step = 1.0f / float(dst.size());
81 for (const int i : dst.index_range()) {
82 dst[i] = bke::attribute_math::mix2(float(i + 1) * step, a, b);
83 }
84 }
85}
86
88{
89 return std::accumulate(values.begin(), values.end(), float2(0)) / values.size();
90}
91
93static Array<float2> sample_curve_2d(Span<float2> positions, const int64_t resolution)
94{
95 BLI_assert(positions.size() % 3 == 0);
96 const int64_t num_handles = positions.size() / 3;
97 if (num_handles == 1) {
98 return Array<float2>(resolution, positions[1]);
99 }
100 const int64_t num_segments = num_handles - 1;
101 const int64_t num_points = num_segments * resolution;
102
103 Array<float2> points(num_points);
104 const Span<float2> curve_segments = positions.drop_front(1).drop_back(1);
105 threading::parallel_for(IndexRange(num_segments), 32 * resolution, [&](const IndexRange range) {
106 for (const int64_t segment_i : range) {
107 IndexRange segment_range(segment_i * resolution, resolution);
108 bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0],
109 curve_segments[segment_i * 3 + 1],
110 curve_segments[segment_i * 3 + 2],
111 curve_segments[segment_i * 3 + 3],
112 points.as_mutable_span().slice(segment_range));
113 }
114 });
115 return points;
116}
117
123{
124 BLI_assert(src.size() == dst.size());
125 Array<float> accumulated_lengths_src(src.size() - 1);
126 length_parameterize::accumulate_lengths<float2>(src, false, accumulated_lengths_src);
127
128 Array<float> accumulated_lengths_target(target.size() - 1);
129 length_parameterize::accumulate_lengths<float2>(target, false, accumulated_lengths_target);
130
131 Array<int> segment_indices(accumulated_lengths_src.size());
132 Array<float> segment_factors(accumulated_lengths_src.size());
134 accumulated_lengths_target, accumulated_lengths_src, segment_indices, segment_factors);
135
137 target, segment_indices, segment_factors, dst.drop_back(1));
138 dst.last() = src.last();
139}
140
143{
144 Brush *fill_guides_brush = BKE_id_new_nomain<Brush>("Draw Fill Guides");
145 fill_guides_brush->ob_mode = OB_MODE_PAINT_GREASE_PENCIL;
146
147 if (fill_guides_brush->gpencil_settings == nullptr) {
148 BKE_brush_init_gpencil_settings(fill_guides_brush);
149 }
150 BrushGpencilSettings *settings = fill_guides_brush->gpencil_settings;
151
158 BKE_curvemapping_init(fill_guides_brush->curve_rand_hue);
160 BKE_curvemapping_init(fill_guides_brush->curve_rand_value);
161
162 fill_guides_brush->flag |= BRUSH_LOCK_SIZE;
163 fill_guides_brush->unprojected_radius = 0.005f;
164
165 settings->flag &= ~GP_BRUSH_USE_PRESSURE;
166
168 /* TODO: Use theme setting. */
169 copy_v3_fl3(fill_guides_brush->rgb, 0.0f, 1.0f, 1.0f);
170 settings->vertex_factor = 1.0f;
171
172 settings->active_smooth = 0.35f;
173 settings->hardness = 1.0f;
174 fill_guides_brush->spacing = 100;
175
176 settings->flag |= GP_BRUSH_GROUP_SETTINGS;
177 settings->simplify_px = 0.4f;
178
179 return fill_guides_brush;
180}
181
183 private:
185 int frame_number_;
187
189 Vector<float2> screen_space_coords_orig_;
190
195 Vector<Vector<float2>> screen_space_curve_fitted_coords_;
197 Vector<float2> screen_space_jitter_offsets_;
199 Vector<std::optional<float>> stroke_placement_depths_;
200
202 Vector<float2> screen_space_smoothed_coords_;
204 Vector<float2> screen_space_final_coords_;
205
207 int active_smooth_start_index_ = 0;
208 blender::float4x2 texture_space_ = float4x2::identity();
209
213 std::optional<float> last_stroke_placement_depth_;
215 std::optional<int> last_stroke_placement_point_;
216
218 float2 smoothed_pen_direction_ = float2(0.0f);
219
221 float accum_distance_ = 0.0f;
222
224
225 float stroke_random_radius_factor_;
226 float stroke_random_opacity_factor_;
227 float stroke_random_rotation_factor_;
228
229 float stroke_random_hue_factor_;
230 float stroke_random_sat_factor_;
231 float stroke_random_val_factor_;
232
234 double start_time_;
236 double delta_time_;
237
239 bool do_fill_guides_;
240
242
243 Brush *saved_active_brush_;
244 Brush *fill_guides_brush_;
245
246 public:
247 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
248 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
249 void on_stroke_done(const bContext &C) override;
250
251 PaintOperation(const bool do_fill_guides = false) : do_fill_guides_(do_fill_guides) {}
252
256 std::optional<int> start_point,
257 float from_depth,
258 float to_depth);
261};
262
270
272
274 std::optional<BrushColorJitterSettings> jitter_settings_;
275
277 ColorGeometry4f fill_color_ = ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f);
280
283
285 {
287 Paint *paint = &scene_->toolsettings->gp_paint->paint;
289 settings_ = brush_->gpencil_settings;
290
292 use_vertex_color_ = brush_using_vertex_color(scene_->toolsettings->gp_paint, brush_);
293 if (use_vertex_color_) {
294 ColorGeometry4f color_base;
295 srgb_to_linearrgb_v3_v3(color_base, brush_->rgb);
296 color_base.a = settings_->vertex_factor;
298 vertex_color_ = color_base;
299 }
301 fill_color_ = color_base;
302 }
303 }
304 softness_ = 1.0f - settings_->hardness;
305 aspect_ratio_ = settings_->aspect_ratio[0] / math::max(settings_->aspect_ratio[1], 1e-8f);
307 }
308
310 const bContext &C,
311 const InputSample &start_sample,
312 const int material_index,
313 const bool use_fill)
314 {
315 const float2 start_coords = start_sample.mouse_position;
316 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
317 const ARegion *region = CTX_wm_region(&C);
318
319 float3 start_location;
320 if (self.placement_.use_project_to_stroke()) {
321 const std::optional<float> depth = self.placement_.get_depth(start_coords);
322 if (depth) {
323 start_location = self.placement_.place(start_coords, *depth);
324 }
325 else {
326 start_location = self.placement_.project(start_coords);
327 }
328 }
329 else {
330 start_location = self.placement_.project(start_coords);
331 }
333 rv3d,
334 region,
335 brush_,
336 start_sample.pressure,
337 start_location,
338 self.placement_.to_world_space(),
339 settings_);
341 *settings_, self.stroke_random_radius_factor_, 0.0f, start_radius, start_sample.pressure);
342
344 start_sample.pressure, brush_, settings_);
346 self.stroke_random_opacity_factor_,
347 0.0f,
348 start_opacity,
349 start_sample.pressure);
350
351 const float start_rotation = ed::greasepencil::randomize_rotation(
352 *settings_, self.rng_, self.stroke_random_rotation_factor_, start_sample.pressure);
353 Scene *scene = CTX_data_scene(&C);
354 if (use_vertex_color_) {
357 self.stroke_random_hue_factor_,
358 self.stroke_random_sat_factor_,
359 self.stroke_random_val_factor_,
360 0.0f,
362 start_sample.pressure);
363 }
364
365 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
366
367 self.screen_space_coords_orig_.append(start_coords);
368 self.screen_space_curve_fitted_coords_.append(Vector<float2>({start_coords}));
369 self.screen_space_jitter_offsets_.append(float2(0.0f));
370 self.screen_space_smoothed_coords_.append(start_coords);
371 self.screen_space_final_coords_.append(start_coords);
372
373 /* Resize the curves geometry so there is one more curve with a single point. */
374 bke::CurvesGeometry &curves = self.drawing_->strokes_for_write();
376
377 const int active_curve = on_back ? curves.curves_range().first() :
378 curves.curves_range().last();
379 const IndexRange curve_points = curves.points_by_curve()[active_curve];
380 const int last_active_point = curve_points.last();
381
382 Set<std::string> point_attributes_to_skip;
383 Set<std::string> curve_attributes_to_skip;
384 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
385 curves.positions_for_write()[last_active_point] = start_location;
386 self.drawing_->radii_for_write()[last_active_point] = start_radius;
387 self.drawing_->opacities_for_write()[last_active_point] = start_opacity;
388 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
389 if (use_vertex_color_ || attributes.contains("vertex_color")) {
390 self.drawing_->vertex_colors_for_write()[last_active_point] = vertex_color_;
391 point_attributes_to_skip.add("vertex_color");
392 }
393 if (use_fill || attributes.contains("fill_color")) {
394 self.drawing_->fill_colors_for_write()[active_curve] = fill_color_;
395 curve_attributes_to_skip.add("fill_color");
396 }
397 if (bke::SpanAttributeWriter<float> delta_times =
398 attributes.lookup_or_add_for_write_span<float>("delta_time", bke::AttrDomain::Point))
399 {
400 delta_times.span[last_active_point] = 0.0f;
401 point_attributes_to_skip.add("delta_time");
402 delta_times.finish();
403 }
404
406 "material_index", bke::AttrDomain::Curve);
408 "cyclic", bke::AttrDomain::Curve);
409 cyclic.span[active_curve] = false;
410 materials.span[active_curve] = material_index;
411 curve_attributes_to_skip.add_multiple({"material_index", "cyclic"});
412 cyclic.finish();
413 materials.finish();
414
415 if (bke::SpanAttributeWriter<float> softness = attributes.lookup_or_add_for_write_span<float>(
416 "softness", bke::AttrDomain::Curve))
417 {
418 softness.span[active_curve] = softness_;
419 curve_attributes_to_skip.add("softness");
420 softness.finish();
421 }
423 "u_scale", bke::AttrDomain::Curve))
424 {
425 u_scale.span[active_curve] = 1.0f;
426 curve_attributes_to_skip.add("u_scale");
427 u_scale.finish();
428 }
429 if (bke::SpanAttributeWriter<float> aspect_ratio =
430 attributes.lookup_or_add_for_write_span<float>("aspect_ratio", bke::AttrDomain::Curve))
431 {
432 aspect_ratio.span[active_curve] = aspect_ratio_;
433 curve_attributes_to_skip.add("aspect_ratio");
434 aspect_ratio.finish();
435 }
436
437 if (settings_->uv_random > 0.0f || attributes.contains("rotation")) {
438 if (bke::SpanAttributeWriter<float> rotations =
439 attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point))
440 {
441 rotations.span[last_active_point] = start_rotation;
442 point_attributes_to_skip.add("rotation");
443 rotations.finish();
444 }
445 }
446
447 /* Only set the attribute if the type is not the default or if it already exists. */
448 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
449 if (bke::SpanAttributeWriter<int8_t> start_caps =
450 attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve))
451 {
452 start_caps.span[active_curve] = settings_->caps_type;
453 curve_attributes_to_skip.add("start_cap");
454 start_caps.finish();
455 }
456 }
457
458 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
460 attributes.lookup_or_add_for_write_span<int8_t>("end_cap", bke::AttrDomain::Curve))
461 {
462 end_caps.span[active_curve] = settings_->caps_type;
463 curve_attributes_to_skip.add("end_cap");
464 end_caps.finish();
465 }
466 }
467
468 if (use_fill && (start_opacity < 1.0f || attributes.contains("fill_opacity"))) {
469 if (bke::SpanAttributeWriter<float> fill_opacities =
470 attributes.lookup_or_add_for_write_span<float>(
471 "fill_opacity",
474 {
475 fill_opacities.span[active_curve] = start_opacity;
476 curve_attributes_to_skip.add("fill_opacity");
477 fill_opacities.finish();
478 }
479 }
480
481 if (bke::SpanAttributeWriter<float> init_times =
482 attributes.lookup_or_add_for_write_span<float>("init_time", bke::AttrDomain::Curve))
483 {
484 /* Truncating time in ms to uint32 then we don't lose precision in lower bits. */
485 init_times.span[active_curve] = float(uint64_t(self.start_time_ * 1e3)) / float(1e3);
486 curve_attributes_to_skip.add("init_time");
487 init_times.finish();
488 }
489
490 if (self.do_fill_guides_) {
491 if (bke::SpanAttributeWriter<bool> is_fill_boundary =
492 attributes.lookup_or_add_for_write_span<bool>(".is_fill_guide",
494 {
495 is_fill_boundary.span[active_curve] = true;
496 curve_attributes_to_skip.add(".is_fill_guide");
497 is_fill_boundary.finish();
498 }
499 }
500
501 curves.curve_types_for_write()[active_curve] = CURVE_TYPE_POLY;
502 curve_attributes_to_skip.add("curve_type");
503 curves.update_curve_types();
504
505 if (self.placement_.use_project_to_stroke()) {
506 self.stroke_placement_depths_.append(self.stroke_placement_depths_.is_empty() ?
507 std::nullopt :
508 self.stroke_placement_depths_.last());
509 /* Initialize the snap point. */
510 self.update_stroke_depth_placement(C, start_sample);
511 }
512
513 /* Initialize the rest of the attributes with default values. */
515 attributes,
517 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
518 IndexRange(last_active_point, 1));
520 attributes,
522 bke::attribute_filter_from_skip_ref(curve_attributes_to_skip),
523 IndexRange(active_curve, 1));
524
525 self.drawing_->tag_topology_changed();
526 }
527
528 void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
529 {
530 const Span<float2> coords_to_smooth = self.screen_space_coords_orig_.as_span().slice(
531 smooth_window);
532
533 /* Detect corners in the current slice of coordinates. */
534 const float corner_min_radius_px = 5.0f;
535 const float corner_max_radius_px = 30.0f;
536 const int64_t corner_max_samples = 64;
537 const float corner_angle_threshold = 0.6f;
538 IndexMaskMemory memory;
540 coords_to_smooth.drop_front(1).drop_back(1),
541 corner_min_radius_px,
542 corner_max_radius_px,
543 corner_max_samples,
544 corner_angle_threshold,
545 memory);
546
547 /* Pre-blur the coordinates for the curve fitting. This generally leads to a better (more
548 * stable) fit. */
549 Array<float2> coords_pre_blur(smooth_window.size());
550 const int pre_blur_iterations = 3;
552 coords_to_smooth,
553 pre_blur_iterations,
554 VArray<float>::ForSingle(settings_->active_smooth, smooth_window.size()),
555 true,
556 true,
557 false,
558 coords_pre_blur.as_mutable_span());
559
560 /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */
561 const float max_error_threshold_px = 5.0f;
563 coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask);
564
565 /* Sampling the curve at a fixed resolution. */
566 const int64_t sample_resolution = 32;
567 Array<float2> sampled_curve_points = sample_curve_2d(curve_points, sample_resolution);
568
569 /* Morphing the coordinates onto the curve. Result is stored in a temporary array. */
570 Array<float2> coords_smoothed(coords_to_smooth.size());
571 morph_points_to_curve(coords_to_smooth, sampled_curve_points, coords_smoothed);
572
573 MutableSpan<float2> window_coords = self.screen_space_smoothed_coords_.as_mutable_span().slice(
574 smooth_window);
575 const float converging_threshold_px = 0.1f;
576 bool stop_counting_converged = false;
577 int num_converged = 0;
578 for (const int64_t window_i : smooth_window.index_range()) {
579 /* Record the curve fitting of this point. */
580 self.screen_space_curve_fitted_coords_[window_i].append(coords_smoothed[window_i]);
581 Span<float2> fit_coords = self.screen_space_curve_fitted_coords_[window_i];
582
583 /* We compare the previous arithmetic mean to the current. Going from the back to the front,
584 * if a point hasn't moved by a minimum threshold, it counts as converged. */
585 float2 new_pos = arithmetic_mean(fit_coords);
586 if (!stop_counting_converged) {
587 float2 prev_pos = window_coords[window_i];
588 if (math::distance(new_pos, prev_pos) < converging_threshold_px) {
589 num_converged++;
590 }
591 else {
592 stop_counting_converged = true;
593 }
594 }
595
596 /* Update the positions in the current cache. */
597 window_coords[window_i] = new_pos;
598 }
599
600 /* Remove all the converged points from the active window and shrink the window accordingly. */
601 if (num_converged > 0) {
602 self.active_smooth_start_index_ += num_converged;
603 self.screen_space_curve_fitted_coords_.remove(0, num_converged);
604 }
605 }
606
608 const int new_points_num,
609 const float brush_radius_px,
610 const float pressure,
611 const IndexRange active_window,
612 MutableSpan<float3> curve_positions)
613 {
614 float jitter_factor = 1.0f;
616 jitter_factor = BKE_curvemapping_evaluateF(settings_->curve_jitter, 0, pressure);
617 }
618 const float2 tangent = math::normalize(self.smoothed_pen_direction_);
619 const float2 cotangent = float2(-tangent.y, tangent.x);
620 for ([[maybe_unused]] const int _ : IndexRange(new_points_num)) {
621 const float rand = self.rng_.get_float() * 2.0f - 1.0f;
622 const float factor = rand * settings_->draw_jitter * jitter_factor;
623 self.screen_space_jitter_offsets_.append(cotangent * factor * brush_radius_px);
624 }
625 const Span<float2> jitter_slice = self.screen_space_jitter_offsets_.as_mutable_span().slice(
626 active_window);
627 MutableSpan<float2> smoothed_coords =
628 self.screen_space_smoothed_coords_.as_mutable_span().slice(active_window);
629 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
630 active_window);
631 MutableSpan<float3> positions_slice = curve_positions.slice(active_window);
632 if (self.placement_.use_project_to_stroke()) {
633 BLI_assert(self.stroke_placement_depths_.size() == self.screen_space_coords_orig_.size());
634 const Span<std::optional<float>> stroke_depths =
635 self.stroke_placement_depths_.as_span().slice(active_window);
636 for (const int64_t window_i : active_window.index_range()) {
637 final_coords[window_i] = smoothed_coords[window_i] + jitter_slice[window_i];
638 const std::optional<float> depth = stroke_depths[window_i];
639 positions_slice[window_i] = depth ? self.placement_.place(final_coords[window_i], *depth) :
640 self.placement_.project(final_coords[window_i]);
641 }
642 }
643 else {
644 for (const int64_t window_i : active_window.index_range()) {
645 final_coords[window_i] = smoothed_coords[window_i] + jitter_slice[window_i];
646 positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
647 }
648 }
649 }
650
652 const bContext &C,
653 const InputSample &extension_sample)
654 {
655 Scene *scene = CTX_data_scene(&C);
656 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
657 const ARegion *region = CTX_wm_region(&C);
658 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
659
660 const float2 coords = extension_sample.mouse_position;
661 float3 position;
662 if (self.placement_.use_project_to_stroke()) {
663 const std::optional<float> depth = self.stroke_placement_depths_.is_empty() ?
664 std::nullopt :
665 self.stroke_placement_depths_.last();
666 if (depth) {
667 position = self.placement_.place(coords, *depth);
668 }
669 else {
670 position = self.placement_.project(coords);
671 }
672 }
673 else {
674 position = self.placement_.project(coords);
675 }
676
678 region,
679 brush_,
680 extension_sample.pressure,
681 position,
682 self.placement_.to_world_space(),
683 settings_);
685 extension_sample.pressure, brush_, settings_);
686
687 const float brush_radius_px = brush_radius_to_pixel_radius(
688 rv3d, brush_, math::transform_point(self.placement_.to_world_space(), position));
689
690 bke::CurvesGeometry &curves = self.drawing_->strokes_for_write();
691 OffsetIndices<int> points_by_curve = curves.points_by_curve();
692 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
693
694 const int active_curve = on_back ? curves.curves_range().first() :
695 curves.curves_range().last();
696 const IndexRange curve_points = points_by_curve[active_curve];
697 const int last_active_point = curve_points.last();
698
699 const float2 prev_coords = self.screen_space_coords_orig_.last();
700 float prev_radius = self.drawing_->radii()[last_active_point];
701 const float prev_opacity = self.drawing_->opacities()[last_active_point];
702 const ColorGeometry4f prev_vertex_color = self.drawing_->vertex_colors()[last_active_point];
703
704 const bool is_first_sample = (curve_points.size() == 1);
705
706 /* Use the vector from the previous to the next point. Set the direction based on the first two
707 * samples. For subsequent samples, interpolate with the previous direction to get a smoothed
708 * value over time. */
709 if (is_first_sample) {
710 self.smoothed_pen_direction_ = self.screen_space_coords_orig_.last() - coords;
711 }
712 else {
713 /* The smoothing rate is a factor from 0 to 1 that represents how quickly the
714 * `smoothed_pen_direction_` "reacts" to changes in direction.
715 * - 1.0f: Immediate reaction.
716 * - 0.0f: No reaction (value never changes). */
717 constexpr float smoothing_rate_factor = 0.3f;
718 self.smoothed_pen_direction_ = math::interpolate(self.smoothed_pen_direction_,
719 self.screen_space_coords_orig_.last() -
720 coords,
721 smoothing_rate_factor);
722 }
723
724 /* Approximate brush with non-circular shape by changing the radius based on the angle. */
725 float radius_factor = 1.0f;
726 if (settings_->draw_angle_factor > 0.0f) {
727 /* `angle` is the angle to the horizontal line in screen space. */
728 const float angle = settings_->draw_angle;
729 const float2 angle_vec = float2(math::cos(angle), math::sin(angle));
730
731 /* The angle factor is 1.0f when the direction is aligned with the angle vector and 0.0f when
732 * it is orthogonal to the angle vector. This is consistent with the behavior from GPv2. */
733 const float angle_factor = math::abs(
734 math::dot(angle_vec, math::normalize(self.smoothed_pen_direction_)));
735
736 /* Influence is controlled by `draw_angle_factor`. */
737 radius_factor = math::interpolate(1.0f, angle_factor, settings_->draw_angle_factor);
738 radius *= radius_factor;
739 }
740
741 /* Overwrite last point if it's very close. */
742 const float distance_px = math::distance(coords, prev_coords);
743 constexpr float point_override_threshold_px = 2.0f;
744 if (distance_px < point_override_threshold_px) {
745 self.accum_distance_ += distance_px;
746 /* Don't move the first point of the stroke. */
747 if (!is_first_sample) {
748 curves.positions_for_write()[last_active_point] = position;
749 }
750 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
752 self.stroke_random_radius_factor_,
753 self.accum_distance_,
754 radius,
755 extension_sample.pressure);
756 }
757 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
759 self.stroke_random_opacity_factor_,
760 self.accum_distance_,
761 opacity,
762 extension_sample.pressure);
763 }
764 self.drawing_->radii_for_write()[last_active_point] = math::max(radius, prev_radius);
765 self.drawing_->opacities_for_write()[last_active_point] = math::max(opacity, prev_opacity);
766 return;
767 }
768
769 /* Adjust the first points radius based on the computed angle. */
770 if (is_first_sample && settings_->draw_angle_factor > 0.0f) {
771 self.drawing_->radii_for_write()[last_active_point] *= radius_factor;
772 prev_radius = self.drawing_->radii()[last_active_point];
773 }
774
775 /* Clamp the number of points within a pixel in screen space. */
776 constexpr int max_points_per_pixel = 4;
777 /* The value `brush_->spacing` is a percentage of the brush radius in pixels. */
778 const float max_spacing_px = math::max((float(brush_->spacing) / 100.0f) *
779 float(brush_radius_px),
780 1.0f / float(max_points_per_pixel));
781 /* If the next sample is far away, we subdivide the segment to add more points. */
782 const int new_points_num = (distance_px > max_spacing_px) ?
783 int(math::floor(distance_px / max_spacing_px)) :
784 1;
785 /* Resize the curves geometry. */
787 curves, on_back == false, curve_points.size() + new_points_num);
788
789 Set<std::string> point_attributes_to_skip;
790 /* Subdivide new segment. */
791 const IndexRange new_points = curves.points_by_curve()[active_curve].take_back(new_points_num);
792 Array<float2> new_screen_space_coords(new_points_num);
793 MutableSpan<float3> positions = curves.positions_for_write();
794 MutableSpan<float3> new_positions = positions.slice(new_points);
795 MutableSpan<float> new_radii = self.drawing_->radii_for_write().slice(new_points);
796 MutableSpan<float> new_opacities = self.drawing_->opacities_for_write().slice(new_points);
797
798 /* Interpolate the screen space positions. */
799 linear_interpolation<float2>(prev_coords, coords, new_screen_space_coords, is_first_sample);
800 linear_interpolation<float>(prev_radius, radius, new_radii, is_first_sample);
801 linear_interpolation<float>(prev_opacity, opacity, new_opacities, is_first_sample);
802 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
803
804 /* Randomize radii. */
805 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
806 for (const int i : IndexRange(new_points_num)) {
808 self.stroke_random_radius_factor_,
809 self.accum_distance_ +
810 max_spacing_px * i,
811 new_radii[i],
812 extension_sample.pressure);
813 }
814 }
815
816 /* Randomize opacities. */
817 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
818 for (const int i : IndexRange(new_points_num)) {
820 self.stroke_random_opacity_factor_,
821 self.accum_distance_ +
822 max_spacing_px * i,
823 new_opacities[i],
824 extension_sample.pressure);
825 }
826 }
827
828 /* Randomize rotations. */
829 if (use_settings_random_ && (settings_->uv_random > 0.0f || attributes.contains("rotation"))) {
830 if (bke::SpanAttributeWriter<float> rotations =
831 attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point))
832 {
833 const MutableSpan<float> new_rotations = rotations.span.slice(new_points);
834 for (const int i : IndexRange(new_points_num)) {
836 *settings_,
837 self.rng_,
838 self.stroke_random_rotation_factor_,
839 extension_sample.pressure);
840 }
841 point_attributes_to_skip.add("rotation");
842 rotations.finish();
843 }
844 }
845
846 /* Randomize vertex color. */
847 if (use_vertex_color_ || attributes.contains("vertex_color")) {
848 MutableSpan<ColorGeometry4f> new_vertex_colors =
849 self.drawing_->vertex_colors_for_write().slice(new_points);
851 prev_vertex_color, vertex_color_, new_vertex_colors, is_first_sample);
852 if (use_settings_random_ || attributes.contains("vertex_color")) {
853 for (const int i : IndexRange(new_points_num)) {
854 new_vertex_colors[i] = ed::greasepencil::randomize_color(*settings_,
856 self.stroke_random_hue_factor_,
857 self.stroke_random_sat_factor_,
858 self.stroke_random_val_factor_,
859 self.accum_distance_ +
860 max_spacing_px * i,
861 new_vertex_colors[i],
862 extension_sample.pressure);
863 }
864 }
865 point_attributes_to_skip.add("vertex_color");
866 }
867
868 const double new_delta_time = BLI_time_now_seconds() - self.start_time_;
869 if (bke::SpanAttributeWriter<float> delta_times =
870 attributes.lookup_or_add_for_write_span<float>("delta_time", bke::AttrDomain::Point))
871 {
872 linear_interpolation<float>(float(self.delta_time_),
873 float(new_delta_time),
874 delta_times.span.slice(new_points),
875 is_first_sample);
876 point_attributes_to_skip.add("delta_time");
877 delta_times.finish();
878 }
879
880 /* Update the accumulated distance along the stroke in pixels. */
881 self.accum_distance_ += distance_px;
882
883 /* Update the current delta time. */
884 self.delta_time_ = new_delta_time;
885
886 /* Update screen space buffers with new points. */
887 self.screen_space_coords_orig_.extend(new_screen_space_coords);
888 self.screen_space_smoothed_coords_.extend(new_screen_space_coords);
889 self.screen_space_final_coords_.extend(new_screen_space_coords);
890 for (float2 new_position : new_screen_space_coords) {
891 self.screen_space_curve_fitted_coords_.append(Vector<float2>({new_position}));
892 }
893 if (self.placement_.use_project_to_stroke()) {
894 const std::optional<float> last_depth = self.stroke_placement_depths_.is_empty() ?
895 std::nullopt :
896 self.stroke_placement_depths_.last();
897 self.stroke_placement_depths_.append_n_times(last_depth, new_points_num);
898 }
899
900 /* Only start smoothing if there are enough points. */
901 constexpr int64_t min_active_smoothing_points_num = 8;
902 const IndexRange smooth_window = self.screen_space_coords_orig_.index_range().drop_front(
903 self.active_smooth_start_index_);
904 if (smooth_window.size() < min_active_smoothing_points_num) {
905 if (self.placement_.use_project_to_stroke()) {
906 const Span<std::optional<float>> new_depths =
907 self.stroke_placement_depths_.as_mutable_span().take_back(new_points_num);
908 for (const int64_t i : new_positions.index_range()) {
909 const std::optional<float> depth = new_depths[i];
910 if (depth) {
911 new_positions[i] = self.placement_.place(coords, *depth);
912 }
913 else {
914 new_positions[i] = self.placement_.project(coords);
915 }
916 }
917 }
918 else {
919 self.placement_.project(new_screen_space_coords, new_positions);
920 }
921 }
922 else {
923 /* Active smoothing is done in a window at the end of the new stroke.
924 * Final positions are written below. */
925 this->active_smoothing(self, smooth_window);
926 }
927
928 /* Jitter uses smoothed coordinates as input. In case smoothing is not applied these are the
929 * unsmoothed original coordinates. */
930 MutableSpan<float3> curve_positions = positions.slice(curves.points_by_curve()[active_curve]);
931 if (use_settings_random_ && settings_->draw_jitter > 0.0f) {
932 this->active_jitter(self,
933 new_points_num,
934 brush_radius_px,
935 extension_sample.pressure,
936 smooth_window,
937 curve_positions);
938 }
939 else {
940 MutableSpan<float2> smoothed_coords =
941 self.screen_space_smoothed_coords_.as_mutable_span().slice(smooth_window);
942 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
943 smooth_window);
944 /* Not jitter, so we just copy the positions over. */
945 final_coords.copy_from(smoothed_coords);
946 MutableSpan<float3> curve_positions_slice = curve_positions.slice(smooth_window);
947 if (self.placement_.use_project_to_stroke()) {
948 BLI_assert(self.stroke_placement_depths_.size() == self.screen_space_coords_orig_.size());
949 const Span<std::optional<float>> stroke_depths =
950 self.stroke_placement_depths_.as_mutable_span().slice(smooth_window);
951 for (const int64_t window_i : smooth_window.index_range()) {
952 const std::optional<float> depth = stroke_depths[window_i];
953 curve_positions_slice[window_i] = depth ?
954 self.placement_.place(final_coords[window_i],
955 *depth) :
956 self.placement_.project(final_coords[window_i]);
957 }
958 }
959 else {
960 for (const int64_t window_i : smooth_window.index_range()) {
961 curve_positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
962 }
963 }
964 }
965
966 if (self.placement_.use_project_to_stroke()) {
967 /* Find a new snap point and apply projection to trailing points. */
968 self.update_stroke_depth_placement(C, extension_sample);
969 }
970
971 /* Initialize the rest of the attributes with default values. */
973 attributes,
975 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
976 curves.points_range().take_back(new_points_num));
977
978 self.drawing_->set_texture_matrices({self.texture_space_},
979 IndexRange::from_single(active_curve));
980 }
981
982 void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
983 {
984 const Scene *scene = CTX_data_scene(&C);
985 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
986
987 this->process_extension_sample(self, C, extension_sample);
988
989 const bke::CurvesGeometry &curves = self.drawing_->strokes();
990 const int active_curve = on_back ? curves.curves_range().first() :
991 curves.curves_range().last();
992 self.drawing_->tag_topology_changed(IndexRange::from_single(active_curve));
993 }
994};
995
1001
1003{
1004 /* gpencil_v3d_align is an awkward combination of multiple properties. If none of the non-zero
1005 * flags are set the AllPoints mode is the default. */
1006 const Scene &scene = *CTX_data_scene(&C);
1007 const char align_flags = scene.toolsettings->gpencil_v3d_align;
1008 if (align_flags & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) {
1010 }
1011 if (align_flags & GP_PROJECT_DEPTH_STROKE_FIRST) {
1013 }
1015}
1016
1018{
1019 BLI_assert(placement_.use_project_to_stroke());
1020
1021 const std::optional<float> new_stroke_placement_depth = placement_.get_depth(
1022 sample.mouse_position);
1023 if (!new_stroke_placement_depth) {
1024 return false;
1025 }
1026
1027 const StrokeSnapMode snap_mode = get_snap_mode(C);
1028 switch (snap_mode) {
1030 const float start_depth = last_stroke_placement_depth_ ? *last_stroke_placement_depth_ :
1031 *new_stroke_placement_depth;
1032 const float end_depth = *new_stroke_placement_depth;
1033 const IndexRange reprojected_points = this->interpolate_stroke_depth(
1034 C, last_stroke_placement_point_, start_depth, end_depth);
1035 /* Only reproject newly added points next time a hit point is found. */
1036 if (!reprojected_points.is_empty()) {
1037 last_stroke_placement_point_ = reprojected_points.one_after_last();
1038 }
1039
1040 last_stroke_placement_depth_ = new_stroke_placement_depth;
1041 break;
1042 }
1044 const float start_depth = last_stroke_placement_depth_ ? *last_stroke_placement_depth_ :
1045 *new_stroke_placement_depth;
1046 const float end_depth = *new_stroke_placement_depth;
1047 const IndexRange reprojected_points = this->interpolate_stroke_depth(
1048 C, last_stroke_placement_point_, start_depth, end_depth);
1049
1050 /* Only update depth on the first hit. */
1051 if (!last_stroke_placement_depth_) {
1052 /* Keep reprojecting all points from the first hit onward. */
1053 if (!reprojected_points.is_empty()) {
1054 last_stroke_placement_point_ = reprojected_points.one_after_last();
1055 }
1056 last_stroke_placement_depth_ = new_stroke_placement_depth;
1057 }
1058 break;
1059 }
1061 /* Only reproject once in "First Point" mode. */
1062 if (!last_stroke_placement_depth_) {
1063 const float start_depth = *new_stroke_placement_depth;
1064 const float end_depth = *new_stroke_placement_depth;
1065 this->interpolate_stroke_depth(C, last_stroke_placement_point_, start_depth, end_depth);
1066
1067 last_stroke_placement_depth_ = new_stroke_placement_depth;
1068 break;
1069 }
1070 }
1071 }
1072
1073 return true;
1074}
1075
1077 std::optional<int> start_point,
1078 const float from_depth,
1079 const float to_depth)
1080{
1081 using namespace blender::bke;
1082
1083 Scene *scene = CTX_data_scene(&C);
1084 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
1085
1086 /* Drawing should exist. */
1087 BLI_assert(drawing_);
1088 bke::greasepencil::Drawing &drawing = *drawing_;
1089 const int active_curve = on_back ? drawing.strokes().curves_range().first() :
1090 drawing.strokes().curves_range().last();
1091 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1092 const IndexRange all_points = points_by_curve[active_curve];
1093 BLI_assert(screen_space_final_coords_.size() == all_points.size());
1094 if (all_points.is_empty()) {
1095 return {};
1096 }
1097
1098 IndexRange active_points = all_points;
1099 if (start_point) {
1100 active_points = IndexRange::from_begin_end_inclusive(*start_point, all_points.last());
1101 }
1102 if (active_points.is_empty()) {
1103 return {};
1104 }
1105
1106 /* Point slice relative to the curve, valid for 2D coordinate array. */
1107 const IndexRange active_curve_points = active_points.shift(-all_points.start());
1108
1109 MutableSpan<std::optional<float>> depths = stroke_placement_depths_.as_mutable_span().slice(
1110 active_curve_points);
1111 MutableSpan<float3> positions = drawing.strokes_for_write().positions_for_write().slice(
1112 active_points);
1113 const Span<float2> final_coords = screen_space_final_coords_.as_span().slice(
1114 active_curve_points);
1115 const float step_size = 1.0f / std::max(int(active_points.size()) - 1, 1);
1116 for (const int i : positions.index_range()) {
1117 /* Update the placement depth for later reprojection (active smoothing). */
1118 depths[i] = math::interpolate(from_depth, to_depth, float(i) * step_size);
1119 positions[i] = placement_.place(final_coords[i], *depths[i]);
1120 }
1121
1122 return active_points;
1123}
1124
1126{
1128 Brush *current_brush = BKE_paint_brush(paint);
1129
1130 fill_guides_brush_ = create_fill_guide_brush();
1131 BLI_assert(fill_guides_brush_ != nullptr);
1132 BKE_paint_brush_set(paint, fill_guides_brush_);
1133
1134 saved_active_brush_ = current_brush;
1135}
1136
1138{
1140 BLI_assert(saved_active_brush_ != nullptr);
1141 BKE_paint_brush_set(paint, saved_active_brush_);
1142 saved_active_brush_ = nullptr;
1143 /* Free the temporary brush. */
1144 BKE_id_free_ex(nullptr, fill_guides_brush_, LIB_ID_FREE_NO_MAIN, false);
1145 fill_guides_brush_ = nullptr;
1146}
1147
1149{
1150 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
1151 ARegion *region = CTX_wm_region(&C);
1153 Scene *scene = CTX_data_scene(&C);
1154 Object *object = CTX_data_active_object(&C);
1155 Object *eval_object = DEG_get_evaluated(depsgraph, object);
1156 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1157
1158 if (do_fill_guides_) {
1160 }
1161
1162 Paint *paint = &scene->toolsettings->gp_paint->paint;
1164
1165 if (brush->gpencil_settings == nullptr) {
1167 }
1168 BrushGpencilSettings *settings = brush->gpencil_settings;
1169
1176 BKE_curvemapping_init(brush->curve_rand_hue);
1177 BKE_curvemapping_init(brush->curve_rand_saturation);
1178 BKE_curvemapping_init(brush->curve_rand_value);
1179
1180 BLI_assert(grease_pencil->has_active_layer());
1181 const bke::greasepencil::Layer &layer = *grease_pencil->get_active_layer();
1182 /* Initialize helper class for projecting screen space coordinates. */
1183 placement_ = ed::greasepencil::DrawingPlacement(*scene, *region, *view3d, *eval_object, &layer);
1184 if (placement_.use_project_to_surface()) {
1185 placement_.cache_viewport_depths(depsgraph, region, view3d);
1186 }
1187 else if (placement_.use_project_to_stroke()) {
1188 placement_.cache_viewport_depths(depsgraph, region, view3d);
1189 }
1190
1192 scene, region, start_sample.mouse_position, placement_);
1193
1194 /* `View` is already stored in object space but all others are in layer space. */
1196 texture_space_ = texture_space_ * layer.to_object_space(*object);
1197 }
1198
1200 if ((settings->flag & GP_BRUSH_GROUP_RANDOM) != 0) {
1201 /* Since we want stroke properties to randomize around set values, it's easier for us to have a
1202 * signed value in range (-1,1) in calculations downstream. */
1203 stroke_random_radius_factor_ = rng_.get_float() * 2.0f - 1.0f;
1204 stroke_random_opacity_factor_ = rng_.get_float() * 2.0f - 1.0f;
1205 stroke_random_rotation_factor_ = rng_.get_float() * 2.0f - 1.0f;
1206
1207 stroke_random_hue_factor_ = rng_.get_float() * 2.0f - 1.0f;
1208 stroke_random_sat_factor_ = rng_.get_float() * 2.0f - 1.0f;
1209 stroke_random_val_factor_ = rng_.get_float() * 2.0f - 1.0f;
1210 }
1211
1213 CTX_data_main(&C), object, brush);
1214 const int material_index = BKE_object_material_index_get(object, material);
1215 const bool use_fill = (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
1216
1217 frame_number_ = scene->r.cfra;
1218 drawing_ = grease_pencil->get_editable_drawing_at(layer, frame_number_);
1219 multi_frame_drawings_ = ed::greasepencil::retrieve_editable_drawings(*scene, *grease_pencil);
1220 BLI_assert(drawing_ != nullptr);
1221
1222 /* We're now starting to draw. */
1223 grease_pencil->runtime->is_drawing_stroke = true;
1224
1225 /* Initialize the start time to the current time. */
1226 start_time_ = BLI_time_now_seconds();
1227 /* Delta time starts at 0. */
1228 delta_time_ = 0.0f;
1229
1230 PaintOperationExecutor executor{C};
1231 executor.process_start_sample(*this, C, start_sample, material_index, use_fill);
1232
1233 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1234 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1235}
1236
1237void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
1238{
1239 Object *object = CTX_data_active_object(&C);
1240 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1241
1242 PaintOperationExecutor executor{C};
1243 executor.execute(*this, C, extension_sample);
1244
1245 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1246 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1247}
1248
1250 const float influence,
1251 const int iterations,
1252 const int active_curve)
1253{
1255 const IndexRange stroke = IndexRange::from_single(active_curve);
1256 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1257 const VArray<bool> cyclic = curves.cyclic();
1258 const VArray<bool> point_selection = VArray<bool>::ForSingle(true, curves.points_num());
1259
1260 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1261 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1263 points_by_curve,
1264 point_selection,
1265 cyclic,
1266 iterations,
1267 influence,
1268 false,
1269 true,
1270 positions.span);
1271 positions.finish();
1272 drawing.tag_positions_changed();
1273
1274 if (drawing.opacities().is_span()) {
1275 bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
1277 points_by_curve,
1278 point_selection,
1279 cyclic,
1280 iterations,
1281 influence,
1282 true,
1283 false,
1284 opacities.span);
1285 opacities.finish();
1286 }
1287 if (drawing.radii().is_span()) {
1288 bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
1290 points_by_curve,
1291 point_selection,
1292 cyclic,
1293 iterations,
1294 influence,
1295 true,
1296 false,
1297 radii.span);
1298 radii.finish();
1299 }
1300}
1301
1303 const float epsilon,
1304 const int active_curve)
1305{
1306 const bke::CurvesGeometry &curves = drawing.strokes();
1307 const bke::AttributeAccessor attributes = curves.attributes();
1308 const IndexRange points = curves.points_by_curve()[active_curve];
1309 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1310 ".draw_tool_screen_space_positions");
1311 BLI_assert(screen_space_positions_attribute.is_span());
1312
1313 const Span<float2> screen_space_positions =
1314 screen_space_positions_attribute.get_internal_span().slice(points);
1315
1316 Array<bool> points_to_delete_arr(drawing.strokes().points_num(), false);
1317 points_to_delete_arr.as_mutable_span().slice(points).fill(true);
1318 geometry::curve_simplify(curves.positions().slice(points),
1319 curves.cyclic()[active_curve],
1320 epsilon,
1321 screen_space_positions,
1322 points_to_delete_arr.as_mutable_span().slice(points));
1323
1324 IndexMaskMemory memory;
1325 const IndexMask points_to_delete = IndexMask::from_bools(points_to_delete_arr, memory);
1326 if (!points_to_delete.is_empty()) {
1327 drawing.strokes_for_write().remove_points(points_to_delete, {});
1328 drawing.tag_topology_changed();
1329 }
1330}
1331
1332static void add_strokes_to_drawing(const bool on_back,
1333 Curves *strokes,
1335{
1336 Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
1337 std::array<bke::GeometrySet, 2> geometry_sets;
1338 if (on_back) {
1339 geometry_sets = {bke::GeometrySet::from_curves(strokes),
1340 bke::GeometrySet::from_curves(other_curves)};
1341 }
1342 else {
1343 geometry_sets = {bke::GeometrySet::from_curves(other_curves),
1345 }
1346 drawing.strokes_for_write() = std::move(
1347 geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
1348 drawing.tag_topology_changed();
1349}
1350
1352 const int active_curve,
1353 const bool on_back)
1354{
1355 const bke::CurvesGeometry &curves = drawing.strokes();
1356 const IndexRange points = curves.points_by_curve()[active_curve];
1357 const bke::AttributeAccessor attributes = curves.attributes();
1358 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1359 ".draw_tool_screen_space_positions");
1360 BLI_assert(screen_space_positions_attribute.is_span());
1361 const Span<float2> screen_space_positions =
1362 screen_space_positions_attribute.get_internal_span().slice(points);
1363 /* Extract the drawn stroke into a separate geometry, so we can trim the ends for just this
1364 * stroke. */
1366 drawing.strokes(), IndexRange::from_single(active_curve), {});
1367 auto bounds = bounds::min_max(screen_space_positions);
1368 rcti screen_space_bounds;
1369 BLI_rcti_init(&screen_space_bounds,
1370 int(bounds->min.x),
1371 int(bounds->max.x),
1372 int(bounds->min.y),
1373 int(bounds->max.y));
1374 /* Use the first and last point. */
1375 const Vector<Vector<int>> point_selection = {{0, int(points.index_range().last())}};
1376 /* Trim the stroke ends by finding self intersections using the screen space positions. */
1378 stroke,
1379 screen_space_positions,
1380 {screen_space_bounds},
1382 point_selection,
1383 true);
1384
1385 /* No intersection found. */
1386 if (stroke_trimmed.is_empty()) {
1387 return;
1388 }
1389
1390 /* Remove the original stroke. */
1391 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1392
1393 /* Join the trimmed stroke into the drawing. */
1394 add_strokes_to_drawing(on_back, bke::curves_new_nomain(std::move(stroke_trimmed)), drawing);
1395}
1396
1398 const int active_curve,
1399 const float4x4 &viewmat,
1400 const ed::greasepencil::DrawingPlacement &placement,
1401 const float outline_radius,
1402 const int material_index,
1403 const bool on_back)
1404{
1405 /* Get the outline stroke (single curve). */
1407 drawing,
1408 IndexRange::from_single(active_curve),
1409 viewmat,
1410 3,
1411 outline_radius,
1412 0.0f,
1413 material_index);
1414
1415 /* Reproject the outline onto the drawing placement. */
1416 placement.reproject(outline.positions(), outline.positions_for_write());
1417
1418 /* Remove the original stroke. */
1419 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1420
1421 /* Join the outline stroke into the drawing. */
1422 add_strokes_to_drawing(on_back, bke::curves_new_nomain(std::move(outline)), drawing);
1423}
1424
1426 const float epsilon,
1427 const bool on_back,
1428 const int active_curve)
1429{
1430 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1432 const VArray<float> radii = drawing.radii();
1433
1434 /* Remove points at the end that have a radius close to 0. */
1435 int64_t num_points_to_remove = 0;
1436 for (int64_t index = points.last(); index >= points.first(); index--) {
1437 if (radii[index] < epsilon) {
1438 num_points_to_remove++;
1439 }
1440 else {
1441 break;
1442 }
1443 }
1444
1445 if (num_points_to_remove <= 0) {
1446 return 0;
1447 }
1448
1449 /* Don't remove the entire stroke. Leave at least one point. */
1450 if (points.size() - num_points_to_remove < 1) {
1451 num_points_to_remove = points.size() - 1;
1452 }
1453
1454 if (!on_back) {
1455 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1456 curves.offsets_for_write().last() = curves.points_num();
1457 return num_points_to_remove;
1458 }
1459
1460 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1461 const int last_active_point = curves.points_by_curve()[0].last();
1462
1463 /* Shift the data before resizing to not delete the data at the end. */
1464 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1465 if (iter.domain != bke::AttrDomain::Point) {
1466 return;
1467 }
1468
1470 GMutableSpan attribute_data = dst.span;
1471
1472 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1473 using T = decltype(dummy);
1474 MutableSpan<T> span_data = attribute_data.typed<T>();
1475
1476 for (int i = last_active_point - num_points_to_remove + 1;
1477 i < curves.points_num() - num_points_to_remove;
1478 i++)
1479 {
1480 span_data[i] = span_data[i + num_points_to_remove];
1481 }
1482 });
1483 dst.finish();
1484 });
1485
1486 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1487 MutableSpan<int> offsets = curves.offsets_for_write();
1488 for (const int src_curve : curves.curves_range().drop_front(1)) {
1489 offsets[src_curve] = offsets[src_curve] - num_points_to_remove;
1490 }
1491 offsets.last() = curves.points_num();
1492
1493 return num_points_to_remove;
1494}
1495
1496static void deselect_stroke(const bContext &C,
1498 const int active_curve)
1499{
1500 Scene *scene = CTX_data_scene(&C);
1501 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1502
1505 scene->toolsettings);
1506
1508 curves, selection_domain, CD_PROP_BOOL);
1509
1510 if (selection_domain == bke::AttrDomain::Curve) {
1512 }
1513 else if (selection_domain == bke::AttrDomain::Point) {
1514 ed::curves::fill_selection_false(selection.span.slice(points));
1515 }
1516
1517 selection.finish();
1518}
1519
1520static void process_stroke_weights(const Scene &scene,
1521 const Object &object,
1523 const int active_curve)
1524{
1526 const IndexRange points = curves.points_by_curve()[active_curve];
1527
1528 const int def_nr = BKE_object_defgroup_active_index_get(&object) - 1;
1529
1530 if (def_nr == -1) {
1531 return;
1532 }
1533
1534 const bDeformGroup *defgroup = static_cast<const bDeformGroup *>(
1535 BLI_findlink(BKE_object_defgroup_list(&object), def_nr));
1536
1537 const StringRef vertex_group_name = defgroup->name;
1538
1540 curves, IndexMask(points), vertex_group_name, scene.toolsettings->vgroup_weight);
1541
1542 if (scene.toolsettings->vgroup_weight == 0.0f) {
1543 return;
1544 }
1545
1546 /* Loop through all modifiers trying to find the pose channel for the vertex group name. */
1547 bPoseChannel *channel = nullptr;
1548 Object *ob_arm = nullptr;
1549 LISTBASE_FOREACH (ModifierData *, md, &(&object)->modifiers) {
1550 if (md->type != eModifierType_GreasePencilArmature) {
1551 continue;
1552 }
1553
1554 /* Skip not visible modifiers. */
1555 if (!(md->mode & eModifierMode_Realtime)) {
1556 continue;
1557 }
1558
1560 md);
1561 if (amd == nullptr) {
1562 continue;
1563 }
1564
1565 ob_arm = amd->object;
1566 /* Not an armature. */
1567 if (ob_arm->type != OB_ARMATURE || ob_arm->pose == nullptr) {
1568 continue;
1569 }
1570
1571 channel = BKE_pose_channel_find_name(ob_arm->pose, vertex_group_name.data());
1572 if (channel == nullptr) {
1573 continue;
1574 }
1575
1576 /* Found the channel. */
1577 break;
1578 }
1579
1580 /* Nothing valid was found. */
1581 if (channel == nullptr) {
1582 return;
1583 }
1584
1585 const float4x4 obinv = math::invert(object.object_to_world());
1586
1587 const float4x4 postmat = obinv * ob_arm->object_to_world();
1588 const float4x4 premat = math::invert(postmat);
1589
1590 const float4x4 matrix = postmat * math::invert(float4x4(channel->chan_mat)) * premat;
1591
1592 /* Update the position of the stroke to undo the movement caused by the modifier. */
1593 MutableSpan<float3> positions = curves.positions_for_write().slice(points);
1594 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
1595 for (float3 &position : positions.slice(range)) {
1596 position = math::transform_point(matrix, position);
1597 }
1598 });
1599}
1600
1602{
1603
1604 const IndexRange points = src.points_by_curve()[curve];
1605 bke::CurvesGeometry dst(points.size(), 1);
1606
1607 Array<int> src_offsets({points.first(), points.one_after_last()});
1608 Array<int> dst_offsets({0, int(points.size())});
1609
1613 {},
1614 src_offsets.as_span(),
1615 dst_offsets.as_span(),
1616 IndexMask{1},
1617 dst.attributes_for_write());
1618
1619 src_offsets = {curve, curve + 1};
1620 dst_offsets = {0, 1};
1621
1625 {},
1626 src_offsets.as_span(),
1627 dst_offsets.as_span(),
1628 IndexMask{1},
1629 dst.attributes_for_write());
1630 return dst;
1631}
1632
1634 const bke::CurvesGeometry &src_strokes,
1635 const int curve,
1636 const int current_frame,
1637 const bool on_back,
1639{
1640 const bke::CurvesGeometry stroke = get_single_stroke(src_strokes, curve);
1641
1642 for (const ed::greasepencil::MutableDrawingInfo &drawing_info : drawings) {
1643 if (drawing_info.frame_number == current_frame) {
1644 continue;
1645 }
1646 add_strokes_to_drawing(on_back, bke::curves_new_nomain(stroke), drawing_info.drawing);
1647 }
1648}
1649
1651{
1652 using namespace blender::bke;
1653 Scene *scene = CTX_data_scene(&C);
1654 Object *object = CTX_data_active_object(&C);
1656 const ARegion *region = CTX_wm_region(&C);
1657 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1658
1659 Paint *paint = &scene->toolsettings->gp_paint->paint;
1661 BrushGpencilSettings *settings = brush->gpencil_settings;
1662 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
1663 const bool do_post_processing = (settings->flag & GP_BRUSH_GROUP_SETTINGS) != 0;
1664 const bool do_automerge_endpoints = (scene->toolsettings->gpencil_flags &
1666
1667 /* Grease Pencil should have an active layer. */
1668 BLI_assert(grease_pencil.has_active_layer());
1669 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1670 /* Drawing should exist. */
1671 bke::greasepencil::Drawing &drawing = *drawing_;
1672 const int active_curve = on_back ? drawing.strokes().curves_range().first() :
1673 drawing.strokes().curves_range().last();
1674 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1675 const IndexRange points = points_by_curve[active_curve];
1676
1677 /* Write the screen space positions of the new stroke as a temporary attribute, so all the
1678 * changes in topology with the operations below get propagated correctly. */
1680 bke::SpanAttributeWriter<float2> screen_space_positions =
1681 attributes.lookup_or_add_for_write_only_span<float2>(".draw_tool_screen_space_positions",
1683 BLI_assert(screen_space_positions);
1684 screen_space_positions.span.slice(points).copy_from(this->screen_space_final_coords_);
1685 screen_space_positions.finish();
1686
1687 /* Remove trailing points with radii close to zero. */
1688 trim_end_points(drawing, 1e-5f, on_back, active_curve);
1689
1690 /* Set the selection of the newly drawn stroke to false. */
1691 deselect_stroke(C, drawing, active_curve);
1692
1693 if (do_post_processing) {
1694 if (settings->draw_smoothfac > 0.0f && settings->draw_smoothlvl > 0) {
1695 smooth_stroke(drawing, settings->draw_smoothfac, settings->draw_smoothlvl, active_curve);
1696 }
1697 if (settings->simplify_px > 0.0f) {
1698 simplify_stroke(drawing, settings->simplify_px, active_curve);
1699 }
1700 if ((settings->flag & GP_BRUSH_TRIM_STROKE) != 0) {
1701 trim_stroke_ends(drawing, active_curve, on_back);
1702 }
1704 process_stroke_weights(*scene, *object, drawing, active_curve);
1705 }
1706 if ((settings->flag & GP_BRUSH_OUTLINE_STROKE) != 0) {
1707 const float outline_radius = brush->unprojected_radius * settings->outline_fac * 0.5f;
1708 const int material_index = [&]() {
1710 CTX_data_main(&C), object, brush);
1711 return BKE_object_material_index_get(object, material);
1712 }();
1713 outline_stroke(drawing,
1714 active_curve,
1715 float4x4(rv3d->viewmat),
1716 placement_,
1717 outline_radius,
1718 material_index,
1719 on_back);
1720 }
1721 }
1722 /* Remove the temporary attribute. */
1723 attributes.remove(".draw_tool_screen_space_positions");
1724
1725 drawing.set_texture_matrices({texture_space_}, IndexRange::from_single(active_curve));
1726
1727 if (do_automerge_endpoints) {
1728 constexpr float merge_distance = 20.0f;
1729 const float4x4 layer_to_world = active_layer.to_world_space(*object);
1730 const IndexMask selection = IndexRange::from_single(active_curve);
1732 *region, drawing.strokes(), layer_to_world, merge_distance, selection, {});
1733 }
1734
1735 drawing.tag_topology_changed();
1736
1737 const bool use_multi_frame_editing = (scene->toolsettings->gpencil_flags &
1739
1740 if (use_multi_frame_editing) {
1742 drawing.strokes(), active_curve, frame_number_, on_back, multi_frame_drawings_);
1743 }
1744
1745 /* Now we're done drawing. */
1746 grease_pencil.runtime->is_drawing_stroke = false;
1747
1748 if (do_fill_guides_) {
1750 }
1751
1752 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1753 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil.id);
1754}
1755
1756std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation(const bool do_fill_guides)
1757{
1758 return std::make_unique<PaintOperation>(do_fill_guides);
1759}
1760
1761} // namespace blender::ed::sculpt_paint::greasepencil
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
const std::optional< BrushColorJitterSettings > BKE_brush_color_jitter_get_settings(const Scene *scene, const Paint *paint, const Brush *brush)
Definition brush.cc:1137
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:604
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
Low-level operations for curves.
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:596
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:574
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_alt_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
Material * BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
Utility functions for vertex groups in grease pencil objects.
@ LIB_ID_FREE_NO_MAIN
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1500
void BKE_id_free_ex(Main *bmain, void *idv, int flag_orig, bool use_flag_from_idtag)
General operations, lookup, etc. for materials.
int BKE_object_material_index_get(Object *ob, const Material *ma)
bool BKE_paint_brush_set(Paint *paint, Brush *brush)
Definition paint.cc:701
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
#define BLI_assert(a)
Definition BLI_assert.h:46
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
MINLINE void copy_v3_fl3(float v[3], float x, float y, float z)
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ GP_BRUSH_MODE_VERTEXCOLOR
@ GP_BRUSH_GROUP_RANDOM
@ GP_BRUSH_OUTLINE_STROKE
@ GP_BRUSH_TRIM_STROKE
@ GP_BRUSH_GROUP_SETTINGS
@ GP_BRUSH_USE_JITTER_PRESSURE
@ GP_BRUSH_USE_PRESSURE
@ BRUSH_LOCK_SIZE
@ GPPAINT_MODE_STROKE
@ GPPAINT_MODE_FILL
@ GPPAINT_MODE_BOTH
@ CURVE_TYPE_POLY
@ GP_STROKE_CAP_TYPE_ROUND
@ GP_MATERIAL_FILL_SHOW
@ eModifierMode_Realtime
@ eModifierType_GreasePencilArmature
@ OB_MODE_PAINT_GREASE_PENCIL
@ OB_ARMATURE
@ GP_LOCKAXIS_VIEW
@ GP_PROJECT_DEPTH_STROKE_ENDPOINTS
@ GP_PROJECT_DEPTH_STROKE_FIRST
@ GP_TOOL_FLAG_PAINT_ONBACK
@ GP_TOOL_FLAG_AUTOMERGE_STROKE
@ GP_TOOL_FLAG_CREATE_WEIGHTS
@ GP_USE_MULTI_FRAME_EDITING
float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
PyObject * self
BPy_StructRNA * depsgraph
long long int int64_t
unsigned long long int uint64_t
int64_t size() const
Definition BLI_array.hh:245
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
ChannelStorageType a
Definition BLI_color.hh:93
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t first() const
constexpr int64_t one_after_last() const
constexpr IndexRange shift(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr int64_t start() const
static constexpr IndexRange from_single(const int64_t index)
constexpr IndexRange index_range() const
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
bool add(const Key &key)
Definition BLI_set.hh:248
void add_multiple(Span< Key > keys)
Definition BLI_set.hh:287
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:171
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr Span drop_back(int64_t n) const
Definition BLI_span.hh:182
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr const char * data() const
Span< T > get_internal_span() const
static VArray ForSingle(T value, const int64_t size)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > positions() const
void remove_curves(const IndexMask &curves_to_delete, const AttributeFilter &attribute_filter)
AttributeAccessor attributes() const
void remove_points(const IndexMask &points_to_delete, const AttributeFilter &attribute_filter)
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
VArray< float > opacities() const
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
float4x4 to_world_space(const Object &object) const
float4x4 to_object_space(const Object &object) const
IndexRange interpolate_stroke_depth(const bContext &C, std::optional< int > start_point, float from_depth, float to_depth)
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
bool update_stroke_depth_placement(const bContext &C, const InputSample &sample)
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
uint pos
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
blender::bke::AttrDomain ED_grease_pencil_edit_selection_domain_get(const ToolSettings *tool_settings)
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix2(float factor, const T &a, const T &b)
void evaluate_segment(const T &point_0, const T &point_1, const T &point_2, const T &point_3, MutableSpan< T > result)
void assign_to_vertex_group_from_mask(CurvesGeometry &curves, const IndexMask &mask, StringRef name, float weight)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
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)
void copy_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
Curves * curves_new_nomain(int points_num, int curves_num)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
void fill_selection_false(GMutableSpan selection)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
bke::CurvesGeometry trim_curve_segments(const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, const Span< rcti > screen_space_curve_bounds, const IndexMask &curve_selection, const Vector< Vector< int > > &selected_points_in_curves, const bool keep_caps)
float randomize_rotation(const BrushGpencilSettings &settings, const float stroke_factor, const float distance, const float pressure)
void resize_single_curve(bke::CurvesGeometry &curves, const bool at_end, const int new_points_num)
float randomize_opacity(const BrushGpencilSettings &settings, const float stroke_factor, const float distance, const float opacity, const float pressure)
float opacity_from_input_sample(const float pressure, const Brush *brush, const BrushGpencilSettings *settings)
ColorGeometry4f randomize_color(const BrushGpencilSettings &settings, const std::optional< BrushColorJitterSettings > &jitter, const float stroke_hue_factor, const float stroke_saturation_factor, const float stroke_value_factor, const float distance, const ColorGeometry4f color, const float pressure)
void add_single_curve(bke::CurvesGeometry &curves, const bool at_end)
float randomize_radius(const BrushGpencilSettings &settings, const float stroke_factor, const float distance, const float radius, const float pressure)
float radius_from_input_sample(const RegionView3D *rv3d, const ARegion *region, const Brush *brush, const float pressure, const float3 location, const float4x4 to_world, const BrushGpencilSettings *settings)
IndexMask polyline_detect_corners(Span< float2 > points, const float radius_min, const float radius_max, const int samples_max, const float angle_threshold, IndexMaskMemory &memory)
bke::CurvesGeometry curves_merge_endpoints_by_distance(const ARegion &region, const bke::CurvesGeometry &src_curves, const float4x4 &layer_to_world, const float merge_distance, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
Array< float2 > polyline_fit_curve(Span< float2 > points, const float error_threshold, const IndexMask &corner_mask)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
float4x2 calculate_texture_space(const Scene *scene, const ARegion *region, const float2 &mouse, const DrawingPlacement &placement)
bke::CurvesGeometry create_curves_outline(const bke::greasepencil::Drawing &drawing, const IndexMask &strokes, const float4x4 &transform, const int corner_subdivisions, const float outline_radius, const float outline_offset, const int material_index)
static StrokeSnapMode get_snap_mode(const bContext &C)
static void linear_interpolation(const T &a, const T &b, MutableSpan< T > dst, const bool include_first_point)
static void deselect_stroke(const bContext &C, bke::greasepencil::Drawing &drawing, const int active_curve)
static void append_stroke_to_multiframe_drawings(const bke::CurvesGeometry &src_strokes, const int curve, const int current_frame, const bool on_back, Span< ed::greasepencil::MutableDrawingInfo > drawings)
std::unique_ptr< GreasePencilStrokeOperation > new_paint_operation(bool do_fill_guides=false)
static void trim_stroke_ends(bke::greasepencil::Drawing &drawing, const int active_curve, const bool on_back)
static float2 arithmetic_mean(Span< float2 > values)
static bke::CurvesGeometry get_single_stroke(const bke::CurvesGeometry &src, const int curve)
static int trim_end_points(bke::greasepencil::Drawing &drawing, const float epsilon, const bool on_back, const int active_curve)
static float brush_radius_to_pixel_radius(const RegionView3D *rv3d, const Brush *brush, const float3 pos)
bool brush_using_vertex_color(const GpPaint *gp_paint, const Brush *brush)
static void morph_points_to_curve(Span< float2 > src, Span< float2 > target, MutableSpan< float2 > dst)
static void simplify_stroke(bke::greasepencil::Drawing &drawing, const float epsilon, const int active_curve)
static void process_stroke_weights(const Scene &scene, const Object &object, bke::greasepencil::Drawing &drawing, const int active_curve)
static void smooth_stroke(bke::greasepencil::Drawing &drawing, const float influence, const int iterations, const int active_curve)
static Array< float2 > sample_curve_2d(Span< float2 > positions, const int64_t resolution)
static void outline_stroke(bke::greasepencil::Drawing &drawing, const int active_curve, const float4x4 &viewmat, const ed::greasepencil::DrawingPlacement &placement, const float outline_radius, const int material_index, const bool on_back)
static void add_strokes_to_drawing(const bool on_back, Curves *strokes, bke::greasepencil::Drawing &drawing)
void smooth_curve_attribute(const IndexMask &curves_to_smooth, const OffsetIndices< int > points_by_curve, const VArray< bool > &point_selection, const VArray< bool > &cyclic, int iterations, float influence, bool smooth_ends, bool keep_shape, GMutableSpan attribute_data)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
void curve_simplify(const Span< float3 > positions, const bool cyclic, const float epsilon, const GSpan attribute_data, MutableSpan< bool > points_to_delete)
void gaussian_blur_1D(const GSpan src, int iterations, const VArray< float > &influence_by_point, const bool smooth_ends, const bool keep_shape, const bool is_cyclic, GMutableSpan dst)
void accumulate_lengths(const Span< T > values, const bool cyclic, MutableSpan< float > lengths)
void sample_at_lengths(Span< float > accumulated_segment_lengths, Span< float > sample_lengths, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factors)
void interpolate(const Span< T > src, const Span< int > indices, const Span< float > factors, MutableSpan< T > dst)
T cos(const AngleRadianBase< T > &a)
T safe_rcp(const T &a)
T floor(const T &a)
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
CartesianBasis invert(const CartesianBasis &basis)
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
T abs(const T &a)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
struct CurveMapping * curve_jitter
struct CurveMapping * curve_rand_pressure
struct CurveMapping * curve_rand_strength
struct CurveMapping * curve_rand_uv
struct CurveMapping * curve_rand_saturation
struct CurveMapping * curve_rand_hue
float unprojected_radius
short ob_mode
struct CurveMapping * curve_rand_value
float rgb[3]
struct BrushGpencilSettings * gpencil_settings
GreasePencilRuntimeHandle * runtime
struct MaterialGPencilStyle * gp_style
struct bPose * pose
float viewmat[4][4]
struct ToolSettings * toolsettings
struct RenderData r
struct GP_Sculpt_Settings gp_sculpt
float chan_mat[4][4]
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
void process_extension_sample(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
void active_jitter(PaintOperation &self, const int new_points_num, const float brush_radius_px, const float pressure, const IndexRange active_window, MutableSpan< float3 > curve_positions)
void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
void process_start_sample(PaintOperation &self, const bContext &C, const InputSample &start_sample, const int material_index, const bool use_fill)
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)