Blender V5.0
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_size / 2.0f) / pixel_size;
63 }
64 return float(brush->size / 2.0f);
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_size = 0.01f;
164
165 settings->flag &= ~GP_BRUSH_USE_PRESSURE;
166
168 /* TODO: Use theme setting. */
169 copy_v3_fl3(fill_guides_brush->color, 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 copy_v3_v3(color_base, brush_->color);
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",
426 {
427 u_scale.span[active_curve] = 1.0f;
428 curve_attributes_to_skip.add("u_scale");
429 u_scale.finish();
430 }
431 if (bke::SpanAttributeWriter<float> aspect_ratio =
432 attributes.lookup_or_add_for_write_span<float>(
433 "aspect_ratio",
436 {
437 aspect_ratio.span[active_curve] = aspect_ratio_;
438 curve_attributes_to_skip.add("aspect_ratio");
439 aspect_ratio.finish();
440 }
441
442 if (settings_->uv_random > 0.0f || attributes.contains("rotation")) {
443 if (bke::SpanAttributeWriter<float> rotations =
444 attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point))
445 {
446 rotations.span[last_active_point] = start_rotation;
447 point_attributes_to_skip.add("rotation");
448 rotations.finish();
449 }
450 }
451
452 /* Only set the attribute if the type is not the default or if it already exists. */
453 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
454 if (bke::SpanAttributeWriter<int8_t> start_caps =
455 attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve))
456 {
457 start_caps.span[active_curve] = settings_->caps_type;
458 curve_attributes_to_skip.add("start_cap");
459 start_caps.finish();
460 }
461 }
462
463 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
465 attributes.lookup_or_add_for_write_span<int8_t>("end_cap", bke::AttrDomain::Curve))
466 {
467 end_caps.span[active_curve] = settings_->caps_type;
468 curve_attributes_to_skip.add("end_cap");
469 end_caps.finish();
470 }
471 }
472
473 if (use_fill && (start_opacity < 1.0f || attributes.contains("fill_opacity"))) {
474 if (bke::SpanAttributeWriter<float> fill_opacities =
475 attributes.lookup_or_add_for_write_span<float>(
476 "fill_opacity",
479 {
480 fill_opacities.span[active_curve] = start_opacity;
481 curve_attributes_to_skip.add("fill_opacity");
482 fill_opacities.finish();
483 }
484 }
485
486 if (bke::SpanAttributeWriter<float> init_times =
487 attributes.lookup_or_add_for_write_span<float>("init_time", bke::AttrDomain::Curve))
488 {
489 /* Truncating time in ms to uint32 then we don't lose precision in lower bits. */
490 init_times.span[active_curve] = float(uint64_t(self.start_time_ * 1e3)) / float(1e3);
491 curve_attributes_to_skip.add("init_time");
492 init_times.finish();
493 }
494
495 if (self.do_fill_guides_) {
496 if (bke::SpanAttributeWriter<bool> is_fill_boundary =
497 attributes.lookup_or_add_for_write_span<bool>(".is_fill_guide",
499 {
500 is_fill_boundary.span[active_curve] = true;
501 curve_attributes_to_skip.add(".is_fill_guide");
502 is_fill_boundary.finish();
503 }
504 }
505
506 curves.curve_types_for_write()[active_curve] = CURVE_TYPE_POLY;
507 curve_attributes_to_skip.add("curve_type");
508 curves.update_curve_types();
509
510 if (self.placement_.use_project_to_stroke()) {
511 self.stroke_placement_depths_.append(self.stroke_placement_depths_.is_empty() ?
512 std::nullopt :
513 self.stroke_placement_depths_.last());
514 /* Initialize the snap point. */
515 self.update_stroke_depth_placement(C, start_sample);
516 }
517
518 /* Initialize the rest of the attributes with default values. */
520 attributes,
522 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
523 IndexRange(last_active_point, 1));
525 attributes,
527 bke::attribute_filter_from_skip_ref(curve_attributes_to_skip),
528 IndexRange(active_curve, 1));
529
530 self.drawing_->tag_topology_changed();
531 }
532
533 void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
534 {
535 const Span<float2> coords_to_smooth = self.screen_space_coords_orig_.as_span().slice(
536 smooth_window);
537
538 /* Detect corners in the current slice of coordinates. */
539 const float corner_min_radius_px = 5.0f;
540 const float corner_max_radius_px = 30.0f;
541 const int64_t corner_max_samples = 64;
542 const float corner_angle_threshold = 0.6f;
543 IndexMaskMemory memory;
545 coords_to_smooth.drop_front(1).drop_back(1),
546 corner_min_radius_px,
547 corner_max_radius_px,
548 corner_max_samples,
549 corner_angle_threshold,
550 memory);
551
552 /* Pre-blur the coordinates for the curve fitting. This generally leads to a better (more
553 * stable) fit. */
554 Array<float2> coords_pre_blur(smooth_window.size());
555 const int pre_blur_iterations = 3;
557 coords_to_smooth,
558 pre_blur_iterations,
559 VArray<float>::from_single(settings_->active_smooth, smooth_window.size()),
560 true,
561 true,
562 false,
563 coords_pre_blur.as_mutable_span());
564
565 /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */
566 const float max_error_threshold_px = 5.0f;
568 coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask);
569
570 /* Sampling the curve at a fixed resolution. */
571 const int64_t sample_resolution = 32;
572 Array<float2> sampled_curve_points = sample_curve_2d(curve_points, sample_resolution);
573
574 /* Morphing the coordinates onto the curve. Result is stored in a temporary array. */
575 Array<float2> coords_smoothed(coords_to_smooth.size());
576 morph_points_to_curve(coords_to_smooth, sampled_curve_points, coords_smoothed);
577
578 MutableSpan<float2> window_coords = self.screen_space_smoothed_coords_.as_mutable_span().slice(
579 smooth_window);
580 const float converging_threshold_px = 0.1f;
581 bool stop_counting_converged = false;
582 int num_converged = 0;
583 for (const int64_t window_i : smooth_window.index_range()) {
584 /* Record the curve fitting of this point. */
585 self.screen_space_curve_fitted_coords_[window_i].append(coords_smoothed[window_i]);
586 Span<float2> fit_coords = self.screen_space_curve_fitted_coords_[window_i];
587
588 /* We compare the previous arithmetic mean to the current. Going from the back to the front,
589 * if a point hasn't moved by a minimum threshold, it counts as converged. */
590 float2 new_pos = arithmetic_mean(fit_coords);
591 if (!stop_counting_converged) {
592 float2 prev_pos = window_coords[window_i];
593 if (math::distance(new_pos, prev_pos) < converging_threshold_px) {
594 num_converged++;
595 }
596 else {
597 stop_counting_converged = true;
598 }
599 }
600
601 /* Update the positions in the current cache. */
602 window_coords[window_i] = new_pos;
603 }
604
605 /* Remove all the converged points from the active window and shrink the window accordingly. */
606 if (num_converged > 0) {
607 self.active_smooth_start_index_ += num_converged;
608 self.screen_space_curve_fitted_coords_.remove(0, num_converged);
609 }
610 }
611
613 const int new_points_num,
614 const float brush_radius_px,
615 const float pressure,
616 const IndexRange active_window,
617 MutableSpan<float3> curve_positions)
618 {
619 float jitter_factor = 1.0f;
621 jitter_factor = BKE_curvemapping_evaluateF(settings_->curve_jitter, 0, pressure);
622 }
623 const float2 tangent = math::normalize(self.smoothed_pen_direction_);
624 const float2 cotangent = float2(-tangent.y, tangent.x);
625 for ([[maybe_unused]] const int _ : IndexRange(new_points_num)) {
626 const float rand = self.rng_.get_float() * 2.0f - 1.0f;
627 const float factor = rand * settings_->draw_jitter * jitter_factor;
628 self.screen_space_jitter_offsets_.append(cotangent * factor * brush_radius_px);
629 }
630 const Span<float2> jitter_slice = self.screen_space_jitter_offsets_.as_mutable_span().slice(
631 active_window);
632 MutableSpan<float2> smoothed_coords =
633 self.screen_space_smoothed_coords_.as_mutable_span().slice(active_window);
634 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
635 active_window);
636 MutableSpan<float3> positions_slice = curve_positions.slice(active_window);
637 if (self.placement_.use_project_to_stroke()) {
638 BLI_assert(self.stroke_placement_depths_.size() == self.screen_space_coords_orig_.size());
639 const Span<std::optional<float>> stroke_depths =
640 self.stroke_placement_depths_.as_span().slice(active_window);
641 for (const int64_t window_i : active_window.index_range()) {
642 final_coords[window_i] = smoothed_coords[window_i] + jitter_slice[window_i];
643 const std::optional<float> depth = stroke_depths[window_i];
644 positions_slice[window_i] = depth ? self.placement_.place(final_coords[window_i], *depth) :
645 self.placement_.project(final_coords[window_i]);
646 }
647 }
648 else {
649 for (const int64_t window_i : active_window.index_range()) {
650 final_coords[window_i] = smoothed_coords[window_i] + jitter_slice[window_i];
651 positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
652 }
653 }
654 }
655
657 const bContext &C,
658 const InputSample &extension_sample)
659 {
660 Scene *scene = CTX_data_scene(&C);
661 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
662 const ARegion *region = CTX_wm_region(&C);
663 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
664
665 const float2 coords = extension_sample.mouse_position;
666 float3 position;
667 if (self.placement_.use_project_to_stroke()) {
668 const std::optional<float> depth = self.stroke_placement_depths_.is_empty() ?
669 std::nullopt :
670 self.stroke_placement_depths_.last();
671 if (depth) {
672 position = self.placement_.place(coords, *depth);
673 }
674 else {
675 position = self.placement_.project(coords);
676 }
677 }
678 else {
679 position = self.placement_.project(coords);
680 }
681
683 region,
684 brush_,
685 extension_sample.pressure,
686 position,
687 self.placement_.to_world_space(),
688 settings_);
690 extension_sample.pressure, brush_, settings_);
691
692 const float brush_radius_px = brush_radius_to_pixel_radius(
693 rv3d, brush_, math::transform_point(self.placement_.to_world_space(), position));
694
695 bke::CurvesGeometry &curves = self.drawing_->strokes_for_write();
696 OffsetIndices<int> points_by_curve = curves.points_by_curve();
697 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
698
699 const int active_curve = on_back ? curves.curves_range().first() :
700 curves.curves_range().last();
701 const IndexRange curve_points = points_by_curve[active_curve];
702 const int last_active_point = curve_points.last();
703
704 const float2 prev_coords = self.screen_space_coords_orig_.last();
705 float prev_radius = self.drawing_->radii()[last_active_point];
706 const float prev_opacity = self.drawing_->opacities()[last_active_point];
707 const ColorGeometry4f prev_vertex_color = self.drawing_->vertex_colors()[last_active_point];
708
709 const bool is_first_sample = (curve_points.size() == 1);
710
711 /* Use the vector from the previous to the next point. Set the direction based on the first two
712 * samples. For subsequent samples, interpolate with the previous direction to get a smoothed
713 * value over time. */
714 if (is_first_sample) {
715 self.smoothed_pen_direction_ = self.screen_space_coords_orig_.last() - coords;
716 }
717 else {
718 /* The smoothing rate is a factor from 0 to 1 that represents how quickly the
719 * `smoothed_pen_direction_` "reacts" to changes in direction.
720 * - 1.0f: Immediate reaction.
721 * - 0.0f: No reaction (value never changes). */
722 constexpr float smoothing_rate_factor = 0.3f;
723 self.smoothed_pen_direction_ = math::interpolate(self.smoothed_pen_direction_,
724 self.screen_space_coords_orig_.last() -
725 coords,
726 smoothing_rate_factor);
727 }
728
729 /* Approximate brush with non-circular shape by changing the radius based on the angle. */
730 float radius_factor = 1.0f;
731 if (settings_->draw_angle_factor > 0.0f) {
732 /* `angle` is the angle to the horizontal line in screen space. */
733 const float angle = settings_->draw_angle;
734 const float2 angle_vec = float2(math::cos(angle), math::sin(angle));
735
736 /* The angle factor is 1.0f when the direction is aligned with the angle vector and 0.0f when
737 * it is orthogonal to the angle vector. This is consistent with the behavior from GPv2. */
738 const float angle_factor = math::abs(
739 math::dot(angle_vec, math::normalize(self.smoothed_pen_direction_)));
740
741 /* Influence is controlled by `draw_angle_factor`. */
742 radius_factor = math::interpolate(1.0f, angle_factor, settings_->draw_angle_factor);
743 radius *= radius_factor;
744 }
745
746 /* Overwrite last point if it's very close. */
747 const float distance_px = math::distance(coords, prev_coords);
748 constexpr float point_override_threshold_px = 2.0f;
749 if (distance_px < point_override_threshold_px) {
750 self.accum_distance_ += distance_px;
751 /* Don't move the first point of the stroke. */
752 if (!is_first_sample) {
753 curves.positions_for_write()[last_active_point] = position;
754 }
755 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
757 self.stroke_random_radius_factor_,
758 self.accum_distance_,
759 radius,
760 extension_sample.pressure);
761 }
762 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
764 self.stroke_random_opacity_factor_,
765 self.accum_distance_,
766 opacity,
767 extension_sample.pressure);
768 }
769 self.drawing_->radii_for_write()[last_active_point] = math::max(radius, prev_radius);
770 self.drawing_->opacities_for_write()[last_active_point] = math::max(opacity, prev_opacity);
771 return;
772 }
773
774 /* Adjust the first points radius based on the computed angle. */
775 if (is_first_sample && settings_->draw_angle_factor > 0.0f) {
776 self.drawing_->radii_for_write()[last_active_point] *= radius_factor;
777 prev_radius = self.drawing_->radii()[last_active_point];
778 }
779
780 /* Clamp the number of points within a pixel in screen space. */
781 constexpr int max_points_per_pixel = 4;
782 /* The value `brush_->spacing` is a percentage of the brush radius in pixels. */
783 const float max_spacing_px = math::max((float(brush_->spacing) / 100.0f) *
784 float(brush_radius_px),
785 1.0f / float(max_points_per_pixel));
786 /* If the next sample is far away, we subdivide the segment to add more points. */
787 const int new_points_num = (distance_px > max_spacing_px) ?
788 int(math::floor(distance_px / max_spacing_px)) :
789 1;
790 /* Resize the curves geometry. */
792 curves, on_back == false, curve_points.size() + new_points_num);
793
794 Set<std::string> point_attributes_to_skip;
795 /* Subdivide new segment. */
796 const IndexRange new_points = curves.points_by_curve()[active_curve].take_back(new_points_num);
797 Array<float2> new_screen_space_coords(new_points_num);
798 MutableSpan<float3> positions = curves.positions_for_write();
799 MutableSpan<float3> new_positions = positions.slice(new_points);
800 MutableSpan<float> new_radii = self.drawing_->radii_for_write().slice(new_points);
801 MutableSpan<float> new_opacities = self.drawing_->opacities_for_write().slice(new_points);
802
803 /* Interpolate the screen space positions. */
804 linear_interpolation<float2>(prev_coords, coords, new_screen_space_coords, is_first_sample);
805 linear_interpolation<float>(prev_radius, radius, new_radii, is_first_sample);
806 linear_interpolation<float>(prev_opacity, opacity, new_opacities, is_first_sample);
807 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
808
809 /* Randomize radii. */
810 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
811 for (const int i : IndexRange(new_points_num)) {
813 self.stroke_random_radius_factor_,
814 self.accum_distance_ +
815 max_spacing_px * i,
816 new_radii[i],
817 extension_sample.pressure);
818 }
819 }
820
821 /* Randomize opacities. */
822 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
823 for (const int i : IndexRange(new_points_num)) {
825 self.stroke_random_opacity_factor_,
826 self.accum_distance_ +
827 max_spacing_px * i,
828 new_opacities[i],
829 extension_sample.pressure);
830 }
831 }
832
833 /* Randomize rotations. */
834 if (use_settings_random_ && (settings_->uv_random > 0.0f || attributes.contains("rotation"))) {
835 if (bke::SpanAttributeWriter<float> rotations =
836 attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point))
837 {
838 const MutableSpan<float> new_rotations = rotations.span.slice(new_points);
839 for (const int i : IndexRange(new_points_num)) {
841 *settings_,
842 self.rng_,
843 self.stroke_random_rotation_factor_,
844 extension_sample.pressure);
845 }
846 point_attributes_to_skip.add("rotation");
847 rotations.finish();
848 }
849 }
850
851 /* Randomize vertex color. */
852 if (use_vertex_color_ || attributes.contains("vertex_color")) {
853 MutableSpan<ColorGeometry4f> new_vertex_colors =
854 self.drawing_->vertex_colors_for_write().slice(new_points);
856 prev_vertex_color, vertex_color_, new_vertex_colors, is_first_sample);
857 if (use_settings_random_ || attributes.contains("vertex_color")) {
858 for (const int i : IndexRange(new_points_num)) {
859 new_vertex_colors[i] = ed::greasepencil::randomize_color(*settings_,
861 self.stroke_random_hue_factor_,
862 self.stroke_random_sat_factor_,
863 self.stroke_random_val_factor_,
864 self.accum_distance_ +
865 max_spacing_px * i,
866 new_vertex_colors[i],
867 extension_sample.pressure);
868 }
869 }
870 point_attributes_to_skip.add("vertex_color");
871 }
872
873 const double new_delta_time = BLI_time_now_seconds() - self.start_time_;
874 if (bke::SpanAttributeWriter<float> delta_times =
875 attributes.lookup_or_add_for_write_span<float>("delta_time", bke::AttrDomain::Point))
876 {
877 linear_interpolation<float>(float(self.delta_time_),
878 float(new_delta_time),
879 delta_times.span.slice(new_points),
880 is_first_sample);
881 point_attributes_to_skip.add("delta_time");
882 delta_times.finish();
883 }
884
885 /* Update the accumulated distance along the stroke in pixels. */
886 self.accum_distance_ += distance_px;
887
888 /* Update the current delta time. */
889 self.delta_time_ = new_delta_time;
890
891 /* Update screen space buffers with new points. */
892 self.screen_space_coords_orig_.extend(new_screen_space_coords);
893 self.screen_space_smoothed_coords_.extend(new_screen_space_coords);
894 self.screen_space_final_coords_.extend(new_screen_space_coords);
895 for (float2 new_position : new_screen_space_coords) {
896 self.screen_space_curve_fitted_coords_.append(Vector<float2>({new_position}));
897 }
898 if (self.placement_.use_project_to_stroke()) {
899 const std::optional<float> last_depth = self.stroke_placement_depths_.is_empty() ?
900 std::nullopt :
901 self.stroke_placement_depths_.last();
902 self.stroke_placement_depths_.append_n_times(last_depth, new_points_num);
903 }
904
905 /* Only start smoothing if there are enough points. */
906 constexpr int64_t min_active_smoothing_points_num = 8;
907 const IndexRange smooth_window = self.screen_space_coords_orig_.index_range().drop_front(
908 self.active_smooth_start_index_);
909 if (smooth_window.size() < min_active_smoothing_points_num) {
910 if (self.placement_.use_project_to_stroke()) {
911 const Span<std::optional<float>> new_depths =
912 self.stroke_placement_depths_.as_mutable_span().take_back(new_points_num);
913 for (const int64_t i : new_positions.index_range()) {
914 const std::optional<float> depth = new_depths[i];
915 if (depth) {
916 new_positions[i] = self.placement_.place(coords, *depth);
917 }
918 else {
919 new_positions[i] = self.placement_.project(coords);
920 }
921 }
922 }
923 else {
924 self.placement_.project(new_screen_space_coords, new_positions);
925 }
926 }
927 else {
928 /* Active smoothing is done in a window at the end of the new stroke.
929 * Final positions are written below. */
930 this->active_smoothing(self, smooth_window);
931 }
932
933 /* Jitter uses smoothed coordinates as input. In case smoothing is not applied these are the
934 * unsmoothed original coordinates. */
935 MutableSpan<float3> curve_positions = positions.slice(curves.points_by_curve()[active_curve]);
936 if (use_settings_random_ && settings_->draw_jitter > 0.0f) {
937 this->active_jitter(self,
938 new_points_num,
939 brush_radius_px,
940 extension_sample.pressure,
941 smooth_window,
942 curve_positions);
943 }
944 else {
945 MutableSpan<float2> smoothed_coords =
946 self.screen_space_smoothed_coords_.as_mutable_span().slice(smooth_window);
947 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
948 smooth_window);
949 /* Not jitter, so we just copy the positions over. */
950 final_coords.copy_from(smoothed_coords);
951 MutableSpan<float3> curve_positions_slice = curve_positions.slice(smooth_window);
952 if (self.placement_.use_project_to_stroke()) {
953 BLI_assert(self.stroke_placement_depths_.size() == self.screen_space_coords_orig_.size());
954 const Span<std::optional<float>> stroke_depths =
955 self.stroke_placement_depths_.as_mutable_span().slice(smooth_window);
956 for (const int64_t window_i : smooth_window.index_range()) {
957 const std::optional<float> depth = stroke_depths[window_i];
958 curve_positions_slice[window_i] = depth ?
959 self.placement_.place(final_coords[window_i],
960 *depth) :
961 self.placement_.project(final_coords[window_i]);
962 }
963 }
964 else {
965 for (const int64_t window_i : smooth_window.index_range()) {
966 curve_positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
967 }
968 }
969 }
970
971 if (self.placement_.use_project_to_stroke()) {
972 /* Find a new snap point and apply projection to trailing points. */
973 self.update_stroke_depth_placement(C, extension_sample);
974 }
975
976 /* Initialize the rest of the attributes with default values. */
978 attributes,
980 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
981 curves.points_range().take_back(new_points_num));
982
983 self.drawing_->set_texture_matrices({self.texture_space_},
984 IndexRange::from_single(active_curve));
985 }
986
987 void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
988 {
989 const Scene *scene = CTX_data_scene(&C);
990 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
991
992 this->process_extension_sample(self, C, extension_sample);
993
994 const bke::CurvesGeometry &curves = self.drawing_->strokes();
995 const int active_curve = on_back ? curves.curves_range().first() :
996 curves.curves_range().last();
997 self.drawing_->tag_topology_changed(IndexRange::from_single(active_curve));
998 }
999};
1000
1006
1008{
1009 /* gpencil_v3d_align is an awkward combination of multiple properties. If none of the non-zero
1010 * flags are set the AllPoints mode is the default. */
1011 const Scene &scene = *CTX_data_scene(&C);
1012 const char align_flags = scene.toolsettings->gpencil_v3d_align;
1013 if (align_flags & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) {
1015 }
1016 if (align_flags & GP_PROJECT_DEPTH_STROKE_FIRST) {
1018 }
1020}
1021
1023{
1024 BLI_assert(placement_.use_project_to_stroke());
1025
1026 const std::optional<float> new_stroke_placement_depth = placement_.get_depth(
1027 sample.mouse_position);
1028 if (!new_stroke_placement_depth) {
1029 return false;
1030 }
1031
1032 const StrokeSnapMode snap_mode = get_snap_mode(C);
1033 switch (snap_mode) {
1035 const float start_depth = last_stroke_placement_depth_ ? *last_stroke_placement_depth_ :
1036 *new_stroke_placement_depth;
1037 const float end_depth = *new_stroke_placement_depth;
1038 const IndexRange reprojected_points = this->interpolate_stroke_depth(
1039 C, last_stroke_placement_point_, start_depth, end_depth);
1040 /* Only reproject newly added points next time a hit point is found. */
1041 if (!reprojected_points.is_empty()) {
1042 last_stroke_placement_point_ = reprojected_points.one_after_last();
1043 }
1044
1045 last_stroke_placement_depth_ = new_stroke_placement_depth;
1046 break;
1047 }
1049 const float start_depth = last_stroke_placement_depth_ ? *last_stroke_placement_depth_ :
1050 *new_stroke_placement_depth;
1051 const float end_depth = *new_stroke_placement_depth;
1052 const IndexRange reprojected_points = this->interpolate_stroke_depth(
1053 C, last_stroke_placement_point_, start_depth, end_depth);
1054
1055 /* Only update depth on the first hit. */
1056 if (!last_stroke_placement_depth_) {
1057 /* Keep reprojecting all points from the first hit onward. */
1058 if (!reprojected_points.is_empty()) {
1059 last_stroke_placement_point_ = reprojected_points.one_after_last();
1060 }
1061 last_stroke_placement_depth_ = new_stroke_placement_depth;
1062 }
1063 break;
1064 }
1066 /* Only reproject once in "First Point" mode. */
1067 if (!last_stroke_placement_depth_) {
1068 const float start_depth = *new_stroke_placement_depth;
1069 const float end_depth = *new_stroke_placement_depth;
1070 this->interpolate_stroke_depth(C, last_stroke_placement_point_, start_depth, end_depth);
1071
1072 last_stroke_placement_depth_ = new_stroke_placement_depth;
1073 break;
1074 }
1075 }
1076 }
1077
1078 return true;
1079}
1080
1082 std::optional<int> start_point,
1083 const float from_depth,
1084 const float to_depth)
1085{
1086 using namespace blender::bke;
1087
1088 Scene *scene = CTX_data_scene(&C);
1089 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
1090
1091 /* Drawing should exist. */
1092 BLI_assert(drawing_);
1093 bke::greasepencil::Drawing &drawing = *drawing_;
1094 const int active_curve = on_back ? drawing.strokes().curves_range().first() :
1095 drawing.strokes().curves_range().last();
1096 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1097 const IndexRange all_points = points_by_curve[active_curve];
1098 BLI_assert(screen_space_final_coords_.size() == all_points.size());
1099 if (all_points.is_empty()) {
1100 return {};
1101 }
1102
1103 IndexRange active_points = all_points;
1104 if (start_point) {
1105 active_points = IndexRange::from_begin_end_inclusive(*start_point, all_points.last());
1106 }
1107 if (active_points.is_empty()) {
1108 return {};
1109 }
1110
1111 /* Point slice relative to the curve, valid for 2D coordinate array. */
1112 const IndexRange active_curve_points = active_points.shift(-all_points.start());
1113
1114 MutableSpan<std::optional<float>> depths = stroke_placement_depths_.as_mutable_span().slice(
1115 active_curve_points);
1116 MutableSpan<float3> positions = drawing.strokes_for_write().positions_for_write().slice(
1117 active_points);
1118 const Span<float2> final_coords = screen_space_final_coords_.as_span().slice(
1119 active_curve_points);
1120 const float step_size = 1.0f / std::max(int(active_points.size()) - 1, 1);
1121 for (const int i : positions.index_range()) {
1122 /* Update the placement depth for later reprojection (active smoothing). */
1123 depths[i] = math::interpolate(from_depth, to_depth, float(i) * step_size);
1124 positions[i] = placement_.place(final_coords[i], *depths[i]);
1125 }
1126
1127 return active_points;
1128}
1129
1131{
1133 Brush *current_brush = BKE_paint_brush(paint);
1134
1135 fill_guides_brush_ = create_fill_guide_brush();
1136 BLI_assert(fill_guides_brush_ != nullptr);
1137 BKE_paint_brush_set(paint, fill_guides_brush_);
1138
1139 saved_active_brush_ = current_brush;
1140}
1141
1143{
1145 BLI_assert(saved_active_brush_ != nullptr);
1146 BKE_paint_brush_set(paint, saved_active_brush_);
1147 saved_active_brush_ = nullptr;
1148 /* Free the temporary brush. */
1149 BKE_id_free_ex(nullptr, fill_guides_brush_, LIB_ID_FREE_NO_MAIN, false);
1150 fill_guides_brush_ = nullptr;
1151}
1152
1154{
1155 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
1156 ARegion *region = CTX_wm_region(&C);
1158 Scene *scene = CTX_data_scene(&C);
1159 Object *object = CTX_data_active_object(&C);
1160 Object *eval_object = DEG_get_evaluated(depsgraph, object);
1161 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1162
1163 if (do_fill_guides_) {
1165 }
1166
1167 Paint *paint = &scene->toolsettings->gp_paint->paint;
1169
1170 if (brush->gpencil_settings == nullptr) {
1172 }
1173 BrushGpencilSettings *settings = brush->gpencil_settings;
1174
1181 BKE_curvemapping_init(brush->curve_rand_hue);
1182 BKE_curvemapping_init(brush->curve_rand_saturation);
1183 BKE_curvemapping_init(brush->curve_rand_value);
1184
1185 BLI_assert(grease_pencil->has_active_layer());
1186 const bke::greasepencil::Layer &layer = *grease_pencil->get_active_layer();
1187 /* Initialize helper class for projecting screen space coordinates. */
1188 placement_ = ed::greasepencil::DrawingPlacement(*scene, *region, *view3d, *eval_object, &layer);
1189 if (placement_.use_project_to_surface()) {
1190 placement_.cache_viewport_depths(depsgraph, region, view3d);
1191 }
1192 else if (placement_.use_project_to_stroke()) {
1193 placement_.cache_viewport_depths(depsgraph, region, view3d);
1194 }
1195
1197 scene, region, start_sample.mouse_position, placement_);
1198
1199 /* `View` is already stored in object space but all others are in layer space. */
1201 texture_space_ = texture_space_ * layer.to_object_space(*object);
1202 }
1203
1205 if ((settings->flag & GP_BRUSH_GROUP_RANDOM) != 0) {
1206 /* Since we want stroke properties to randomize around set values, it's easier for us to have a
1207 * signed value in range (-1,1) in calculations downstream. */
1208 stroke_random_radius_factor_ = rng_.get_float() * 2.0f - 1.0f;
1209 stroke_random_opacity_factor_ = rng_.get_float() * 2.0f - 1.0f;
1210 stroke_random_rotation_factor_ = rng_.get_float() * 2.0f - 1.0f;
1211
1212 stroke_random_hue_factor_ = rng_.get_float() * 2.0f - 1.0f;
1213 stroke_random_sat_factor_ = rng_.get_float() * 2.0f - 1.0f;
1214 stroke_random_val_factor_ = rng_.get_float() * 2.0f - 1.0f;
1215 }
1216
1218 CTX_data_main(&C), object, brush);
1219 const int material_index = BKE_object_material_index_get(object, material);
1220 const bool use_fill = (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
1221
1222 frame_number_ = scene->r.cfra;
1223 drawing_ = grease_pencil->get_editable_drawing_at(layer, frame_number_);
1224 multi_frame_drawings_ = ed::greasepencil::retrieve_editable_drawings(*scene, *grease_pencil);
1225 BLI_assert(drawing_ != nullptr);
1226
1227 /* We're now starting to draw. */
1228 grease_pencil->runtime->is_drawing_stroke = true;
1229
1230 /* Initialize the start time to the current time. */
1231 start_time_ = BLI_time_now_seconds();
1232 /* Delta time starts at 0. */
1233 delta_time_ = 0.0f;
1234
1235 PaintOperationExecutor executor{C};
1236 executor.process_start_sample(*this, C, start_sample, material_index, use_fill);
1237
1238 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1239 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1240}
1241
1242void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
1243{
1244 Object *object = CTX_data_active_object(&C);
1245 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1246
1247 PaintOperationExecutor executor{C};
1248 executor.execute(*this, C, extension_sample);
1249
1250 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1251 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1252}
1253
1255 const float influence,
1256 const int iterations,
1257 const int active_curve)
1258{
1260 const IndexRange stroke = IndexRange::from_single(active_curve);
1261 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1262 const VArray<bool> cyclic = curves.cyclic();
1263 const VArray<bool> point_selection = VArray<bool>::from_single(true, curves.points_num());
1264
1265 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1266 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1268 points_by_curve,
1269 point_selection,
1270 cyclic,
1271 iterations,
1272 influence,
1273 false,
1274 true,
1275 positions.span);
1276 positions.finish();
1277 drawing.tag_positions_changed();
1278
1279 if (drawing.opacities().is_span()) {
1280 bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
1282 points_by_curve,
1283 point_selection,
1284 cyclic,
1285 iterations,
1286 influence,
1287 true,
1288 false,
1289 opacities.span);
1290 opacities.finish();
1291 }
1292 if (drawing.radii().is_span()) {
1293 bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
1295 points_by_curve,
1296 point_selection,
1297 cyclic,
1298 iterations,
1299 influence,
1300 true,
1301 false,
1302 radii.span);
1303 radii.finish();
1304 }
1305}
1306
1308 const float epsilon,
1309 const int active_curve)
1310{
1311 const bke::CurvesGeometry &curves = drawing.strokes();
1312 const bke::AttributeAccessor attributes = curves.attributes();
1313 const IndexRange points = curves.points_by_curve()[active_curve];
1314 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1315 ".draw_tool_screen_space_positions");
1316 BLI_assert(screen_space_positions_attribute.is_span());
1317
1318 const Span<float2> screen_space_positions =
1319 screen_space_positions_attribute.get_internal_span().slice(points);
1320
1321 Array<bool> points_to_delete_arr(drawing.strokes().points_num(), false);
1322 points_to_delete_arr.as_mutable_span().slice(points).fill(true);
1323 geometry::curve_simplify(curves.positions().slice(points),
1324 curves.cyclic()[active_curve],
1325 epsilon,
1326 screen_space_positions,
1327 points_to_delete_arr.as_mutable_span().slice(points));
1328
1329 IndexMaskMemory memory;
1330 const IndexMask points_to_delete = IndexMask::from_bools(points_to_delete_arr, memory);
1331 if (!points_to_delete.is_empty()) {
1332 drawing.strokes_for_write().remove_points(points_to_delete, {});
1333 drawing.tag_topology_changed();
1334 }
1335}
1336
1337static void add_strokes_to_drawing(const bool on_back,
1338 Curves *strokes,
1340{
1341 Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
1342 std::array<bke::GeometrySet, 2> geometry_sets;
1343 if (on_back) {
1344 geometry_sets = {bke::GeometrySet::from_curves(strokes),
1345 bke::GeometrySet::from_curves(other_curves)};
1346 }
1347 else {
1348 geometry_sets = {bke::GeometrySet::from_curves(other_curves),
1350 }
1351 drawing.strokes_for_write() = std::move(
1352 geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
1353 drawing.tag_topology_changed();
1354}
1355
1357 const int active_curve,
1358 const bool on_back)
1359{
1360 const bke::CurvesGeometry &curves = drawing.strokes();
1361 const IndexRange points = curves.points_by_curve()[active_curve];
1362 const bke::AttributeAccessor attributes = curves.attributes();
1363 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1364 ".draw_tool_screen_space_positions");
1365 BLI_assert(screen_space_positions_attribute.is_span());
1366 const Span<float2> screen_space_positions =
1367 screen_space_positions_attribute.get_internal_span().slice(points);
1368 /* Extract the drawn stroke into a separate geometry, so we can trim the ends for just this
1369 * stroke. */
1371 drawing.strokes(), IndexRange::from_single(active_curve), {});
1372 auto bounds = bounds::min_max(screen_space_positions);
1373 rcti screen_space_bounds;
1374 BLI_rcti_init(&screen_space_bounds,
1375 int(bounds->min.x),
1376 int(bounds->max.x),
1377 int(bounds->min.y),
1378 int(bounds->max.y));
1379 /* Use the first and last point. */
1380 const Vector<Vector<int>> point_selection = {{0, int(points.index_range().last())}};
1381 /* Trim the stroke ends by finding self intersections using the screen space positions. */
1383 stroke,
1384 screen_space_positions,
1385 {screen_space_bounds},
1387 point_selection,
1388 true);
1389
1390 /* No intersection found. */
1391 if (stroke_trimmed.is_empty()) {
1392 return;
1393 }
1394
1395 /* Remove the original stroke. */
1396 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1397
1398 /* Join the trimmed stroke into the drawing. */
1399 add_strokes_to_drawing(on_back, bke::curves_new_nomain(std::move(stroke_trimmed)), drawing);
1400}
1401
1403 const int active_curve,
1404 const float4x4 &viewmat,
1405 const ed::greasepencil::DrawingPlacement &placement,
1406 const float outline_radius,
1407 const int material_index,
1408 const bool on_back)
1409{
1410 /* Get the outline stroke (single curve). */
1412 drawing,
1413 IndexRange::from_single(active_curve),
1414 viewmat,
1415 3,
1416 outline_radius,
1417 0.0f,
1418 material_index);
1419
1420 /* Reproject the outline onto the drawing placement. */
1421 placement.reproject(outline.positions(), outline.positions_for_write());
1422
1423 /* Remove the original stroke. */
1424 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1425
1426 /* Join the outline stroke into the drawing. */
1427 add_strokes_to_drawing(on_back, bke::curves_new_nomain(std::move(outline)), drawing);
1428}
1429
1431 const float epsilon,
1432 const bool on_back,
1433 const int active_curve)
1434{
1435 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1437 const VArray<float> radii = drawing.radii();
1438
1439 /* Remove points at the end that have a radius close to 0. */
1440 int64_t num_points_to_remove = 0;
1441 for (int64_t index = points.last(); index >= points.first(); index--) {
1442 if (radii[index] < epsilon) {
1443 num_points_to_remove++;
1444 }
1445 else {
1446 break;
1447 }
1448 }
1449
1450 if (num_points_to_remove <= 0) {
1451 return 0;
1452 }
1453
1454 /* Don't remove the entire stroke. Leave at least one point. */
1455 if (points.size() - num_points_to_remove < 1) {
1456 num_points_to_remove = points.size() - 1;
1457 }
1458
1459 if (!on_back) {
1460 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1461 curves.offsets_for_write().last() = curves.points_num();
1462 return num_points_to_remove;
1463 }
1464
1465 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1466 const int last_active_point = curves.points_by_curve()[0].last();
1467
1468 /* Shift the data before resizing to not delete the data at the end. */
1469 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1470 if (iter.domain != bke::AttrDomain::Point) {
1471 return;
1472 }
1473
1475 GMutableSpan attribute_data = dst.span;
1476
1477 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1478 using T = decltype(dummy);
1479 MutableSpan<T> span_data = attribute_data.typed<T>();
1480
1481 for (int i = last_active_point - num_points_to_remove + 1;
1482 i < curves.points_num() - num_points_to_remove;
1483 i++)
1484 {
1485 span_data[i] = span_data[i + num_points_to_remove];
1486 }
1487 });
1488 dst.finish();
1489 });
1490
1491 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1492 MutableSpan<int> offsets = curves.offsets_for_write();
1493 for (const int src_curve : curves.curves_range().drop_front(1)) {
1494 offsets[src_curve] = offsets[src_curve] - num_points_to_remove;
1495 }
1496 offsets.last() = curves.points_num();
1497
1498 return num_points_to_remove;
1499}
1500
1501static void deselect_stroke(const bContext &C,
1503 const int active_curve)
1504{
1505 Scene *scene = CTX_data_scene(&C);
1506 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1507
1510 scene->toolsettings);
1511
1513 curves, selection_domain, bke::AttrType::Bool);
1514
1515 if (selection_domain == bke::AttrDomain::Curve) {
1517 }
1518 else if (selection_domain == bke::AttrDomain::Point) {
1519 ed::curves::fill_selection_false(selection.span.slice(points));
1520 }
1521
1522 selection.finish();
1523}
1524
1525static void process_stroke_weights(const Scene &scene,
1526 const Object &object,
1528 const int active_curve)
1529{
1531 const IndexRange points = curves.points_by_curve()[active_curve];
1532
1533 const int def_nr = BKE_object_defgroup_active_index_get(&object) - 1;
1534
1535 if (def_nr == -1) {
1536 return;
1537 }
1538
1539 const bDeformGroup *defgroup = static_cast<const bDeformGroup *>(
1540 BLI_findlink(BKE_object_defgroup_list(&object), def_nr));
1541
1542 const StringRef vertex_group_name = defgroup->name;
1543
1545 curves, IndexMask(points), vertex_group_name, scene.toolsettings->vgroup_weight);
1546
1547 if (scene.toolsettings->vgroup_weight == 0.0f) {
1548 return;
1549 }
1550
1551 /* Loop through all modifiers trying to find the pose channel for the vertex group name. */
1552 bPoseChannel *channel = nullptr;
1553 Object *ob_arm = nullptr;
1554 LISTBASE_FOREACH (ModifierData *, md, &(&object)->modifiers) {
1555 if (md->type != eModifierType_GreasePencilArmature) {
1556 continue;
1557 }
1558
1559 /* Skip not visible modifiers. */
1560 if (!(md->mode & eModifierMode_Realtime)) {
1561 continue;
1562 }
1563
1565 md);
1566 if (amd == nullptr) {
1567 continue;
1568 }
1569
1570 ob_arm = amd->object;
1571 /* Not an armature. */
1572 if (ob_arm->type != OB_ARMATURE || ob_arm->pose == nullptr) {
1573 continue;
1574 }
1575
1576 channel = BKE_pose_channel_find_name(ob_arm->pose, vertex_group_name.data());
1577 if (channel == nullptr) {
1578 continue;
1579 }
1580
1581 /* Found the channel. */
1582 break;
1583 }
1584
1585 /* Nothing valid was found. */
1586 if (channel == nullptr) {
1587 return;
1588 }
1589
1590 const float4x4 obinv = math::invert(object.object_to_world());
1591
1592 const float4x4 postmat = obinv * ob_arm->object_to_world();
1593 const float4x4 premat = math::invert(postmat);
1594
1595 const float4x4 matrix = postmat * math::invert(float4x4(channel->chan_mat)) * premat;
1596
1597 /* Update the position of the stroke to undo the movement caused by the modifier. */
1598 MutableSpan<float3> positions = curves.positions_for_write().slice(points);
1599 math::transform_points(matrix, positions);
1600}
1601
1603{
1604
1605 const IndexRange points = src.points_by_curve()[curve];
1606 bke::CurvesGeometry dst(points.size(), 1);
1607
1608 Array<int> src_offsets({points.first(), points.one_after_last()});
1609 Array<int> dst_offsets({0, int(points.size())});
1610
1614 {},
1615 src_offsets.as_span(),
1616 dst_offsets.as_span(),
1617 IndexMask{1},
1618 dst.attributes_for_write());
1619
1620 src_offsets = {curve, curve + 1};
1621 dst_offsets = {0, 1};
1622
1626 {},
1627 src_offsets.as_span(),
1628 dst_offsets.as_span(),
1629 IndexMask{1},
1630 dst.attributes_for_write());
1631 return dst;
1632}
1633
1635 const bke::CurvesGeometry &src_strokes,
1636 const int curve,
1637 const int current_frame,
1638 const bool on_back,
1640{
1641 const bke::CurvesGeometry stroke = get_single_stroke(src_strokes, curve);
1642
1643 for (const ed::greasepencil::MutableDrawingInfo &drawing_info : drawings) {
1644 if (drawing_info.frame_number == current_frame) {
1645 continue;
1646 }
1647 add_strokes_to_drawing(on_back, bke::curves_new_nomain(stroke), drawing_info.drawing);
1648 }
1649}
1650
1652{
1653 using namespace blender::bke;
1654 Scene *scene = CTX_data_scene(&C);
1655 Object *object = CTX_data_active_object(&C);
1657 const ARegion *region = CTX_wm_region(&C);
1658 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1659
1660 Paint *paint = &scene->toolsettings->gp_paint->paint;
1662 BrushGpencilSettings *settings = brush->gpencil_settings;
1663 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
1664 const bool do_post_processing = (settings->flag & GP_BRUSH_GROUP_SETTINGS) != 0;
1665 const bool do_automerge_endpoints = (scene->toolsettings->gpencil_flags &
1667
1668 /* Grease Pencil should have an active layer. */
1669 BLI_assert(grease_pencil.has_active_layer());
1670 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1671 /* Drawing should exist. */
1672 bke::greasepencil::Drawing &drawing = *drawing_;
1673 const int active_curve = on_back ? drawing.strokes().curves_range().first() :
1674 drawing.strokes().curves_range().last();
1675 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1676 const IndexRange points = points_by_curve[active_curve];
1677
1678 /* Write the screen space positions of the new stroke as a temporary attribute, so all the
1679 * changes in topology with the operations below get propagated correctly. */
1681 bke::SpanAttributeWriter<float2> screen_space_positions =
1682 attributes.lookup_or_add_for_write_only_span<float2>(".draw_tool_screen_space_positions",
1684 BLI_assert(screen_space_positions);
1685 screen_space_positions.span.slice(points).copy_from(this->screen_space_final_coords_);
1686 screen_space_positions.finish();
1687
1688 /* Remove trailing points with radii close to zero. */
1689 trim_end_points(drawing, 1e-5f, on_back, active_curve);
1690
1691 /* Set the selection of the newly drawn stroke to false. */
1692 deselect_stroke(C, drawing, active_curve);
1693
1694 if (do_post_processing) {
1695 if (settings->draw_smoothfac > 0.0f && settings->draw_smoothlvl > 0) {
1696 smooth_stroke(drawing, settings->draw_smoothfac, settings->draw_smoothlvl, active_curve);
1697 }
1698 if (settings->simplify_px > 0.0f) {
1699 simplify_stroke(drawing, settings->simplify_px, active_curve);
1700 }
1701 if ((settings->flag & GP_BRUSH_TRIM_STROKE) != 0) {
1702 trim_stroke_ends(drawing, active_curve, on_back);
1703 }
1705 process_stroke_weights(*scene, *object, drawing, active_curve);
1706 }
1707 if ((settings->flag & GP_BRUSH_OUTLINE_STROKE) != 0) {
1708 const float outline_radius = brush->unprojected_size / 2.0f * settings->outline_fac * 0.5f;
1709 const int material_index = [&]() {
1711 CTX_data_main(&C), object, brush);
1712 return BKE_object_material_index_get(object, material);
1713 }();
1714 outline_stroke(drawing,
1715 active_curve,
1716 float4x4(rv3d->viewmat),
1717 placement_,
1718 outline_radius,
1719 material_index,
1720 on_back);
1721 }
1722 }
1723 /* Remove the temporary attribute. */
1724 attributes.remove(".draw_tool_screen_space_positions");
1725
1726 drawing.set_texture_matrices({texture_space_}, IndexRange::from_single(active_curve));
1727
1728 if (do_automerge_endpoints) {
1729 constexpr float merge_distance = 20.0f;
1730 const float4x4 layer_to_world = active_layer.to_world_space(*object);
1731 const IndexMask selection = IndexRange::from_single(active_curve);
1733 *region, drawing.strokes(), layer_to_world, merge_distance, selection, {});
1734 }
1735
1736 drawing.tag_topology_changed();
1737
1738 const bool use_multi_frame_editing = (scene->toolsettings->gpencil_flags &
1740
1741 if (use_multi_frame_editing) {
1743 drawing.strokes(), active_curve, frame_number_, on_back, multi_frame_drawings_);
1744 }
1745
1746 /* Now we're done drawing. */
1747 grease_pencil.runtime->is_drawing_stroke = false;
1748
1749 if (do_fill_guides_) {
1751 }
1752
1753 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1754 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil.id);
1755}
1756
1757std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation(const bool do_fill_guides)
1758{
1759 return std::make_unique<PaintOperation>(do_fill_guides);
1760}
1761
1762} // namespace blender::ed::sculpt_paint::greasepencil
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
std::optional< BrushColorJitterSettings > BKE_brush_color_jitter_get_settings(const Paint *paint, const Brush *brush)
Definition brush.cc:1170
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:648
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:607
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:585
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:1519
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:710
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
#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)
MINLINE void copy_v3_v3(float r[3], const float a[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:113
#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:1074
@ 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
@ 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:393
#define ND_DATA
Definition WM_types.hh:509
PyObject * self
BPy_StructRNA * depsgraph
long long int int64_t
unsigned long long int uint64_t
int64_t size() const
Definition BLI_array.hh:256
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
ChannelStorageType a
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 from_single(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, AttrType 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, AttrType 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
nullptr float
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, bke::AttrType 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)
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)
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)
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)
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, bool allow_merging_instance_references=true)
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)
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)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
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
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
short ob_mode
struct CurveMapping * curve_rand_value
struct BrushGpencilSettings * gpencil_settings
float unprojected_size
float color[3]
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)