Blender V4.3
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_material.h"
16#include "BKE_paint.hh"
17#include "BKE_scene.hh"
18
19#include "BLI_bounds.hh"
20#include "BLI_color.hh"
22#include "BLI_math_base.hh"
23#include "BLI_math_color.h"
24#include "BLI_math_geom.h"
25#include "BLI_noise.hh"
26#include "BLI_rand.hh"
27#include "BLI_rect.h"
28#include "BLI_time.h"
29
31
32#include "DNA_brush_enums.h"
33#include "DNA_material_types.h"
34#include "DNA_modifier_types.h"
35
36#include "DNA_scene_types.h"
37#include "ED_curves.hh"
38#include "ED_grease_pencil.hh"
39#include "ED_view3d.hh"
40
43#include "GEO_smooth_curves.hh"
44
45#include "WM_api.hh"
46#include "WM_types.hh"
47
49
50#include <optional>
51
53
55 const Brush *brush,
56 const float3 pos)
57{
58 if ((brush->flag & BRUSH_LOCK_SIZE) != 0) {
59 const float pixel_size = ED_view3d_pixel_size(rv3d, pos);
60 return brush->unprojected_radius / pixel_size;
61 }
62 return float(brush->size);
63}
64
65template<typename T>
66static inline void linear_interpolation(const T &a,
67 const T &b,
69 const bool include_first_point)
70{
71 if (include_first_point) {
72 const float step = math::safe_rcp(float(dst.size() - 1));
73 for (const int i : dst.index_range()) {
74 dst[i] = bke::attribute_math::mix2(float(i) * step, a, b);
75 }
76 }
77 else {
78 const float step = 1.0f / float(dst.size());
79 for (const int i : dst.index_range()) {
80 dst[i] = bke::attribute_math::mix2(float(i + 1) * step, a, b);
81 }
82 }
83}
84
86{
87 return std::accumulate(values.begin(), values.end(), float2(0)) / values.size();
88}
89
91static Array<float2> sample_curve_2d(Span<float2> positions, const int64_t resolution)
92{
93 BLI_assert(positions.size() % 3 == 0);
94 const int64_t num_handles = positions.size() / 3;
95 if (num_handles == 1) {
96 return Array<float2>(resolution, positions[1]);
97 }
98 const int64_t num_segments = num_handles - 1;
99 const int64_t num_points = num_segments * resolution;
100
101 Array<float2> points(num_points);
102 const Span<float2> curve_segments = positions.drop_front(1).drop_back(1);
103 threading::parallel_for(IndexRange(num_segments), 32 * resolution, [&](const IndexRange range) {
104 for (const int64_t segment_i : range) {
105 IndexRange segment_range(segment_i * resolution, resolution);
106 bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0],
107 curve_segments[segment_i * 3 + 1],
108 curve_segments[segment_i * 3 + 2],
109 curve_segments[segment_i * 3 + 3],
110 points.as_mutable_span().slice(segment_range));
111 }
112 });
113 return points;
114}
115
121{
122 BLI_assert(src.size() == dst.size());
123 Array<float> accumulated_lengths_src(src.size() - 1);
124 length_parameterize::accumulate_lengths<float2>(src, false, accumulated_lengths_src);
125
126 Array<float> accumulated_lengths_target(target.size() - 1);
127 length_parameterize::accumulate_lengths<float2>(target, false, accumulated_lengths_target);
128
129 Array<int> segment_indices(accumulated_lengths_src.size());
130 Array<float> segment_factors(accumulated_lengths_src.size());
132 accumulated_lengths_target, accumulated_lengths_src, segment_indices, segment_factors);
133
135 target, segment_indices, segment_factors, dst.drop_back(1));
136 dst.last() = src.last();
137}
138
143static void create_blank_curve(bke::CurvesGeometry &curves, const bool on_back)
144{
145 if (!on_back) {
146 const int num_old_points = curves.points_num();
147 curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
148 curves.offsets_for_write().last(1) = num_old_points;
149 return;
150 }
151
152 curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
153 MutableSpan<int> offsets = curves.offsets_for_write();
154 offsets.first() = 0;
155
156 /* Loop through backwards to not overwrite the data. */
157 for (int i = curves.curves_num() - 2; i >= 0; i--) {
158 offsets[i + 1] = offsets[i] + 1;
159 }
160
161 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
162
163 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
164 bke::GSpanAttributeWriter dst = attributes.lookup_for_write_span(iter.name);
165 GMutableSpan attribute_data = dst.span;
166
167 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
168 using T = decltype(dummy);
169 MutableSpan<T> span_data = attribute_data.typed<T>();
170
171 /* Loop through backwards to not overwrite the data. */
172 for (int i = span_data.size() - 2; i >= 0; i--) {
173 span_data[i + 1] = span_data[i];
174 }
175 });
176 dst.finish();
177 });
178}
179
184static void extend_curve(bke::CurvesGeometry &curves, const bool on_back, const int new_points_num)
185{
186 if (!on_back) {
187 curves.resize(curves.points_num() + new_points_num, curves.curves_num());
188 curves.offsets_for_write().last() = curves.points_num();
189 return;
190 }
191
192 const int last_active_point = curves.points_by_curve()[0].last();
193
194 curves.resize(curves.points_num() + new_points_num, curves.curves_num());
195 MutableSpan<int> offsets = curves.offsets_for_write();
196
197 for (const int src_curve : curves.curves_range().drop_front(1)) {
198 offsets[src_curve] = offsets[src_curve] + new_points_num;
199 }
200 offsets.last() = curves.points_num();
201
202 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
203
204 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
205 if (iter.domain != bke::AttrDomain::Point) {
206 return;
207 }
208
209 bke::GSpanAttributeWriter dst = attributes.lookup_for_write_span(iter.name);
210 GMutableSpan attribute_data = dst.span;
211
212 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
213 using T = decltype(dummy);
214 MutableSpan<T> span_data = attribute_data.typed<T>();
215
216 /* Loop through backwards to not overwrite the data. */
217 for (int i = (span_data.size() - 1) - new_points_num; i >= last_active_point; i--) {
218 span_data[i + new_points_num] = span_data[i];
219 }
220 });
221 dst.finish();
222 });
223
224 curves.tag_topology_changed();
225}
226
228 private:
229 /* Screen space coordinates from input samples. */
230 Vector<float2> screen_space_coords_orig_;
231
232 /* Temporary vector of curve fitted screen space coordinates per input sample from the active
233 * smoothing window. The length of this depends on `active_smooth_start_index_`. */
234 Vector<Vector<float2>> screen_space_curve_fitted_coords_;
235 /* Temporary vector of screen space offsets */
236 Vector<float2> screen_space_jitter_offsets_;
237
238 /* Screen space coordinates after smoothing. */
239 Vector<float2> screen_space_smoothed_coords_;
240 /* Screen space coordinates after smoothing and jittering. */
241 Vector<float2> screen_space_final_coords_;
242
243 /* The start index of the smoothing window. */
244 int active_smooth_start_index_ = 0;
245 blender::float4x2 texture_space_ = float4x2::identity();
246
247 /* Helper class to project screen space coordinates to 3d. */
249
250 /* Direction the pen is moving in smoothed over time. */
251 float2 smoothed_pen_direction_ = float2(0.0f);
252
253 /* Accumulated distance along the stroke. */
254 float accum_distance_ = 0.0f;
255
257
258 float stroke_random_radius_factor_;
259 float stroke_random_opacity_factor_;
260 float stroke_random_rotation_factor_;
261
262 float stroke_random_hue_factor_;
263 float stroke_random_sat_factor_;
264 float stroke_random_val_factor_;
265
266 /* The current time at which the paint operation begins. */
267 double start_time_;
268 /* Current delta time from #start_time_, updated after each extension sample. */
269 double delta_time_;
270
272
273 public:
274 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
275 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
276 void on_stroke_done(const bContext &C) override;
277};
278
286
288
290 ColorGeometry4f vertex_color_ = ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f);
291 ColorGeometry4f fill_color_ = ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f);
293
296
298
300 {
301 scene_ = CTX_data_scene(&C);
302 Object *object = CTX_data_active_object(&C);
303 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
304
305 Paint *paint = &scene_->toolsettings->gp_paint->paint;
306 brush_ = BKE_paint_brush(paint);
307 settings_ = brush_->gpencil_settings;
308
309 use_settings_random_ = (settings_->flag & GP_BRUSH_GROUP_RANDOM) != 0;
310 use_vertex_color_ = (scene_->toolsettings->gp_paint->mode == GPPAINT_FLAG_USE_VERTEXCOLOR);
311 if (use_vertex_color_) {
312 ColorGeometry4f color_base;
313 srgb_to_linearrgb_v3_v3(color_base, brush_->rgb);
314 color_base.a = settings_->vertex_factor;
316 vertex_color_ = color_base;
317 }
319 fill_color_ = color_base;
320 }
321 }
322 softness_ = 1.0f - settings_->hardness;
323
324 BLI_assert(grease_pencil->has_active_layer());
325 drawing_ = grease_pencil->get_editable_drawing_at(*grease_pencil->get_active_layer(),
326 scene_->r.cfra);
327 BLI_assert(drawing_ != nullptr);
328 }
329
331 const float distance,
332 const float radius,
333 const float pressure)
334 {
335 if (!use_settings_random_ || !(settings_->draw_random_press > 0.0f)) {
336 return radius;
337 }
338 float random_factor = 0.0f;
339 if ((settings_->flag2 & GP_BRUSH_USE_PRESS_AT_STROKE) == 0) {
340 /* TODO: This should be exposed as a setting to scale the noise along the stroke. */
341 constexpr float noise_scale = 1 / 20.0f;
342 random_factor = noise::perlin(
343 float2(distance * noise_scale, self.stroke_random_radius_factor_));
344 }
345 else {
346 random_factor = self.stroke_random_radius_factor_;
347 }
348
349 if ((settings_->flag2 & GP_BRUSH_USE_PRESSURE_RAND_PRESS) != 0) {
350 random_factor *= BKE_curvemapping_evaluateF(settings_->curve_rand_pressure, 0, pressure);
351 }
352
353 return math::interpolate(radius, radius * random_factor, settings_->draw_random_press);
354 }
355
357 const float distance,
358 const float opacity,
359 const float pressure)
360 {
361 if (!use_settings_random_ || !(settings_->draw_random_strength > 0.0f)) {
362 return opacity;
363 }
364 float random_factor = 0.0f;
365 if ((settings_->flag2 & GP_BRUSH_USE_STRENGTH_AT_STROKE) == 0) {
366 /* TODO: This should be exposed as a setting to scale the noise along the stroke. */
367 constexpr float noise_scale = 1 / 20.0f;
368 random_factor = noise::perlin(
369 float2(distance * noise_scale, self.stroke_random_opacity_factor_));
370 }
371 else {
372 random_factor = self.stroke_random_opacity_factor_;
373 }
374
375 if ((settings_->flag2 & GP_BRUSH_USE_STRENGTH_RAND_PRESS) != 0) {
376 random_factor *= BKE_curvemapping_evaluateF(settings_->curve_rand_strength, 0, pressure);
377 }
378
379 return math::interpolate(opacity, opacity * random_factor, settings_->draw_random_strength);
380 }
381
382 float randomize_rotation(PaintOperation &self, const float pressure)
383 {
384 if (!use_settings_random_ || !(settings_->uv_random > 0.0f)) {
385 return 0.0f;
386 }
387 float random_factor = 0.0f;
388 if ((settings_->flag2 & GP_BRUSH_USE_UV_AT_STROKE) == 0) {
389 random_factor = self.rng_.get_float();
390 }
391 else {
392 random_factor = self.stroke_random_rotation_factor_;
393 }
394
395 if ((settings_->flag2 & GP_BRUSH_USE_UV_RAND_PRESS) != 0) {
396 random_factor *= BKE_curvemapping_evaluateF(settings_->curve_rand_uv, 0, pressure);
397 }
398
399 const float random_rotation = (random_factor * 2.0f - 1.0f) * math::numbers::pi;
400 return math::interpolate(0.0f, random_rotation, settings_->uv_random);
401 }
402
404 const float distance,
405 const ColorGeometry4f color,
406 const float pressure)
407 {
408 if (!use_settings_random_ ||
409 !(settings_->random_hue > 0.0f || settings_->random_saturation > 0.0f ||
410 settings_->random_value > 0.0f))
411 {
412 return color;
413 }
414 /* TODO: This should be exposed as a setting to scale the noise along the stroke. */
415 constexpr float noise_scale = 1 / 20.0f;
416
417 float random_hue = 0.0f;
418 if ((settings_->flag2 & GP_BRUSH_USE_HUE_AT_STROKE) == 0) {
419 random_hue = noise::perlin(float2(distance * noise_scale, self.stroke_random_hue_factor_));
420 }
421 else {
422 random_hue = self.stroke_random_hue_factor_;
423 }
424
425 float random_saturation = 0.0f;
426 if ((settings_->flag2 & GP_BRUSH_USE_SAT_AT_STROKE) == 0) {
427 random_saturation = noise::perlin(
428 float2(distance * noise_scale, self.stroke_random_sat_factor_));
429 }
430 else {
431 random_saturation = self.stroke_random_sat_factor_;
432 }
433
434 float random_value = 0.0f;
435 if ((settings_->flag2 & GP_BRUSH_USE_VAL_AT_STROKE) == 0) {
436 random_value = noise::perlin(float2(distance * noise_scale, self.stroke_random_val_factor_));
437 }
438 else {
439 random_value = self.stroke_random_val_factor_;
440 }
441
442 if ((settings_->flag2 & GP_BRUSH_USE_HUE_RAND_PRESS) != 0) {
443 random_hue *= BKE_curvemapping_evaluateF(settings_->curve_rand_hue, 0, pressure);
444 }
445 if ((settings_->flag2 & GP_BRUSH_USE_SAT_RAND_PRESS) != 0) {
446 random_saturation *= BKE_curvemapping_evaluateF(
447 settings_->curve_rand_saturation, 0, pressure);
448 }
449 if ((settings_->flag2 & GP_BRUSH_USE_VAL_RAND_PRESS) != 0) {
450 random_value *= BKE_curvemapping_evaluateF(settings_->curve_rand_value, 0, pressure);
451 }
452
453 float3 hsv;
454 rgb_to_hsv_v(color, hsv);
455
456 hsv[0] += math::interpolate(0.5f, random_hue, settings_->random_hue) - 0.5f;
457 /* Wrap hue. */
458 if (hsv[0] > 1.0f) {
459 hsv[0] -= 1.0f;
460 }
461 else if (hsv[0] < 0.0f) {
462 hsv[0] += 1.0f;
463 }
464 hsv[1] *= math::interpolate(1.0f, random_saturation * 2.0f, settings_->random_saturation);
465 hsv[2] *= math::interpolate(1.0f, random_value * 2.0f, settings_->random_value);
466
467 ColorGeometry4f random_color;
468 hsv_to_rgb_v(hsv, random_color);
469 random_color.a = color.a;
470 return random_color;
471 }
472
474 const bContext &C,
475 const InputSample &start_sample,
476 const int material_index,
477 const bool use_fill)
478 {
479 const float2 start_coords = start_sample.mouse_position;
480 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
481 const ARegion *region = CTX_wm_region(&C);
482
483 const float3 start_location = self.placement_.project(start_coords);
484 float start_radius = ed::greasepencil::radius_from_input_sample(
485 rv3d,
486 region,
487 brush_,
488 start_sample.pressure,
489 start_location,
490 self.placement_.to_world_space(),
491 settings_);
492 start_radius = randomize_radius(self, 0.0f, start_radius, start_sample.pressure);
493
494 float start_opacity = ed::greasepencil::opacity_from_input_sample(
495 start_sample.pressure, brush_, settings_);
496 start_opacity = randomize_opacity(self, 0.0f, start_opacity, start_sample.pressure);
497
498 const float start_rotation = randomize_rotation(self, start_sample.pressure);
499 if (use_vertex_color_) {
500 vertex_color_ = randomize_color(self, 0.0f, vertex_color_, start_sample.pressure);
501 }
502
503 Scene *scene = CTX_data_scene(&C);
504 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
505
506 self.screen_space_coords_orig_.append(start_coords);
507 self.screen_space_curve_fitted_coords_.append(Vector<float2>({start_coords}));
508 self.screen_space_jitter_offsets_.append(float2(0.0f));
509 self.screen_space_smoothed_coords_.append(start_coords);
510 self.screen_space_final_coords_.append(start_coords);
511
512 /* Resize the curves geometry so there is one more curve with a single point. */
513 bke::CurvesGeometry &curves = drawing_->strokes_for_write();
514 create_blank_curve(curves, on_back);
515
516 const int active_curve = on_back ? curves.curves_range().first() :
517 curves.curves_range().last();
518 const IndexRange curve_points = curves.points_by_curve()[active_curve];
519 const int last_active_point = curve_points.last();
520
521 Set<std::string> point_attributes_to_skip;
522 Set<std::string> curve_attributes_to_skip;
523 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
524 curves.positions_for_write()[last_active_point] = start_location;
525 drawing_->radii_for_write()[last_active_point] = start_radius;
526 drawing_->opacities_for_write()[last_active_point] = start_opacity;
527 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
528 if (use_vertex_color_ || attributes.contains("vertex_color")) {
529 drawing_->vertex_colors_for_write()[last_active_point] = vertex_color_;
530 point_attributes_to_skip.add("vertex_color");
531 }
532 if (use_fill || attributes.contains("fill_color")) {
533 drawing_->fill_colors_for_write()[active_curve] = fill_color_;
534 curve_attributes_to_skip.add("fill_color");
535 }
536 bke::SpanAttributeWriter<float> delta_times = attributes.lookup_or_add_for_write_span<float>(
537 "delta_time", bke::AttrDomain::Point);
538 delta_times.span[last_active_point] = 0.0f;
539 point_attributes_to_skip.add("delta_time");
540 delta_times.finish();
541
542 bke::SpanAttributeWriter<int> materials = attributes.lookup_or_add_for_write_span<int>(
543 "material_index", bke::AttrDomain::Curve);
544 bke::SpanAttributeWriter<bool> cyclic = attributes.lookup_or_add_for_write_span<bool>(
545 "cyclic", bke::AttrDomain::Curve);
546 bke::SpanAttributeWriter<float> softness = attributes.lookup_or_add_for_write_span<float>(
547 "softness", bke::AttrDomain::Curve);
548 bke::SpanAttributeWriter<float> u_scale = attributes.lookup_or_add_for_write_span<float>(
549 "u_scale", bke::AttrDomain::Curve);
550 cyclic.span[active_curve] = false;
551 materials.span[active_curve] = material_index;
552 softness.span[active_curve] = softness_;
553 u_scale.span[active_curve] = 1.0f;
554 curve_attributes_to_skip.add_multiple({"material_index", "cyclic", "softness", "u_scale"});
555 cyclic.finish();
556 materials.finish();
557 softness.finish();
558 u_scale.finish();
559
560 if (settings_->uv_random > 0.0f || attributes.contains("rotation")) {
561 bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
562 "rotation", bke::AttrDomain::Point);
563 rotations.span[last_active_point] = start_rotation;
564 point_attributes_to_skip.add("rotation");
565 rotations.finish();
566 }
567
568 /* Only set the attribute if the type is not the default or if it already exists. */
569 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
571 attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve);
572 start_caps.span[active_curve] = settings_->caps_type;
573 curve_attributes_to_skip.add("start_cap");
574 start_caps.finish();
575 }
576
577 if (settings_->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
578 bke::SpanAttributeWriter<int8_t> end_caps = attributes.lookup_or_add_for_write_span<int8_t>(
579 "end_cap", bke::AttrDomain::Curve);
580 end_caps.span[active_curve] = settings_->caps_type;
581 curve_attributes_to_skip.add("end_cap");
582 end_caps.finish();
583 }
584
585 if (use_fill && (start_opacity < 1.0f || attributes.contains("fill_opacity"))) {
586 bke::SpanAttributeWriter<float> fill_opacities =
587 attributes.lookup_or_add_for_write_span<float>(
588 "fill_opacity",
589 bke::AttrDomain::Curve,
590 bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, curves.curves_num())));
591 fill_opacities.span[active_curve] = start_opacity;
592 curve_attributes_to_skip.add("fill_opacity");
593 fill_opacities.finish();
594 }
595
596 bke::SpanAttributeWriter<float> init_times = attributes.lookup_or_add_for_write_span<float>(
597 "init_time", bke::AttrDomain::Curve);
598 /* Truncating time in ms to uint32 then we don't lose precision in lower bits. */
599 init_times.span[active_curve] = float(uint64_t(self.start_time_ * double(1e3))) / float(1e3);
600 curve_attributes_to_skip.add("init_time");
601 init_times.finish();
602
603 curves.curve_types_for_write()[active_curve] = CURVE_TYPE_POLY;
604 curve_attributes_to_skip.add("curve_type");
605 curves.update_curve_types();
606
607 /* Initialize the rest of the attributes with default values. */
608 bke::fill_attribute_range_default(
609 attributes,
610 bke::AttrDomain::Point,
611 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
612 IndexRange(last_active_point, 1));
613 bke::fill_attribute_range_default(
614 attributes,
615 bke::AttrDomain::Curve,
616 bke::attribute_filter_from_skip_ref(curve_attributes_to_skip),
617 IndexRange(active_curve, 1));
618
619 drawing_->tag_topology_changed();
620 }
621
622 void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
623 {
624 const Span<float2> coords_to_smooth = self.screen_space_coords_orig_.as_span().slice(
625 smooth_window);
626
627 /* Detect corners in the current slice of coordinates. */
628 const float corner_min_radius_px = 5.0f;
629 const float corner_max_radius_px = 30.0f;
630 const int64_t corner_max_samples = 64;
631 const float corner_angle_threshold = 0.6f;
632 IndexMaskMemory memory;
633 const IndexMask corner_mask = ed::greasepencil::polyline_detect_corners(
634 coords_to_smooth.drop_front(1).drop_back(1),
635 corner_min_radius_px,
636 corner_max_radius_px,
637 corner_max_samples,
638 corner_angle_threshold,
639 memory);
640
641 /* Pre-blur the coordinates for the curve fitting. This generally leads to a better (more
642 * stable) fit. */
643 Array<float2> coords_pre_blur(smooth_window.size());
644 const int pre_blur_iterations = 3;
645 geometry::gaussian_blur_1D(
646 coords_to_smooth,
647 pre_blur_iterations,
648 VArray<float>::ForSingle(settings_->active_smooth, smooth_window.size()),
649 true,
650 true,
651 false,
652 coords_pre_blur.as_mutable_span());
653
654 /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */
655 const float max_error_threshold_px = 5.0f;
656 Array<float2> curve_points = ed::greasepencil::polyline_fit_curve(
657 coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask);
658
659 /* Sampling the curve at a fixed resolution. */
660 const int64_t sample_resolution = 32;
661 Array<float2> sampled_curve_points = sample_curve_2d(curve_points, sample_resolution);
662
663 /* Morphing the coordinates onto the curve. Result is stored in a temporary array. */
664 Array<float2> coords_smoothed(coords_to_smooth.size());
665 morph_points_to_curve(coords_to_smooth, sampled_curve_points, coords_smoothed);
666
667 MutableSpan<float2> window_coords = self.screen_space_smoothed_coords_.as_mutable_span().slice(
668 smooth_window);
669 const float converging_threshold_px = 0.1f;
670 bool stop_counting_converged = false;
671 int num_converged = 0;
672 for (const int64_t window_i : smooth_window.index_range()) {
673 /* Record the curve fitting of this point. */
674 self.screen_space_curve_fitted_coords_[window_i].append(coords_smoothed[window_i]);
675 Span<float2> fit_coords = self.screen_space_curve_fitted_coords_[window_i];
676
677 /* We compare the previous arithmetic mean to the current. Going from the back to the front,
678 * if a point hasn't moved by a minimum threshold, it counts as converged. */
679 float2 new_pos = arithmetic_mean(fit_coords);
680 if (!stop_counting_converged) {
681 float2 prev_pos = window_coords[window_i];
682 if (math::distance(new_pos, prev_pos) < converging_threshold_px) {
683 num_converged++;
684 }
685 else {
686 stop_counting_converged = true;
687 }
688 }
689
690 /* Update the positions in the current cache. */
691 window_coords[window_i] = new_pos;
692 }
693
694 /* Remove all the converged points from the active window and shrink the window accordingly. */
695 if (num_converged > 0) {
696 self.active_smooth_start_index_ += num_converged;
697 self.screen_space_curve_fitted_coords_.remove(0, num_converged);
698 }
699 }
700
702 const int new_points_num,
703 const float brush_radius_px,
704 const float pressure,
705 const IndexRange active_window,
706 MutableSpan<float3> curve_positions)
707 {
708 float jitter_factor = 1.0f;
709 if (settings_->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
710 jitter_factor = BKE_curvemapping_evaluateF(settings_->curve_jitter, 0, pressure);
711 }
712 const float2 tangent = math::normalize(self.smoothed_pen_direction_);
713 const float2 cotangent = float2(-tangent.y, tangent.x);
714 for ([[maybe_unused]] const int _ : IndexRange(new_points_num)) {
715 const float rand = self.rng_.get_float() * 2.0f - 1.0f;
716 const float factor = rand * settings_->draw_jitter * jitter_factor;
717 self.screen_space_jitter_offsets_.append(cotangent * factor * brush_radius_px);
718 }
719 const Span<float2> jitter_slice = self.screen_space_jitter_offsets_.as_mutable_span().slice(
720 active_window);
721 MutableSpan<float2> smoothed_coords =
722 self.screen_space_smoothed_coords_.as_mutable_span().slice(active_window);
723 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
724 active_window);
725 MutableSpan<float3> positions_slice = curve_positions.slice(active_window);
726 for (const int64_t window_i : active_window.index_range()) {
727 final_coords[window_i] = smoothed_coords[window_i] + jitter_slice[window_i];
728 positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
729 }
730 }
731
733 const bContext &C,
734 const InputSample &extension_sample)
735 {
736 Scene *scene = CTX_data_scene(&C);
737 const RegionView3D *rv3d = CTX_wm_region_view3d(&C);
738 const ARegion *region = CTX_wm_region(&C);
739 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
740
741 const float2 coords = extension_sample.mouse_position;
742 float3 position = self.placement_.project(coords);
743 float radius = ed::greasepencil::radius_from_input_sample(rv3d,
744 region,
745 brush_,
746 extension_sample.pressure,
747 position,
748 self.placement_.to_world_space(),
749 settings_);
750 float opacity = ed::greasepencil::opacity_from_input_sample(
751 extension_sample.pressure, brush_, settings_);
752
753 const float brush_radius_px = brush_radius_to_pixel_radius(
754 rv3d, brush_, math::transform_point(self.placement_.to_world_space(), position));
755
756 bke::CurvesGeometry &curves = drawing_->strokes_for_write();
757 OffsetIndices<int> points_by_curve = curves.points_by_curve();
758 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
759
760 const int active_curve = on_back ? curves.curves_range().first() :
761 curves.curves_range().last();
762 const IndexRange curve_points = points_by_curve[active_curve];
763 const int last_active_point = curve_points.last();
764
765 const float2 prev_coords = self.screen_space_coords_orig_.last();
766 float prev_radius = drawing_->radii()[last_active_point];
767 const float prev_opacity = drawing_->opacities()[last_active_point];
768 const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors()[last_active_point];
769
770 const bool is_first_sample = (curve_points.size() == 1);
771
772 /* Use the vector from the previous to the next point. Set the direction based on the first two
773 * samples. For subsequent samples, interpolate with the previous direction to get a smoothed
774 * value over time. */
775 if (is_first_sample) {
776 self.smoothed_pen_direction_ = self.screen_space_coords_orig_.last() - coords;
777 }
778 else {
779 /* The smoothing rate is a factor from 0 to 1 that represents how quickly the
780 * `smoothed_pen_direction_` "reacts" to changes in direction.
781 * - 1.0f: Immediate reaction.
782 * - 0.0f: No reaction (value never changes). */
783 constexpr float smoothing_rate_factor = 0.3f;
784 self.smoothed_pen_direction_ = math::interpolate(self.smoothed_pen_direction_,
785 self.screen_space_coords_orig_.last() -
786 coords,
787 smoothing_rate_factor);
788 }
789
790 /* Approximate brush with non-circular shape by changing the radius based on the angle. */
791 float radius_factor = 1.0f;
792 if (settings_->draw_angle_factor > 0.0f) {
793 /* `angle` is the angle to the horizontal line in screen space. */
794 const float angle = settings_->draw_angle;
795 const float2 angle_vec = float2(math::cos(angle), math::sin(angle));
796
797 /* The angle factor is 1.0f when the direction is aligned with the angle vector and 0.0f when
798 * it is orthogonal to the angle vector. This is consistent with the behavior from GPv2. */
799 const float angle_factor = math::abs(
800 math::dot(angle_vec, math::normalize(self.smoothed_pen_direction_)));
801
802 /* Influence is controlled by `draw_angle_factor`. */
803 radius_factor = math::interpolate(1.0f, angle_factor, settings_->draw_angle_factor);
804 radius *= radius_factor;
805 }
806
807 /* Overwrite last point if it's very close. */
808 const float distance_px = math::distance(coords, prev_coords);
809 constexpr float point_override_threshold_px = 2.0f;
810 if (distance_px < point_override_threshold_px) {
811 self.accum_distance_ += distance_px;
812 /* Don't move the first point of the stroke. */
813 if (!is_first_sample) {
814 curves.positions_for_write()[last_active_point] = position;
815 }
816 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
817 radius = randomize_radius(self, self.accum_distance_, radius, extension_sample.pressure);
818 }
819 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
820 opacity = randomize_opacity(
821 self, self.accum_distance_, opacity, extension_sample.pressure);
822 }
823 drawing_->radii_for_write()[last_active_point] = math::max(radius, prev_radius);
824 drawing_->opacities_for_write()[last_active_point] = math::max(opacity, prev_opacity);
825 return;
826 }
827
828 /* Adjust the first points radius based on the computed angle. */
829 if (is_first_sample && settings_->draw_angle_factor > 0.0f) {
830 drawing_->radii_for_write()[last_active_point] *= radius_factor;
831 prev_radius = drawing_->radii()[last_active_point];
832 }
833
834 /* Clamp the number of points within a pixel in screen space. */
835 constexpr int max_points_per_pixel = 4;
836 /* The value `brush_->spacing` is a percentage of the brush radius in pixels. */
837 const float max_spacing_px = math::max((float(brush_->spacing) / 100.0f) *
838 float(brush_radius_px),
839 1.0f / float(max_points_per_pixel));
840 /* If the next sample is far away, we subdivide the segment to add more points. */
841 const int new_points_num = (distance_px > max_spacing_px) ?
842 int(math::floor(distance_px / max_spacing_px)) :
843 1;
844 /* Resize the curves geometry. */
845 extend_curve(curves, on_back, new_points_num);
846
847 Set<std::string> point_attributes_to_skip;
848 /* Subdivide new segment. */
849 const IndexRange new_points = curves.points_by_curve()[active_curve].take_back(new_points_num);
850 Array<float2> new_screen_space_coords(new_points_num);
851 MutableSpan<float3> positions = curves.positions_for_write();
852 MutableSpan<float3> new_positions = positions.slice(new_points);
853 MutableSpan<float> new_radii = drawing_->radii_for_write().slice(new_points);
854 MutableSpan<float> new_opacities = drawing_->opacities_for_write().slice(new_points);
855
856 /* Interpolate the screen space positions. */
857 linear_interpolation<float2>(prev_coords, coords, new_screen_space_coords, is_first_sample);
858 point_attributes_to_skip.add_multiple({"position", "radius", "opacity"});
859
860 /* Randomize radii. */
861 if (use_settings_random_ && settings_->draw_random_press > 0.0f) {
862 for (const int i : IndexRange(new_points_num)) {
863 new_radii[i] = randomize_radius(
864 self, self.accum_distance_ + max_spacing_px * i, radius, extension_sample.pressure);
865 }
866 }
867 else {
868 linear_interpolation<float>(prev_radius, radius, new_radii, is_first_sample);
869 }
870
871 /* Randomize opacities. */
872 if (use_settings_random_ && settings_->draw_random_strength > 0.0f) {
873 for (const int i : IndexRange(new_points_num)) {
874 new_opacities[i] = randomize_opacity(
875 self, self.accum_distance_ + max_spacing_px * i, opacity, extension_sample.pressure);
876 }
877 }
878 else {
879 linear_interpolation<float>(prev_opacity, opacity, new_opacities, is_first_sample);
880 }
881
882 /* Randomize rotations. */
883 if (use_settings_random_ && (settings_->uv_random > 0.0f || attributes.contains("rotation"))) {
884 bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
885 "rotation", bke::AttrDomain::Point);
886 const MutableSpan<float> new_rotations = rotations.span.slice(new_points);
887 for (const int i : IndexRange(new_points_num)) {
888 new_rotations[i] = randomize_rotation(self, extension_sample.pressure);
889 }
890 point_attributes_to_skip.add("rotation");
891 rotations.finish();
892 }
893
894 /* Randomize vertex color. */
895 if (use_vertex_color_ || attributes.contains("vertex_color")) {
896 MutableSpan<ColorGeometry4f> new_vertex_colors = drawing_->vertex_colors_for_write().slice(
897 new_points);
898 if (use_settings_random_ || attributes.contains("vertex_color")) {
899 for (const int i : IndexRange(new_points_num)) {
900 new_vertex_colors[i] = randomize_color(self,
901 self.accum_distance_ + max_spacing_px * i,
902 vertex_color_,
903 extension_sample.pressure);
904 }
905 }
906 else {
907 linear_interpolation<ColorGeometry4f>(
908 prev_vertex_color, vertex_color_, new_vertex_colors, is_first_sample);
909 }
910 point_attributes_to_skip.add("vertex_color");
911 }
912
913 bke::SpanAttributeWriter<float> delta_times = attributes.lookup_or_add_for_write_span<float>(
914 "delta_time", bke::AttrDomain::Point);
915 const double new_delta_time = BLI_time_now_seconds() - self.start_time_;
916 linear_interpolation<float>(float(self.delta_time_),
917 float(new_delta_time),
918 delta_times.span.slice(new_points),
919 is_first_sample);
920 point_attributes_to_skip.add("delta_time");
921 delta_times.finish();
922
923 /* Update the accumulated distance along the stroke in pixels. */
924 self.accum_distance_ += distance_px;
925
926 /* Update the current delta time. */
927 self.delta_time_ = new_delta_time;
928
929 /* Update screen space buffers with new points. */
930 self.screen_space_coords_orig_.extend(new_screen_space_coords);
931 self.screen_space_smoothed_coords_.extend(new_screen_space_coords);
932 self.screen_space_final_coords_.extend(new_screen_space_coords);
933 for (float2 new_position : new_screen_space_coords) {
934 self.screen_space_curve_fitted_coords_.append(Vector<float2>({new_position}));
935 }
936
937 /* Only start smoothing if there are enough points. */
938 constexpr int64_t min_active_smoothing_points_num = 8;
939 const IndexRange smooth_window = self.screen_space_coords_orig_.index_range().drop_front(
940 self.active_smooth_start_index_);
941 if (smooth_window.size() < min_active_smoothing_points_num) {
942 self.placement_.project(new_screen_space_coords, new_positions);
943 }
944 else {
945 /* Active smoothing is done in a window at the end of the new stroke. */
946 this->active_smoothing(self, smooth_window);
947 }
948
949 MutableSpan<float3> curve_positions = positions.slice(curves.points_by_curve()[active_curve]);
950 if (use_settings_random_ && settings_->draw_jitter > 0.0f) {
951 this->active_jitter(self,
952 new_points_num,
953 brush_radius_px,
954 extension_sample.pressure,
955 smooth_window,
956 curve_positions);
957 }
958 else {
959 MutableSpan<float2> smoothed_coords =
960 self.screen_space_smoothed_coords_.as_mutable_span().slice(smooth_window);
961 MutableSpan<float2> final_coords = self.screen_space_final_coords_.as_mutable_span().slice(
962 smooth_window);
963 /* Not jitter, so we just copy the positions over. */
964 final_coords.copy_from(smoothed_coords);
965 MutableSpan<float3> curve_positions_slice = curve_positions.slice(smooth_window);
966 for (const int64_t window_i : smooth_window.index_range()) {
967 curve_positions_slice[window_i] = self.placement_.project(final_coords[window_i]);
968 }
969 }
970
971 /* Initialize the rest of the attributes with default values. */
972 bke::fill_attribute_range_default(
973 attributes,
974 bke::AttrDomain::Point,
975 bke::attribute_filter_from_skip_ref(point_attributes_to_skip),
976 curves.points_range().take_back(1));
977
978 drawing_->set_texture_matrices({self.texture_space_}, IndexRange::from_single(active_curve));
979 }
980
981 void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
982 {
983 const Scene *scene = CTX_data_scene(&C);
984 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
985
986 this->process_extension_sample(self, C, extension_sample);
987
988 const bke::CurvesGeometry &curves = drawing_->strokes();
989 const int active_curve = on_back ? curves.curves_range().first() :
990 curves.curves_range().last();
991 drawing_->tag_topology_changed(IndexRange::from_single(active_curve));
992 }
993};
994
995void PaintOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
996{
997 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
998 ARegion *region = CTX_wm_region(&C);
999 View3D *view3d = CTX_wm_view3d(&C);
1000 Scene *scene = CTX_data_scene(&C);
1001 Object *object = CTX_data_active_object(&C);
1002 Object *eval_object = DEG_get_evaluated_object(depsgraph, object);
1003 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1004
1005 Paint *paint = &scene->toolsettings->gp_paint->paint;
1006 Brush *brush = BKE_paint_brush(paint);
1007
1008 if (brush->gpencil_settings == nullptr) {
1010 }
1011 BrushGpencilSettings *settings = brush->gpencil_settings;
1012
1013 BKE_curvemapping_init(settings->curve_sensitivity);
1014 BKE_curvemapping_init(settings->curve_strength);
1015 BKE_curvemapping_init(settings->curve_jitter);
1016 BKE_curvemapping_init(settings->curve_rand_pressure);
1017 BKE_curvemapping_init(settings->curve_rand_strength);
1018 BKE_curvemapping_init(settings->curve_rand_uv);
1019 BKE_curvemapping_init(settings->curve_rand_hue);
1020 BKE_curvemapping_init(settings->curve_rand_saturation);
1021 BKE_curvemapping_init(settings->curve_rand_value);
1022
1023 const bke::greasepencil::Layer &layer = *grease_pencil->get_active_layer();
1024 /* Initialize helper class for projecting screen space coordinates. */
1025 placement_ = ed::greasepencil::DrawingPlacement(*scene, *region, *view3d, *eval_object, &layer);
1026 if (placement_.use_project_to_surface()) {
1027 placement_.cache_viewport_depths(depsgraph, region, view3d);
1028 }
1029 else if (placement_.use_project_to_nearest_stroke()) {
1030 placement_.cache_viewport_depths(depsgraph, region, view3d);
1031 placement_.set_origin_to_nearest_stroke(start_sample.mouse_position);
1032 }
1033
1034 texture_space_ = ed::greasepencil::calculate_texture_space(
1035 scene, region, start_sample.mouse_position, placement_);
1036
1037 /* `View` is already stored in object space but all others are in layer space. */
1038 if (scene->toolsettings->gp_sculpt.lock_axis != GP_LOCKAXIS_VIEW) {
1039 texture_space_ = texture_space_ * layer.to_object_space(*object);
1040 }
1041
1042 rng_ = RandomNumberGenerator::from_random_seed();
1043 if ((settings->flag & GP_BRUSH_GROUP_RANDOM) != 0) {
1044 stroke_random_radius_factor_ = rng_.get_float();
1045 stroke_random_opacity_factor_ = rng_.get_float();
1046 stroke_random_rotation_factor_ = rng_.get_float();
1047
1048 stroke_random_hue_factor_ = rng_.get_float();
1049 stroke_random_sat_factor_ = rng_.get_float();
1050 stroke_random_val_factor_ = rng_.get_float();
1051 }
1052
1054 CTX_data_main(&C), object, brush);
1055 const int material_index = BKE_object_material_index_get(object, material);
1056 const bool use_fill = (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
1057
1058 /* We're now starting to draw. */
1059 grease_pencil->runtime->is_drawing_stroke = true;
1060
1061 /* Initialize the start time to the current time. */
1062 start_time_ = BLI_time_now_seconds();
1063 /* Delta time starts at 0. */
1064 delta_time_ = 0.0f;
1065
1066 PaintOperationExecutor executor{C};
1067 executor.process_start_sample(*this, C, start_sample, material_index, use_fill);
1068
1069 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1070 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1071}
1072
1073void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
1074{
1075 Object *object = CTX_data_active_object(&C);
1076 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
1077
1078 PaintOperationExecutor executor{C};
1079 executor.execute(*this, C, extension_sample);
1080
1081 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
1082 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil);
1083}
1084
1086 const float influence,
1087 const int iterations,
1088 const int active_curve)
1089{
1090 bke::CurvesGeometry &curves = drawing.strokes_for_write();
1091 const IndexRange stroke = IndexRange::from_single(active_curve);
1092 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1093 const VArray<bool> cyclic = curves.cyclic();
1094 const VArray<bool> point_selection = VArray<bool>::ForSingle(true, curves.points_num());
1095
1096 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1097 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1098 geometry::smooth_curve_attribute(stroke,
1099 points_by_curve,
1100 point_selection,
1101 cyclic,
1102 iterations,
1103 influence,
1104 false,
1105 true,
1106 positions.span);
1107 positions.finish();
1108 drawing.tag_positions_changed();
1109
1110 if (drawing.opacities().is_span()) {
1111 bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
1112 geometry::smooth_curve_attribute(stroke,
1113 points_by_curve,
1114 point_selection,
1115 cyclic,
1116 iterations,
1117 influence,
1118 true,
1119 false,
1120 opacities.span);
1121 opacities.finish();
1122 }
1123 if (drawing.radii().is_span()) {
1124 bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
1125 geometry::smooth_curve_attribute(stroke,
1126 points_by_curve,
1127 point_selection,
1128 cyclic,
1129 iterations,
1130 influence,
1131 true,
1132 false,
1133 radii.span);
1134 radii.finish();
1135 }
1136}
1137
1139 const float epsilon,
1140 const int active_curve)
1141{
1142 const bke::CurvesGeometry &curves = drawing.strokes();
1143 const bke::AttributeAccessor attributes = curves.attributes();
1144 const IndexRange points = curves.points_by_curve()[active_curve];
1145 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1146 ".draw_tool_screen_space_positions");
1147 BLI_assert(screen_space_positions_attribute.is_span());
1148
1149 const Span<float2> screen_space_positions =
1150 screen_space_positions_attribute.get_internal_span().slice(points);
1151
1152 Array<bool> points_to_delete_arr(drawing.strokes().points_num(), false);
1153 points_to_delete_arr.as_mutable_span().slice(points).fill(true);
1154 geometry::curve_simplify(curves.positions().slice(points),
1155 curves.cyclic()[active_curve],
1156 epsilon,
1157 screen_space_positions,
1158 points_to_delete_arr.as_mutable_span().slice(points));
1159
1160 IndexMaskMemory memory;
1161 const IndexMask points_to_delete = IndexMask::from_bools(points_to_delete_arr, memory);
1162 if (!points_to_delete.is_empty()) {
1163 drawing.strokes_for_write().remove_points(points_to_delete, {});
1164 drawing.tag_topology_changed();
1165 }
1166}
1167
1169 const int active_curve,
1170 const bool on_back)
1171{
1172 const bke::CurvesGeometry &curves = drawing.strokes();
1173 const IndexRange points = curves.points_by_curve()[active_curve];
1174 const bke::AttributeAccessor attributes = curves.attributes();
1175 const VArray<float2> screen_space_positions_attribute = *attributes.lookup<float2>(
1176 ".draw_tool_screen_space_positions");
1177 BLI_assert(screen_space_positions_attribute.is_span());
1178 const Span<float2> screen_space_positions =
1179 screen_space_positions_attribute.get_internal_span().slice(points);
1180 /* Extract the drawn stroke into a separate geometry, so we can trim the ends for just this
1181 * stroke. */
1182 bke::CurvesGeometry stroke = bke::curves_copy_curve_selection(
1183 drawing.strokes(), IndexRange::from_single(active_curve), {});
1184 auto bounds = bounds::min_max(screen_space_positions);
1185 rcti screen_space_bounds;
1186 BLI_rcti_init(&screen_space_bounds,
1187 int(bounds->min.x),
1188 int(bounds->max.x),
1189 int(bounds->min.y),
1190 int(bounds->max.y));
1191 /* Use the first and last point. */
1192 const Vector<Vector<int>> point_selection = {{0, int(points.index_range().last())}};
1193 /* Trim the stroke ends by finding self intersections using the screen space positions. */
1194 bke::CurvesGeometry stroke_trimmed = ed::greasepencil::trim::trim_curve_segments(
1195 stroke,
1196 screen_space_positions,
1197 {screen_space_bounds},
1198 IndexRange::from_single(0),
1199 point_selection,
1200 true);
1201
1202 /* No intersection found. */
1203 if (stroke_trimmed.points_num() == 0) {
1204 return;
1205 }
1206
1207 /* Remove the original stroke. */
1208 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1209
1210 /* Join the trimmed stroke into the drawing. */
1211 Curves *trimmed_curve = bke::curves_new_nomain(std::move(stroke_trimmed));
1212 Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
1213 std::array<bke::GeometrySet, 2> geometry_sets;
1214 if (on_back) {
1215 geometry_sets = {bke::GeometrySet::from_curves(trimmed_curve),
1216 bke::GeometrySet::from_curves(other_curves)};
1217 }
1218 else {
1219 geometry_sets = {bke::GeometrySet::from_curves(other_curves),
1220 bke::GeometrySet::from_curves(trimmed_curve)};
1221 }
1222 drawing.strokes_for_write() = std::move(
1223 geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
1224 drawing.tag_topology_changed();
1225}
1226
1228 const int active_curve,
1229 const float4x4 &viewmat,
1230 const ed::greasepencil::DrawingPlacement &placement,
1231 const float outline_radius,
1232 const int material_index,
1233 const bool on_back)
1234{
1235 /* Get the outline stroke (single curve). */
1236 bke::CurvesGeometry outline = ed::greasepencil::create_curves_outline(
1237 drawing,
1238 IndexRange::from_single(active_curve),
1239 viewmat,
1240 3,
1241 outline_radius,
1242 0.0f,
1243 material_index);
1244
1245 /* Reproject the outline onto the drawing placement. */
1246 placement.reproject(outline.positions(), outline.positions_for_write());
1247
1248 /* Remove the original stroke. */
1249 drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
1250
1251 /* Join the outline stroke into the drawing. */
1252 Curves *outline_curve = bke::curves_new_nomain(std::move(outline));
1253 Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
1254 std::array<bke::GeometrySet, 2> geometry_sets;
1255 if (on_back) {
1256 geometry_sets = {bke::GeometrySet::from_curves(outline_curve),
1257 bke::GeometrySet::from_curves(other_curves)};
1258 }
1259 else {
1260 geometry_sets = {bke::GeometrySet::from_curves(other_curves),
1261 bke::GeometrySet::from_curves(outline_curve)};
1262 }
1263 drawing.strokes_for_write() = std::move(
1264 geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
1265 drawing.tag_topology_changed();
1266}
1267
1269 const float epsilon,
1270 const bool on_back,
1271 const int active_curve)
1272{
1273 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1274 bke::CurvesGeometry &curves = drawing.strokes_for_write();
1275 const VArray<float> radii = drawing.radii();
1276
1277 /* Remove points at the end that have a radius close to 0. */
1278 int64_t num_points_to_remove = 0;
1279 for (int64_t index = points.last(); index >= points.first(); index--) {
1280 if (radii[index] < epsilon) {
1281 num_points_to_remove++;
1282 }
1283 else {
1284 break;
1285 }
1286 }
1287
1288 if (num_points_to_remove <= 0) {
1289 return 0;
1290 }
1291
1292 /* Don't remove the entire stroke. Leave at least one point. */
1293 if (points.size() - num_points_to_remove < 1) {
1294 num_points_to_remove = points.size() - 1;
1295 }
1296
1297 if (!on_back) {
1298 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1299 curves.offsets_for_write().last() = curves.points_num();
1300 return num_points_to_remove;
1301 }
1302
1303 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1304 const int last_active_point = curves.points_by_curve()[0].last();
1305
1306 /* Shift the data before resizing to not delete the data at the end. */
1307 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1308 if (iter.domain != bke::AttrDomain::Point) {
1309 return;
1310 }
1311
1312 bke::GSpanAttributeWriter dst = attributes.lookup_for_write_span(iter.name);
1313 GMutableSpan attribute_data = dst.span;
1314
1315 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1316 using T = decltype(dummy);
1317 MutableSpan<T> span_data = attribute_data.typed<T>();
1318
1319 for (int i = last_active_point - num_points_to_remove + 1;
1320 i < curves.points_num() - num_points_to_remove;
1321 i++)
1322 {
1323 span_data[i] = span_data[i + num_points_to_remove];
1324 }
1325 });
1326 dst.finish();
1327 });
1328
1329 curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
1330 MutableSpan<int> offsets = curves.offsets_for_write();
1331 for (const int src_curve : curves.curves_range().drop_front(1)) {
1332 offsets[src_curve] = offsets[src_curve] - num_points_to_remove;
1333 }
1334 offsets.last() = curves.points_num();
1335
1336 return num_points_to_remove;
1337}
1338
1339static void deselect_stroke(const bContext &C,
1341 const int active_curve)
1342{
1343 Scene *scene = CTX_data_scene(&C);
1344 const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
1345
1346 bke::CurvesGeometry &curves = drawing.strokes_for_write();
1348 scene->toolsettings);
1349
1350 bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
1351 curves, selection_domain, CD_PROP_BOOL);
1352
1353 if (selection_domain == bke::AttrDomain::Curve) {
1354 ed::curves::fill_selection_false(selection.span.slice(IndexRange::from_single(active_curve)));
1355 }
1356 else if (selection_domain == bke::AttrDomain::Point) {
1357 ed::curves::fill_selection_false(selection.span.slice(points));
1358 }
1359
1360 selection.finish();
1361}
1362
1363static void process_stroke_weights(const Scene &scene,
1364 const Object &object,
1366 const int active_curve)
1367{
1368 bke::CurvesGeometry &curves = drawing.strokes_for_write();
1369 const IndexRange points = curves.points_by_curve()[active_curve];
1370
1371 const int def_nr = BKE_object_defgroup_active_index_get(&object) - 1;
1372
1373 if (def_nr == -1) {
1374 return;
1375 }
1376
1377 const bDeformGroup *defgroup = static_cast<const bDeformGroup *>(
1378 BLI_findlink(BKE_object_defgroup_list(&object), def_nr));
1379
1380 const StringRef vertex_group_name = defgroup->name;
1381
1383 curves, IndexMask(points), vertex_group_name, scene.toolsettings->vgroup_weight);
1384
1385 if (scene.toolsettings->vgroup_weight == 0.0f) {
1386 return;
1387 }
1388
1389 /* Loop through all modifiers trying to find the pose channel for the vertex group name. */
1390 bPoseChannel *channel = nullptr;
1391 Object *ob_arm = nullptr;
1392 LISTBASE_FOREACH (ModifierData *, md, &(&object)->modifiers) {
1393 if (md->type != eModifierType_GreasePencilArmature) {
1394 continue;
1395 }
1396
1397 /* Skip not visible modifiers. */
1398 if (!(md->mode & eModifierMode_Realtime)) {
1399 continue;
1400 }
1401
1403 md);
1404 if (amd == nullptr) {
1405 continue;
1406 }
1407
1408 ob_arm = amd->object;
1409 /* Not an armature. */
1410 if (ob_arm->type != OB_ARMATURE || ob_arm->pose == nullptr) {
1411 continue;
1412 }
1413
1414 channel = BKE_pose_channel_find_name(ob_arm->pose, vertex_group_name.data());
1415 if (channel == nullptr) {
1416 continue;
1417 }
1418
1419 /* Found the channel. */
1420 break;
1421 }
1422
1423 /* Nothing valid was found. */
1424 if (channel == nullptr) {
1425 return;
1426 }
1427
1428 const float4x4 obinv = math::invert(object.object_to_world());
1429
1430 const float4x4 postmat = obinv * ob_arm->object_to_world();
1431 const float4x4 premat = math::invert(postmat);
1432
1433 const float4x4 matrix = postmat * math::invert(float4x4(channel->chan_mat)) * premat;
1434
1435 /* Update the position of the stroke to undo the movement caused by the modifier.*/
1436 MutableSpan<float3> positions = curves.positions_for_write().slice(points);
1437 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
1438 for (float3 &position : positions.slice(range)) {
1439 position = math::transform_point(matrix, position);
1440 }
1441 });
1442}
1443
1444void PaintOperation::on_stroke_done(const bContext &C)
1445{
1446 using namespace blender::bke;
1447 Scene *scene = CTX_data_scene(&C);
1448 Object *object = CTX_data_active_object(&C);
1450 const ARegion *region = CTX_wm_region(&C);
1451 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1452
1453 Paint *paint = &scene->toolsettings->gp_paint->paint;
1454 Brush *brush = BKE_paint_brush(paint);
1455 BrushGpencilSettings *settings = brush->gpencil_settings;
1456 const bool on_back = (scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) != 0;
1457 const bool do_post_processing = (settings->flag & GP_BRUSH_GROUP_SETTINGS) != 0;
1458 const bool do_automerge_endpoints = (scene->toolsettings->gpencil_flags &
1460
1461 /* Grease Pencil should have an active layer. */
1462 BLI_assert(grease_pencil.has_active_layer());
1463 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1464 /* Drawing should exist. */
1465 bke::greasepencil::Drawing &drawing = *grease_pencil.get_editable_drawing_at(active_layer,
1466 scene->r.cfra);
1467 const int active_curve = on_back ? drawing.strokes().curves_range().first() :
1468 drawing.strokes().curves_range().last();
1469 const offset_indices::OffsetIndices<int> points_by_curve = drawing.strokes().points_by_curve();
1470 const IndexRange points = points_by_curve[active_curve];
1471
1472 /* Write the screen space positions of the new stroke as a temporary attribute, so all the
1473 * changes in topology with the operations below get propagated correctly. */
1475 bke::SpanAttributeWriter<float2> screen_space_positions =
1476 attributes.lookup_or_add_for_write_only_span<float2>(".draw_tool_screen_space_positions",
1477 bke::AttrDomain::Point);
1478 screen_space_positions.span.slice(points).copy_from(this->screen_space_final_coords_);
1479 screen_space_positions.finish();
1480
1481 /* Remove trailing points with radii close to zero. */
1482 trim_end_points(drawing, 1e-5f, on_back, active_curve);
1483
1484 /* Set the selection of the newly drawn stroke to false. */
1485 deselect_stroke(C, drawing, active_curve);
1486
1487 if (do_post_processing) {
1488 if (settings->draw_smoothfac > 0.0f) {
1489 smooth_stroke(drawing, settings->draw_smoothfac, settings->draw_smoothlvl, active_curve);
1490 }
1491 if (settings->simplify_px > 0.0f) {
1492 simplify_stroke(drawing, settings->simplify_px, active_curve);
1493 }
1494 if ((settings->flag & GP_BRUSH_TRIM_STROKE) != 0) {
1495 trim_stroke_ends(drawing, active_curve, on_back);
1496 }
1497 if ((scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) != 0) {
1498 process_stroke_weights(*scene, *object, drawing, active_curve);
1499 }
1500 if ((settings->flag & GP_BRUSH_OUTLINE_STROKE) != 0) {
1501 const float outline_radius = float(brush->unprojected_radius) * settings->outline_fac * 0.5f;
1502 const int material_index = [&]() {
1504 CTX_data_main(&C), object, brush);
1505 const int active_index = BKE_object_material_index_get(object, material);
1506 if (settings->material_alt == nullptr) {
1507 return active_index;
1508 }
1509 const int alt_index = BKE_object_material_slot_find_index(object, settings->material_alt);
1510 return (alt_index > -1) ? alt_index - 1 : active_index;
1511 }();
1512 outline_stroke(drawing,
1513 active_curve,
1514 float4x4(rv3d->viewmat),
1515 placement_,
1516 outline_radius,
1517 material_index,
1518 on_back);
1519 }
1520 }
1521 /* Remove the temporary attribute. */
1522 attributes.remove(".draw_tool_screen_space_positions");
1523
1524 drawing.set_texture_matrices({texture_space_}, IndexRange::from_single(active_curve));
1525
1526 if (do_automerge_endpoints) {
1527 constexpr float merge_distance = 20.0f;
1528 const float4x4 layer_to_world = active_layer.to_world_space(*object);
1529 const IndexMask selection = IndexRange::from_single(active_curve);
1530 drawing.strokes_for_write() = ed::greasepencil::curves_merge_endpoints_by_distance(
1531 *region, drawing.strokes(), layer_to_world, merge_distance, selection, {});
1532 }
1533
1534 drawing.tag_topology_changed();
1535
1536 /* Now we're done drawing. */
1537 grease_pencil.runtime->is_drawing_stroke = false;
1538
1539 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1540 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil.id);
1541}
1542
1543std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation()
1544{
1545 return std::make_unique<PaintOperation>();
1546}
1547
1548} // namespace blender::ed::sculpt_paint::greasepencil
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:563
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:601
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:579
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_ensure_from_active_input_brush(Main *bmain, Object *ob, Brush *brush)
Utility functions for vertex groups in grease pencil objects.
General operations, lookup, etc. for materials.
short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma)
int BKE_object_material_index_get(Object *ob, const Material *ma)
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ GP_BRUSH_USE_SAT_RAND_PRESS
@ GP_BRUSH_USE_STRENGTH_RAND_PRESS
@ GP_BRUSH_USE_VAL_RAND_PRESS
@ GP_BRUSH_USE_HUE_RAND_PRESS
@ GP_BRUSH_USE_STRENGTH_AT_STROKE
@ GP_BRUSH_USE_UV_RAND_PRESS
@ GP_BRUSH_USE_HUE_AT_STROKE
@ GP_BRUSH_USE_VAL_AT_STROKE
@ GP_BRUSH_USE_SAT_AT_STROKE
@ GP_BRUSH_USE_PRESS_AT_STROKE
@ GP_BRUSH_USE_UV_AT_STROKE
@ GP_BRUSH_USE_PRESSURE_RAND_PRESS
@ GP_BRUSH_GROUP_RANDOM
@ GP_BRUSH_OUTLINE_STROKE
@ GP_BRUSH_TRIM_STROKE
@ GP_BRUSH_GROUP_SETTINGS
@ GP_BRUSH_USE_JITTER_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_ARMATURE
@ GPPAINT_FLAG_USE_VERTEXCOLOR
@ GP_LOCKAXIS_VIEW
@ GP_TOOL_FLAG_PAINT_ONBACK
@ GP_TOOL_FLAG_AUTOMERGE_STROKE
@ GP_TOOL_FLAG_CREATE_WEIGHTS
float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
PyObject * self
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
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:88
const CPPType & type() const
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange take_back(int64_t n) const
constexpr IndexRange index_range() const
constexpr IndexRange drop_front(int64_t n) const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr T & first() const
Definition BLI_span.hh:680
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
bool add(const Key &key)
Definition BLI_set.hh:248
void add_multiple(Span< Key > keys)
Definition BLI_set.hh:268
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:172
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr Span drop_back(int64_t n) const
Definition BLI_span.hh:183
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr const char * data() const
Span< T > get_internal_span() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) 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)
void remove_points(const IndexMask &points_to_delete, const AttributeFilter &attribute_filter)
VArray< ColorGeometry4f > vertex_colors() const
MutableSpan< float > opacities_for_write()
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
VArray< float > opacities() const
MutableSpan< ColorGeometry4f > fill_colors_for_write()
MutableSpan< ColorGeometry4f > vertex_colors_for_write()
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
float4x4 to_world_space(const Object &object) const
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
IndexRange range
blender::bke::AttrDomain ED_grease_pencil_edit_selection_domain_get(const ToolSettings *tool_settings)
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)
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 create_blank_curve(bke::CurvesGeometry &curves, const bool on_back)
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 void extend_curve(bke::CurvesGeometry &curves, const bool on_back, const int new_points_num)
static int trim_end_points(bke::greasepencil::Drawing &drawing, const float epsilon, const bool on_back, const int active_curve)
std::unique_ptr< GreasePencilStrokeOperation > new_paint_operation()
static float brush_radius_to_pixel_radius(const RegionView3D *rv3d, const Brush *brush, const float3 pos)
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)
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:95
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
signed char int8_t
Definition stdint.h:75
struct CurveMapping * curve_jitter
struct CurveMapping * curve_rand_pressure
struct CurveMapping * curve_rand_strength
struct CurveMapping * curve_rand_saturation
struct CurveMapping * curve_rand_hue
struct CurveMapping * curve_rand_uv
struct CurveMapping * curve_rand_value
float unprojected_radius
float rgb[3]
struct BrushGpencilSettings * gpencil_settings
GreasePencilRuntimeHandle * runtime
struct bPose * pose
float viewmat[4][4]
struct ToolSettings * toolsettings
struct RenderData r
float randomize_radius(PaintOperation &self, const float distance, const float radius, const float pressure)
void active_smoothing(PaintOperation &self, const IndexRange smooth_window)
void process_extension_sample(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
float randomize_rotation(PaintOperation &self, const float pressure)
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)
float randomize_opacity(PaintOperation &self, const float distance, const float opacity, const float pressure)
void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample)
ColorGeometry4f randomize_color(PaintOperation &self, const float distance, const ColorGeometry4f color, const float pressure)
void process_start_sample(PaintOperation &self, const bContext &C, const InputSample &start_sample, const int material_index, const bool use_fill)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)