Blender V5.0
grease_pencil_utils.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
8
10#include "BKE_attribute.hh"
11#include "BKE_brush.hh"
12#include "BKE_colortools.hh"
13#include "BKE_context.hh"
14#include "BKE_curves_utils.hh"
15#include "BKE_deform.hh"
16#include "BKE_grease_pencil.hh"
17#include "BKE_lib_id.hh"
18#include "BKE_material.hh"
19#include "BKE_paint.hh"
20#include "BKE_report.hh"
21#include "BKE_scene.hh"
22
23#include "BLI_array_utils.hh"
24#include "BLI_bounds.hh"
25#include "BLI_listbase.h"
26#include "BLI_math_geom.h"
27#include "BLI_math_numbers.hh"
28#include "BLI_math_vector.hh"
29#include "BLI_vector_set.hh"
30
31#include "DNA_brush_types.h"
32#include "DNA_material_types.h"
33#include "DNA_object_types.h"
34#include "DNA_scene_types.h"
35#include "DNA_view3d_types.h"
36
38
39#include "GEO_merge_layers.hh"
40
41#include "RNA_prototypes.hh"
42
43#include "ED_curves.hh"
44#include "ED_grease_pencil.hh"
45#include "ED_view3d.hh"
46
48
50 const ARegion &region,
51 const View3D &view3d,
52 const Object &eval_object,
53 const bke::greasepencil::Layer *layer)
54 : region_(&region), view3d_(&view3d)
55{
56 layer_space_to_world_space_ = (layer != nullptr) ? layer->to_world_space(eval_object) :
57 eval_object.object_to_world();
58 world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
59 /* Initialize DrawingPlacementPlane from toolsettings. */
61 case GP_LOCKAXIS_VIEW:
62 plane_ = DrawingPlacementPlane::View;
63 break;
64 case GP_LOCKAXIS_Y:
65 plane_ = DrawingPlacementPlane::Front;
66 placement_normal_ = float3(0, 1, 0);
67 break;
68 case GP_LOCKAXIS_X:
69 plane_ = DrawingPlacementPlane::Side;
70 placement_normal_ = float3(1, 0, 0);
71 break;
72 case GP_LOCKAXIS_Z:
73 plane_ = DrawingPlacementPlane::Top;
74 placement_normal_ = float3(0, 0, 1);
75 break;
76 case GP_LOCKAXIS_CURSOR: {
77 plane_ = DrawingPlacementPlane::Cursor;
78 placement_normal_ = scene.cursor.matrix<float3x3>() * float3(0, 0, 1);
79 break;
80 }
81 }
82
83 /* Account for layer transform. */
84 if (!ELEM(scene.toolsettings->gp_sculpt.lock_axis, GP_LOCKAXIS_VIEW, GP_LOCKAXIS_CURSOR)) {
85 /* Use the transpose inverse for normal. */
86 placement_normal_ = math::transform_direction(math::transpose(world_space_to_layer_space_),
87 placement_normal_);
88 }
89
90 /* Initialize DrawingPlacementDepth from toolsettings. */
91 const char align_flag = scene.toolsettings->gpencil_v3d_align;
92 if (align_flag & GP_PROJECT_VIEWSPACE) {
93 if (align_flag & GP_PROJECT_CURSOR) {
95 surface_offset_ = 0.0f;
96 placement_loc_ = float3(scene.cursor.location);
97 }
98 else if (align_flag & GP_PROJECT_DEPTH_VIEW) {
100 if (align_flag & GP_PROJECT_DEPTH_ONLY_SELECTED) {
101 use_project_only_selected_ = true;
102 }
103 surface_offset_ = scene.toolsettings->gpencil_surface_offset;
104 /* Default to view placement with the object origin if we don't hit a surface. */
105 placement_loc_ = layer_space_to_world_space_.location();
106 }
107 else if (align_flag & GP_PROJECT_DEPTH_STROKE) {
109 surface_offset_ = 0.0f;
110 /* Default to view placement with the object origin if we don't hit a stroke. */
111 placement_loc_ = layer_space_to_world_space_.location();
112 }
113 else {
115 surface_offset_ = 0.0f;
116 placement_loc_ = layer_space_to_world_space_.location();
117 }
118 }
119 else {
121 surface_offset_ = 0.0f;
122 placement_loc_ = float3(0.0f);
123 }
124
125 if (plane_ != DrawingPlacementPlane::View) {
126 placement_plane_ = float4();
127 plane_from_point_normal_v3(*placement_plane_, placement_loc_, placement_normal_);
128 }
129}
130
132 const ARegion &region,
133 const View3D &view3d,
134 const Object &eval_object,
135 const bke::greasepencil::Layer *layer,
136 const ReprojectMode reproject_mode,
137 const float surface_offset,
138 ViewDepths *view_depths)
139 : region_(&region),
140 view3d_(&view3d),
141 depth_cache_(view_depths),
142 surface_offset_(surface_offset)
143{
144 layer_space_to_world_space_ = (layer != nullptr) ? layer->to_world_space(eval_object) :
145 eval_object.object_to_world();
146 world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
147 /* Initialize DrawingPlacementPlane from mode. */
148 switch (reproject_mode) {
151 break;
154 placement_normal_ = float3(0, 1, 0);
155 break;
158 placement_normal_ = float3(1, 0, 0);
159 break;
162 placement_normal_ = float3(0, 0, 1);
163 break;
166 placement_normal_ = scene.cursor.matrix<float3x3>() * float3(0, 0, 1);
167 break;
168 }
169 default:
170 break;
171 }
172
173 /* Account for layer transform. */
174 if (!ELEM(reproject_mode, ReprojectMode::View, ReprojectMode::Cursor)) {
175 /* Use the transpose inverse for normal. */
176 placement_normal_ = math::transform_direction(math::transpose(world_space_to_layer_space_),
177 placement_normal_);
178 }
179
180 /* Initialize DrawingPlacementDepth from mode. */
181 switch (reproject_mode) {
184 surface_offset_ = 0.0f;
185 placement_loc_ = float3(scene.cursor.location);
186 break;
189 surface_offset_ = 0.0f;
190 placement_loc_ = layer_space_to_world_space_.location();
191 break;
194 placement_loc_ = layer_space_to_world_space_.location();
195 break;
196 default:
198 surface_offset_ = 0.0f;
199 placement_loc_ = layer_space_to_world_space_.location();
200 break;
201 }
202
203 if (plane_ != DrawingPlacementPlane::View) {
204 placement_plane_ = float4();
205 plane_from_point_normal_v3(*placement_plane_, placement_loc_, placement_normal_);
206 }
207}
208
210{
211 region_ = other.region_;
212 view3d_ = other.view3d_;
213
214 depth_ = other.depth_;
215 plane_ = other.plane_;
216
217 if (other.depth_cache_ != nullptr) {
218 depth_cache_ = static_cast<ViewDepths *>(MEM_dupallocN(other.depth_cache_));
219 depth_cache_->depths = static_cast<float *>(MEM_dupallocN(other.depth_cache_->depths));
220 }
221 use_project_only_selected_ = other.use_project_only_selected_;
222
223 surface_offset_ = other.surface_offset_;
224
225 placement_loc_ = other.placement_loc_;
226 placement_normal_ = other.placement_normal_;
227 placement_plane_ = other.placement_plane_;
228
229 layer_space_to_world_space_ = other.layer_space_to_world_space_;
230 world_space_to_layer_space_ = other.world_space_to_layer_space_;
231}
232
234{
235 region_ = other.region_;
236 view3d_ = other.view3d_;
237
238 depth_ = other.depth_;
239 plane_ = other.plane_;
240
241 std::swap(depth_cache_, other.depth_cache_);
242 use_project_only_selected_ = other.use_project_only_selected_;
243
244 surface_offset_ = other.surface_offset_;
245
246 placement_loc_ = other.placement_loc_;
247 placement_normal_ = other.placement_normal_;
248 placement_plane_ = other.placement_plane_;
249
250 layer_space_to_world_space_ = other.layer_space_to_world_space_;
251 world_space_to_layer_space_ = other.world_space_to_layer_space_;
252}
253
255{
256 if (this == &other) {
257 return *this;
258 }
259 std::destroy_at(this);
260 new (this) DrawingPlacement(other);
261 return *this;
262}
263
265{
266 if (this == &other) {
267 return *this;
268 }
269 std::destroy_at(this);
270 new (this) DrawingPlacement(std::move(other));
271 return *this;
272}
273
275{
276 if (depth_cache_ != nullptr) {
277 ED_view3d_depths_free(depth_cache_);
278 }
279}
280
285
290
292{
293 const short previous_gp_flag = view3d->gp_flag;
295
297 if (use_project_only_selected_) {
299 }
300 else {
302 }
303 }
304 if (use_project_to_stroke()) {
305 /* Enforce render engine to use 3D stroke order, otherwise depth buffer values are not in 3D
306 * space. */
308 }
309
310 ED_view3d_depth_override(depsgraph, region, view3d, nullptr, mode, false, &this->depth_cache_);
311
312 view3d->gp_flag = previous_gp_flag;
313}
314
315std::optional<float3> DrawingPlacement::project_depth(const float2 co) const
316{
317 std::optional<float> depth = get_depth(co);
318 if (!depth) {
319 return std::nullopt;
320 }
321
322 float3 proj_point;
323 if (ED_view3d_depth_unproject_v3(region_, int2(co), *depth, proj_point)) {
324 float3 view_normal;
325 ED_view3d_win_to_vector(region_, co, view_normal);
326 proj_point -= view_normal * surface_offset_;
327 return proj_point;
328 }
329 return std::nullopt;
330}
331
332std::optional<float> DrawingPlacement::get_depth(float2 co) const
333{
334 float depth;
335 if (depth_cache_ != nullptr && ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth)) {
336 return depth;
337 }
338 return std::nullopt;
339}
340
341float3 DrawingPlacement::try_project_depth(const float2 co) const
342{
343 if (std::optional<float3> proj_point = this->project_depth(co)) {
344 return *proj_point;
345 }
346
347 float3 proj_point;
348 /* Fall back to `View` placement. */
349 ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
350 return proj_point;
351}
352
353float3 DrawingPlacement::project(const float2 co, bool &r_clipped) const
354{
355 float3 proj_point;
356 if (depth_ == DrawingPlacementDepth::Surface) {
357 /* Project using the viewport depth cache. */
358 proj_point = this->try_project_depth(co);
359 r_clipped = false;
360 }
361 else {
362 if (placement_plane_) {
363 r_clipped = !ED_view3d_win_to_3d_on_plane(region_, *placement_plane_, co, true, proj_point);
364 }
365 else {
366 ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
367 r_clipped = false;
368 }
369 }
370 return math::transform_point(world_space_to_layer_space_, proj_point);
371}
373{
374 [[maybe_unused]] bool clipped_unused;
375 return this->project(co, clipped_unused);
376}
377
379{
380 float3 proj_point;
381 if (depth_ == DrawingPlacementDepth::Surface) {
382 /* Project using the viewport depth cache. */
383 proj_point = this->try_project_depth(co);
384 }
385 else {
386 if (placement_plane_) {
387 ED_view3d_win_to_3d_on_plane(region_, *placement_plane_, co, false, proj_point);
388 }
389 else {
390 ED_view3d_win_to_3d_with_shift(view3d_, region_, placement_loc_, co, proj_point);
391 }
392 }
393 return math::transform_point(world_space_to_layer_space_, proj_point);
394}
395
397{
398 threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
399 for (const int i : range) {
400 dst[i] = this->project(src[i]);
401 }
402 });
403}
404
405float3 DrawingPlacement::place(const float2 co, const float depth) const
406{
407 float3 loc;
408 ED_view3d_unproject_v3(region_, co.x, co.y, depth, loc);
409 return math::transform_point(world_space_to_layer_space_, loc);
410}
411
413{
414 const float3 world_pos = math::transform_point(layer_space_to_world_space_, pos);
415 float3 proj_point;
416 if (depth_ == DrawingPlacementDepth::Surface) {
417 /* First project the position into view space. */
418 float2 co;
419 if (ED_view3d_project_float_global(region_, world_pos, co, V3D_PROJ_TEST_NOP)) {
420 /* Can't reproject the point. */
421 return pos;
422 }
423 /* Project using the viewport depth cache. */
424 proj_point = this->try_project_depth(co);
425 }
426 else {
427 /* Reproject the point onto the `placement_plane_` from the current view. */
428 RegionView3D *rv3d = static_cast<RegionView3D *>(region_->regiondata);
429
430 float3 ray_no;
431 if (rv3d->is_persp) {
432 ray_no = math::normalize(world_pos - float3(rv3d->viewinv[3]));
433 }
434 else {
435 ray_no = -float3(rv3d->viewinv[2]);
436 }
437 float4 plane;
438 if (placement_plane_) {
439 plane = *placement_plane_;
440 }
441 else {
442 plane_from_point_normal_v3(plane, placement_loc_, rv3d->viewinv[2]);
443 }
444
445 float lambda;
446 if (isect_ray_plane_v3(world_pos, ray_no, plane, &lambda, false)) {
447 proj_point = world_pos + ray_no * lambda;
448 }
449 else {
450 return pos;
451 }
452 }
453 return math::transform_point(world_space_to_layer_space_, proj_point);
454}
455
457{
458 threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
459 for (const int i : range) {
460 dst[i] = this->reproject(src[i]);
461 }
462 });
463}
464
466{
467 return layer_space_to_world_space_;
468}
469
470static float get_frame_falloff(const bool use_multi_frame_falloff,
471 const int frame_number,
472 const int active_frame,
473 const std::optional<Bounds<int>> frame_bounds,
474 const CurveMapping *falloff_curve)
475{
476 if (!use_multi_frame_falloff || !frame_bounds.has_value() || falloff_curve == nullptr) {
477 return 1.0f;
478 }
479
480 const int min_frame = frame_bounds->min;
481 const int max_frame = frame_bounds->max;
482
483 /* Frame right of the center frame. */
484 if (frame_number < active_frame) {
485 const float frame_factor = 0.5f * float(frame_number - min_frame) / (active_frame - min_frame);
486 return BKE_curvemapping_evaluateF(falloff_curve, 0, frame_factor);
487 }
488 /* Frame left of the center frame. */
489 if (frame_number > active_frame) {
490 const float frame_factor = 0.5f * float(frame_number - active_frame) /
491 (max_frame - active_frame);
492 return BKE_curvemapping_evaluateF(falloff_curve, 0, frame_factor + 0.5f);
493 }
494 /* Frame at center. */
495 return BKE_curvemapping_evaluateF(falloff_curve, 0, 0.5f);
496}
497
498static std::optional<Bounds<int>> get_selected_frame_number_bounds(
499 const bke::greasepencil::Layer &layer)
500{
501 using namespace blender::bke::greasepencil;
502 if (!layer.is_editable()) {
503 return {};
504 }
505 Vector<int> frame_numbers;
506 for (const auto [frame_number, frame] : layer.frames().items()) {
507 if (frame.is_selected()) {
508 frame_numbers.append(frame_number);
509 }
510 }
511 return bounds::min_max<int>(frame_numbers);
512}
513
515 const std::optional<Bounds<int>> frame_bounds,
516 const int current_frame)
517{
518 std::optional<int> current_start_frame = layer.start_frame_at(current_frame);
519 if (!current_start_frame && frame_bounds) {
520 return math::clamp(current_frame, frame_bounds->min, frame_bounds->max);
521 }
522 return *current_start_frame;
523}
524
525static std::optional<int> get_frame_id(const bke::greasepencil::Layer &layer,
526 const GreasePencilFrame &frame,
527 const int frame_number,
528 const int frame_index,
529 const int current_frame,
530 const int current_frame_index,
531 const int last_frame,
532 const int last_frame_index,
533 const bool use_multi_frame_editing,
534 const bool do_onion_skinning,
535 const bool is_before_first,
536 const GreasePencilOnionSkinningSettings onion_settings)
537{
538 if (use_multi_frame_editing) {
539 if (frame.is_selected()) {
540 if (do_onion_skinning) {
541 return (frame_number < current_frame) ? -1 : 1;
542 }
543 return 0;
544 }
545 return {};
546 }
547 if (do_onion_skinning && layer.use_onion_skinning()) {
548 /* Keyframe type filter. */
549 if (onion_settings.filter != 0 && (onion_settings.filter & (1 << frame.type)) == 0) {
550 return {};
551 }
552 /* Selected mode filter. */
553 if (onion_settings.mode == GP_ONION_SKINNING_MODE_SELECTED && !frame.is_selected()) {
554 return {};
555 }
556
557 int delta = 0;
558 if (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) {
559 delta = frame_number - current_frame;
560 }
561 else {
562 delta = frame_index - current_frame_index;
563 }
564
565 if (is_before_first) {
566 delta++;
567 }
568 if ((onion_settings.flag & GP_ONION_SKINNING_SHOW_LOOP) != 0 &&
569 (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after))
570 {
571 /* We wrap the value using the last frame and 0 as reference. */
572 /* FIXME: This might not be good for animations not starting at 0. */
573 int shift = 0;
574 if (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) {
575 shift = last_frame;
576 }
577 else {
578 shift = last_frame_index;
579 }
580 delta += (delta < 0) ? (shift + 1) : -(shift + 1);
581 }
582 /* Frame range filter. */
583 if (ELEM(onion_settings.mode,
586 (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after))
587 {
588 return {};
589 }
590
591 return delta;
592 }
593 return {};
594}
595
597 const GreasePencil &grease_pencil,
598 const bke::greasepencil::Layer &layer,
599 const int current_frame,
600 const bool use_multi_frame_editing,
601 const bool do_onion_skinning)
602{
603 GreasePencilOnionSkinningSettings onion_settings = grease_pencil.onion_skinning_settings;
604 Vector<std::pair<int, int>> frame_numbers;
605 const Span<int> sorted_keys = layer.sorted_keys();
606 if (sorted_keys.is_empty()) {
607 return {};
608 }
609 const int current_frame_index = std::max(layer.sorted_keys_index_at(current_frame), 0);
610 const int last_frame = sorted_keys.last();
611 const int last_frame_index = sorted_keys.index_range().last();
612 const bool is_before_first = (current_frame < sorted_keys.first());
613 const std::optional<int> current_start_frame = layer.start_frame_at(current_frame);
614 for (const int frame_i : sorted_keys.index_range()) {
615 const int frame_number = sorted_keys[frame_i];
616 if (current_start_frame && *current_start_frame == frame_number) {
617 continue;
618 }
619 const GreasePencilFrame &frame = layer.frames().lookup(frame_number);
620 const std::optional<int> frame_id = get_frame_id(layer,
621 frame,
622 frame_number,
623 frame_i,
624 current_frame,
625 current_frame_index,
626 last_frame,
627 last_frame_index,
628 use_multi_frame_editing,
629 do_onion_skinning,
630 is_before_first,
631 onion_settings);
632 if (!frame_id.has_value()) {
633 /* Drawing on this frame is not visible. */
634 continue;
635 }
636
637 frame_numbers.append({frame_number, *frame_id});
638 }
639
640 frame_numbers.append({current_frame, 0});
641
642 return frame_numbers.as_span();
643}
644
646 const bke::greasepencil::Layer &layer,
647 const int current_frame,
648 const bool use_multi_frame_editing)
649{
650 using namespace blender::bke::greasepencil;
651 Vector<int> frame_numbers;
652 Set<const Drawing *> added_drawings;
653 if (use_multi_frame_editing) {
654 const Drawing *current_drawing = grease_pencil.get_drawing_at(layer, current_frame);
655 for (const auto [frame_number, frame] : layer.frames().items()) {
656 if (!frame.is_selected()) {
657 continue;
658 }
659 frame_numbers.append(frame_number);
660 added_drawings.add(grease_pencil.get_drawing_at(layer, frame_number));
661 }
662 if (added_drawings.contains(current_drawing)) {
663 return frame_numbers.as_span();
664 }
665 }
666
667 frame_numbers.append(current_frame);
668 return frame_numbers.as_span();
669}
670
672 GreasePencil &grease_pencil)
673{
674 using namespace blender::bke::greasepencil;
675 const int current_frame = scene.r.cfra;
676 const ToolSettings *toolsettings = scene.toolsettings;
677 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
679
680 Vector<MutableDrawingInfo> editable_drawings;
681 Span<const Layer *> layers = grease_pencil.layers();
682 for (const int layer_i : layers.index_range()) {
683 const Layer &layer = *layers[layer_i];
684 if (!layer.is_editable()) {
685 continue;
686 }
687 const Array<int> frame_numbers = get_editable_frames_for_layer(
688 grease_pencil, layer, current_frame, use_multi_frame_editing);
689 for (const int frame_number : frame_numbers) {
690 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
691 editable_drawings.append({*drawing, layer_i, frame_number, 1.0f});
692 }
693 }
694 }
695
696 return editable_drawings;
697}
698
700 GreasePencil &grease_pencil)
701{
702 using namespace blender::bke::greasepencil;
703 const int current_frame = scene.r.cfra;
704 const ToolSettings *toolsettings = scene.toolsettings;
705 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
707 const bool use_multi_frame_falloff = use_multi_frame_editing &&
708 (toolsettings->gp_sculpt.flag &
710 if (use_multi_frame_falloff) {
712 }
713
714 Vector<MutableDrawingInfo> editable_drawings;
715 Span<const Layer *> layers = grease_pencil.layers();
716 for (const int layer_i : layers.index_range()) {
717 const Layer &layer = *layers[layer_i];
718 if (!layer.is_editable()) {
719 continue;
720 }
721 const std::optional<Bounds<int>> frame_bounds = get_selected_frame_number_bounds(layer);
722 const int active_frame = get_active_frame_for_falloff(layer, frame_bounds, current_frame);
723 const Array<int> frame_numbers = get_editable_frames_for_layer(
724 grease_pencil, layer, current_frame, use_multi_frame_editing);
725 for (const int frame_number : frame_numbers) {
726 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
727 const float falloff = get_frame_falloff(use_multi_frame_falloff,
728 frame_number,
729 active_frame,
730 frame_bounds,
731 toolsettings->gp_sculpt.cur_falloff);
732 editable_drawings.append({*drawing, layer_i, frame_number, falloff});
733 }
734 }
735 }
736
737 return editable_drawings;
738}
739
741 const Scene &scene, GreasePencil &grease_pencil)
742{
743 using namespace blender::bke::greasepencil;
744 int current_frame = scene.r.cfra;
745 const ToolSettings *toolsettings = scene.toolsettings;
746 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
748 const bool use_multi_frame_falloff = use_multi_frame_editing &&
749 (toolsettings->gp_sculpt.flag &
751 if (use_multi_frame_falloff) {
753 }
754
755 /* Get a set of unique frame numbers with editable drawings on them. */
756 VectorSet<int> selected_frames;
757 Span<const Layer *> layers = grease_pencil.layers();
758 if (use_multi_frame_editing) {
759 for (const int layer_i : layers.index_range()) {
760 const Layer &layer = *layers[layer_i];
761 if (!layer.is_editable()) {
762 continue;
763 }
764 for (const auto [frame_number, frame] : layer.frames().items()) {
765 if (frame_number != current_frame && frame.is_selected()) {
766 selected_frames.add(frame_number);
767 }
768 }
769 }
770 }
771 selected_frames.add(current_frame);
772
773 /* Get drawings grouped per frame. */
774 Array<Vector<MutableDrawingInfo>> drawings_grouped_per_frame(selected_frames.size());
775 Set<const Drawing *> added_drawings;
776 for (const int layer_i : layers.index_range()) {
777 const Layer &layer = *layers[layer_i];
778 if (!layer.is_editable()) {
779 continue;
780 }
781 const std::optional<Bounds<int>> frame_bounds = get_selected_frame_number_bounds(layer);
782 const int active_frame = get_active_frame_for_falloff(layer, frame_bounds, current_frame);
783
784 /* In multi frame editing mode, add drawings at selected frames. */
785 if (use_multi_frame_editing) {
786 for (const auto [frame_number, frame] : layer.frames().items()) {
787 Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number);
788 if (!frame.is_selected() || drawing == nullptr || added_drawings.contains(drawing)) {
789 continue;
790 }
791 const float falloff = get_frame_falloff(use_multi_frame_falloff,
792 frame_number,
793 active_frame,
794 frame_bounds,
795 toolsettings->gp_sculpt.cur_falloff);
796 const int frame_group = selected_frames.index_of(frame_number);
797 drawings_grouped_per_frame[frame_group].append({*drawing, layer_i, frame_number, falloff});
798 added_drawings.add_new(drawing);
799 }
800 }
801
802 /* Add drawing at current frame. */
803 Drawing *current_drawing = grease_pencil.get_drawing_at(layer, current_frame);
804 if (current_drawing != nullptr && !added_drawings.contains(current_drawing)) {
805 const float falloff = get_frame_falloff(use_multi_frame_falloff,
806 current_frame,
807 active_frame,
808 frame_bounds,
809 toolsettings->gp_sculpt.cur_falloff);
810 const int frame_group = selected_frames.index_of(current_frame);
811 drawings_grouped_per_frame[frame_group].append(
812 {*current_drawing, layer_i, current_frame, falloff});
813 added_drawings.add_new(current_drawing);
814 }
815 }
816
817 return drawings_grouped_per_frame;
818}
819
821 const Scene &scene,
822 GreasePencil &grease_pencil,
824{
825 using namespace blender::bke::greasepencil;
826 const int current_frame = scene.r.cfra;
827 const ToolSettings *toolsettings = scene.toolsettings;
828 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
830 const int layer_index = *grease_pencil.get_layer_index(layer);
831
832 Vector<MutableDrawingInfo> editable_drawings;
833 const Array<int> frame_numbers = get_editable_frames_for_layer(
834 grease_pencil, layer, current_frame, use_multi_frame_editing);
835 for (const int frame_number : frame_numbers) {
836 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
837 editable_drawings.append({*drawing, layer_index, frame_number, 1.0f});
838 }
839 }
840
841 return editable_drawings;
842}
843
845 const Scene &scene,
846 GreasePencil &grease_pencil,
848{
849 using namespace blender::bke::greasepencil;
850 const int current_frame = scene.r.cfra;
851 const ToolSettings *toolsettings = scene.toolsettings;
852 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
854 const bool use_multi_frame_falloff = use_multi_frame_editing &&
855 (toolsettings->gp_sculpt.flag &
857 const int layer_index = *grease_pencil.get_layer_index(layer);
858 std::optional<Bounds<int>> frame_bounds;
859 if (use_multi_frame_falloff) {
861 frame_bounds = get_selected_frame_number_bounds(layer);
862 }
863
864 const int active_frame = get_active_frame_for_falloff(layer, frame_bounds, current_frame);
865
866 Vector<MutableDrawingInfo> editable_drawings;
867 const Array<int> frame_numbers = get_editable_frames_for_layer(
868 grease_pencil, layer, current_frame, use_multi_frame_editing);
869 for (const int frame_number : frame_numbers) {
870 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
871 const float falloff = get_frame_falloff(use_multi_frame_falloff,
872 frame_number,
873 active_frame,
874 frame_bounds,
875 toolsettings->gp_sculpt.cur_falloff);
876 editable_drawings.append({*drawing, layer_index, frame_number, falloff});
877 }
878 }
879
880 return editable_drawings;
881}
882
884 const GreasePencil &grease_pencil,
885 const bool do_onion_skinning)
886{
887 using namespace blender::bke::greasepencil;
888 const int current_frame = BKE_scene_ctime_get(&scene);
889 const ToolSettings *toolsettings = scene.toolsettings;
890 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
892
893 Vector<DrawingInfo> visible_drawings;
894 Span<const Layer *> layers = grease_pencil.layers();
895 for (const int layer_i : layers.index_range()) {
896 const Layer &layer = *layers[layer_i];
897 if (!layer.is_visible()) {
898 continue;
899 }
901 grease_pencil, layer, current_frame, use_multi_frame_editing, do_onion_skinning);
902 for (const auto &[frame_number, onion_id] : frames) {
903 if (const Drawing *drawing = grease_pencil.get_drawing_at(layer, frame_number)) {
904 visible_drawings.append({*drawing, layer_i, frame_number, onion_id});
905 }
906 }
907 }
908
909 return visible_drawings;
910}
911
913{
914 BLI_assert(object.type == OB_GREASE_PENCIL);
915 VectorSet<int> locked_material_indices;
916 for (const int mat_i : IndexRange(object.totcol)) {
917 Material *material = BKE_object_material_get(&object, mat_i + 1);
918 /* The editable materials are unlocked and not hidden. */
919 if (material != nullptr && material->gp_style != nullptr &&
920 ((material->gp_style->flag & GP_MATERIAL_LOCKED) != 0 ||
921 (material->gp_style->flag & GP_MATERIAL_HIDE) != 0))
922 {
923 locked_material_indices.add_new(mat_i);
924 }
925 }
926 return locked_material_indices;
927}
928
930{
931 BLI_assert(object.type == OB_GREASE_PENCIL);
932 VectorSet<int> hidden_material_indices;
933 for (const int mat_i : IndexRange(object.totcol)) {
934 Material *material = BKE_object_material_get(&object, mat_i + 1);
935 if (material != nullptr && material->gp_style != nullptr &&
936 (material->gp_style->flag & GP_MATERIAL_HIDE) != 0)
937 {
938 hidden_material_indices.add_new(mat_i);
939 }
940 }
941 return hidden_material_indices;
942}
943
945{
946 BLI_assert(object.type == OB_GREASE_PENCIL);
947 VectorSet<int> fill_material_indices;
948 for (const int mat_i : IndexRange(object.totcol)) {
949 Material *material = BKE_object_material_get(&object, mat_i + 1);
950 if (material != nullptr && material->gp_style != nullptr &&
951 (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0)
952 {
953 fill_material_indices.add_new(mat_i);
954 }
955 }
956 return fill_material_indices;
957}
958
960 const bke::greasepencil::Drawing &drawing,
961 int layer_index,
962 IndexMaskMemory &memory)
963{
964 using namespace blender;
965 const bke::CurvesGeometry &curves = drawing.strokes();
966 const IndexRange curves_range = curves.curves_range();
967
968 if (object.totcol == 0) {
969 return IndexMask(curves_range);
970 }
971
972 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
973 const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
974
975 /* If we're not using material locking, the entire curves range is editable. */
976 if (layer.ignore_locked_materials()) {
977 return IndexMask(curves_range);
978 }
979
980 /* Get all the editable material indices */
981 VectorSet<int> locked_material_indices = get_locked_material_indices(object);
982 if (locked_material_indices.is_empty()) {
983 return curves_range;
984 }
985
986 const bke::AttributeAccessor attributes = curves.attributes();
987 const VArray<int> materials = *attributes.lookup_or_default<int>(
988 "material_index", bke::AttrDomain::Curve, 0);
989 if (!materials) {
990 /* If the attribute does not exist then the default is the first material. */
991 if (locked_material_indices.contains(0)) {
992 return {};
993 }
994 return curves_range;
995 }
996 /* Get all the strokes that have their material unlocked. */
998 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
999 return !locked_material_indices.contains(materials[curve_i]);
1000 });
1001}
1002
1004 const bke::greasepencil::Drawing &drawing,
1005 int layer_index,
1006 IndexMaskMemory &memory)
1007{
1008 using namespace blender;
1009 const IndexMask editable_strokes = retrieve_editable_strokes(
1010 object, drawing, layer_index, memory);
1011 if (editable_strokes.is_empty()) {
1012 return {};
1013 }
1014
1015 const bke::CurvesGeometry &curves = drawing.strokes();
1016 const IndexRange curves_range = curves.curves_range();
1017
1018 const bke::AttributeAccessor attributes = curves.attributes();
1019 const VArray<int> materials = *attributes.lookup_or_default<int>(
1020 "material_index", bke::AttrDomain::Curve, 0);
1021 const VectorSet<int> fill_material_indices = get_fill_material_indices(object);
1022 if (!materials) {
1023 /* If the attribute does not exist then the default is the first material. */
1024 if (editable_strokes.contains(0) && fill_material_indices.contains(0)) {
1025 return curves_range;
1026 }
1027 return {};
1028 }
1030 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1031 const int material_index = materials[curve_i];
1032 return fill_material_indices.contains(material_index);
1033 });
1034 return IndexMask::from_intersection(editable_strokes, fill_strokes, memory);
1035}
1036
1038 const bke::greasepencil::Drawing &drawing,
1039 const int mat_i,
1040 IndexMaskMemory &memory)
1041{
1042 using namespace blender;
1043
1044 const bke::CurvesGeometry &curves = drawing.strokes();
1045 const IndexRange curves_range = curves.curves_range();
1046
1047 /* Get all the editable material indices */
1048 VectorSet<int> locked_material_indices = get_locked_material_indices(object);
1049
1050 const bke::AttributeAccessor attributes = curves.attributes();
1051
1052 const VArray<int> materials = *attributes.lookup_or_default<int>(
1053 "material_index", bke::AttrDomain::Curve, 0);
1054 if (!materials) {
1055 /* If the attribute does not exist then the default is the first material. */
1056 if (locked_material_indices.contains(0)) {
1057 return {};
1058 }
1059 return curves_range;
1060 }
1061 /* Get all the strokes that share the same material and have it unlocked. */
1063 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1064 const int material_index = materials[curve_i];
1065 if (material_index == mat_i) {
1066 return !locked_material_indices.contains(material_index);
1067 }
1068 return false;
1069 });
1070}
1071
1073 const bke::greasepencil::Drawing &drawing,
1074 int layer_index,
1075 IndexMaskMemory &memory)
1076{
1077 const bke::CurvesGeometry &curves = drawing.strokes();
1078 const IndexRange points_range = curves.points_range();
1079
1080 if (object.totcol == 0) {
1081 return IndexMask(points_range);
1082 }
1083
1084 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1085 const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
1086
1087 /* If we're not using material locking, the entire points range is editable. */
1088 if (layer.ignore_locked_materials()) {
1089 return IndexMask(points_range);
1090 }
1091
1092 /* Get all the editable material indices */
1093 VectorSet<int> locked_material_indices = get_locked_material_indices(object);
1094 if (locked_material_indices.is_empty()) {
1095 return points_range;
1096 }
1097
1098 /* Propagate the material index to the points. */
1099 const bke::AttributeAccessor attributes = curves.attributes();
1100 const VArray<int> materials = *attributes.lookup_or_default<int>(
1101 "material_index", bke::AttrDomain::Point, 0);
1102 if (!materials) {
1103 /* If the attribute does not exist then the default is the first material. */
1104 if (locked_material_indices.contains(0)) {
1105 return {};
1106 }
1107 return points_range;
1108 }
1109 /* Get all the points that are part of a stroke with an unlocked material. */
1111 points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
1112 return !locked_material_indices.contains(materials[point_i]);
1113 });
1114}
1115
1117 const MutableDrawingInfo &info,
1118 const bke::AttrDomain selection_domain,
1119 IndexMaskMemory &memory)
1120{
1121
1122 const bke::greasepencil::Drawing &drawing = info.drawing;
1123 if (selection_domain == bke::AttrDomain::Curve) {
1124 return ed::greasepencil::retrieve_editable_strokes(object, drawing, info.layer_index, memory);
1125 }
1126 if (selection_domain == bke::AttrDomain::Point) {
1127 return ed::greasepencil::retrieve_editable_points(object, drawing, info.layer_index, memory);
1128 }
1129 return {};
1130}
1131
1133 const bke::greasepencil::Drawing &drawing,
1134 IndexMaskMemory &memory)
1135{
1136 using namespace blender;
1137
1138 /* Get all the hidden material indices. */
1139 VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
1140
1141 if (hidden_material_indices.is_empty()) {
1142 return drawing.strokes().curves_range();
1143 }
1144
1145 const bke::CurvesGeometry &curves = drawing.strokes();
1146 const IndexRange curves_range = drawing.strokes().curves_range();
1147 const bke::AttributeAccessor attributes = curves.attributes();
1148
1149 /* Get all the strokes that have their material visible. */
1150 const VArray<int> materials = *attributes.lookup_or_default<int>(
1151 "material_index", bke::AttrDomain::Curve, 0);
1153 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1154 const int material_index = materials[curve_i];
1155 return !hidden_material_indices.contains(material_index);
1156 });
1157}
1158
1160 const bke::greasepencil::Drawing &drawing,
1161 IndexMaskMemory &memory)
1162{
1163 /* Get all the hidden material indices. */
1164 VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
1165
1166 if (hidden_material_indices.is_empty()) {
1167 return drawing.strokes().points_range();
1168 }
1169
1170 const bke::CurvesGeometry &curves = drawing.strokes();
1171 const IndexRange points_range = curves.points_range();
1172 const bke::AttributeAccessor attributes = curves.attributes();
1173
1174 /* Propagate the material index to the points. */
1175 const VArray<int> materials = *attributes.lookup_or_default<int>(
1176 "material_index", bke::AttrDomain::Point, 0);
1177 if (const std::optional<int> single_material = materials.get_if_single()) {
1178 if (!hidden_material_indices.contains(*single_material)) {
1179 return points_range;
1180 }
1181 return {};
1182 }
1183
1184 /* Get all the points that are part of a stroke with a visible material. */
1186 points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
1187 const int material_index = materials[point_i];
1188 return !hidden_material_indices.contains(material_index);
1189 });
1190}
1191
1193 const bke::greasepencil::Drawing &drawing,
1194 IndexMaskMemory &memory)
1195{
1196 const bke::CurvesGeometry &curves = drawing.strokes();
1197
1198 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
1199 return IndexMask(0);
1200 }
1201
1202 const IndexRange curves_range = curves.curves_range();
1203 const VArray<int8_t> curve_types = curves.curve_types();
1204 const std::array<int, CURVE_TYPES_NUM> type_counts = curves.curve_type_counts();
1205
1206 const IndexMask bezier_strokes = bke::curves::indices_for_type(
1207 curve_types, type_counts, CURVE_TYPE_BEZIER, curves_range, memory);
1208
1210 object, drawing, memory);
1211
1212 return IndexMask::from_intersection(visible_strokes, bezier_strokes, memory);
1213}
1214
1216 const bke::greasepencil::Drawing &drawing,
1217 IndexMaskMemory &memory)
1218{
1219 const bke::CurvesGeometry &curves = drawing.strokes();
1220
1221 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
1222 return IndexMask(0);
1223 }
1224
1225 const IndexMask visible_bezier_strokes = retrieve_visible_bezier_strokes(
1226 object, drawing, memory);
1227
1228 return IndexMask::from_ranges(curves.points_by_curve(), visible_bezier_strokes, memory);
1229}
1230
1232 const bke::greasepencil::Drawing &drawing,
1233 const int handle_display,
1234 IndexMaskMemory &memory)
1235{
1236 if (handle_display == CURVE_HANDLE_NONE) {
1237 return IndexMask(0);
1238 }
1239
1240 const bke::CurvesGeometry &curves = drawing.strokes();
1241
1242 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
1243 return IndexMask(0);
1244 }
1245
1246 const IndexMask visible_bezier_strokes = retrieve_visible_bezier_strokes(
1247 object, drawing, memory);
1248
1249 if (handle_display == CURVE_HANDLE_ALL) {
1250 return visible_bezier_strokes;
1251 }
1252
1253 /* handle_display == CURVE_HANDLE_SELECTED */
1254 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1255 return IndexMask::from_intersection(visible_bezier_strokes, selected_strokes, memory);
1256}
1257
1259 const bke::greasepencil::Drawing &drawing,
1260 const int layer_index,
1261 const int handle_display,
1262 IndexMaskMemory &memory)
1263{
1264 if (handle_display == CURVE_HANDLE_NONE) {
1265 return IndexMask(0);
1266 }
1267 else if (handle_display == CURVE_HANDLE_ALL) {
1268 return retrieve_visible_bezier_points(object, drawing, memory);
1269 }
1270 /* else handle_display == CURVE_HANDLE_SELECTED */
1271
1272 const bke::CurvesGeometry &curves = drawing.strokes();
1273
1274 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
1275 return IndexMask(0);
1276 }
1277
1278 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1279 const VArray<int8_t> types = curves.curve_types();
1280
1281 const VArray<bool> selected_point = *curves.attributes().lookup_or_default<bool>(
1282 ".selection", bke::AttrDomain::Point, true);
1283 const VArray<bool> selected_left = *curves.attributes().lookup_or_default<bool>(
1284 ".selection_handle_left", bke::AttrDomain::Point, true);
1285 const VArray<bool> selected_right = *curves.attributes().lookup_or_default<bool>(
1286 ".selection_handle_right", bke::AttrDomain::Point, true);
1287
1289 object, drawing, layer_index, memory);
1290
1291 const IndexMask selected_points = IndexMask::from_predicate(
1292 curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
1293 const bool is_selected = selected_point[point_i] || selected_left[point_i] ||
1294 selected_right[point_i];
1295 const bool is_bezier = types[point_to_curve_map[point_i]] == CURVE_TYPE_BEZIER;
1296 return is_selected && is_bezier;
1297 });
1298
1299 return IndexMask::from_intersection(editable_points, selected_points, memory);
1300}
1301
1303 const bke::greasepencil::Drawing &drawing,
1304 const int layer_index,
1305 const bke::AttrDomain selection_domain,
1306 const int handle_display,
1307 IndexMaskMemory &memory)
1308{
1309 if (selection_domain == bke::AttrDomain::Curve) {
1311 object, drawing, handle_display, memory);
1312 }
1313 if (selection_domain == bke::AttrDomain::Point) {
1315 object, drawing, layer_index, handle_display, memory);
1316 }
1317 return {};
1318}
1319
1321 const bke::greasepencil::Drawing &drawing,
1322 int layer_index,
1323 IndexMaskMemory &memory)
1324{
1325 using namespace blender;
1326 const bke::CurvesGeometry &curves = drawing.strokes();
1327
1328 const IndexMask editable_strokes = retrieve_editable_strokes(
1329 object, drawing, layer_index, memory);
1330 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1331
1332 return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
1333}
1334
1336 const bke::greasepencil::Drawing &drawing,
1337 int layer_index,
1338 IndexMaskMemory &memory)
1339{
1340 using namespace blender;
1341 const bke::CurvesGeometry &curves = drawing.strokes();
1342
1343 const IndexMask editable_strokes = retrieve_editable_fill_strokes(
1344 object, drawing, layer_index, memory);
1345 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1346
1347 return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
1348}
1349
1351 const bke::greasepencil::Drawing &drawing,
1352 int layer_index,
1353 IndexMaskMemory &memory)
1354{
1355 const bke::CurvesGeometry &curves = drawing.strokes();
1356
1357 const IndexMask editable_points = retrieve_editable_points(object, drawing, layer_index, memory);
1358 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
1359
1360 return IndexMask::from_intersection(editable_points, selected_points, memory);
1361}
1362
1364 const bke::greasepencil::Drawing &drawing,
1365 int layer_index,
1366 const bke::AttrDomain selection_domain,
1367 IndexMaskMemory &memory)
1368{
1369 if (selection_domain == bke::AttrDomain::Curve) {
1371 object, drawing, layer_index, memory);
1372 }
1373 if (selection_domain == bke::AttrDomain::Point) {
1375 object, drawing, layer_index, memory);
1376 }
1377 return {};
1378}
1379
1381 const bke::greasepencil::Drawing &drawing,
1382 int layer_index,
1383 int handle_display,
1384 IndexMaskMemory &memory)
1385{
1386 const bke::CurvesGeometry &curves = drawing.strokes();
1387
1388 const IndexMask editable_points = retrieve_editable_points(object, drawing, layer_index, memory);
1390 curves, handle_display, memory);
1391
1392 return IndexMask::from_intersection(editable_points, selected_points, memory);
1393}
1394
1395bool has_editable_layer(const GreasePencil &grease_pencil)
1396{
1397 using namespace blender::bke::greasepencil;
1398 for (const Layer *layer : grease_pencil.layers()) {
1399 if (layer->is_editable()) {
1400 return true;
1401 }
1402 }
1403 return false;
1404}
1405
1407 const bke::CurvesGeometry &src,
1409 const Span<Vector<PointTransferData>> src_to_dst_points,
1410 const bool keep_caps)
1411{
1412 const int src_curves_num = src.curves_num();
1413 const OffsetIndices<int> src_points_by_curve = src.points_by_curve();
1414 const VArray<bool> src_cyclic = src.cyclic();
1415
1416 int dst_points_num = 0;
1417 for (const Vector<PointTransferData> &src_transfer_data : src_to_dst_points) {
1418 dst_points_num += src_transfer_data.size();
1419 }
1420 if (dst_points_num == 0) {
1421 dst.resize(0, 0);
1422 return Array<PointTransferData>(0);
1423 }
1424
1425 /* Set the intersection parameters in the destination domain : a pair of int and float
1426 * numbers for which the integer is the index of the corresponding segment in the
1427 * source curves, and the float part is the (0,1) factor representing its position in
1428 * the segment.
1429 */
1430 Array<PointTransferData> dst_transfer_data(dst_points_num);
1431
1432 Array<int> src_pivot_point(src_curves_num, -1);
1433 Array<int> dst_interm_curves_offsets(src_curves_num + 1, 0);
1434 int dst_point = -1;
1435 for (const int src_curve : src.curves_range()) {
1436 const IndexRange src_points = src_points_by_curve[src_curve];
1437
1438 for (const int src_point : src_points) {
1439 for (const PointTransferData &dst_point_transfer : src_to_dst_points[src_point]) {
1440 if (dst_point_transfer.is_src_point) {
1441 dst_transfer_data[++dst_point] = dst_point_transfer;
1442 continue;
1443 }
1444
1445 /* Add an intersection with the eraser and mark it as a cut. */
1446 dst_transfer_data[++dst_point] = dst_point_transfer;
1447
1448 /* For cyclic curves, mark the pivot point as the last intersection with the eraser
1449 * that starts a new segment in the destination.
1450 */
1451 if (src_cyclic[src_curve] && dst_point_transfer.is_cut) {
1452 src_pivot_point[src_curve] = dst_point;
1453 }
1454 }
1455 }
1456 /* We store intermediate curve offsets represent an intermediate state of the
1457 * destination curves before cutting the curves at eraser's intersection. Thus, it
1458 * contains the same number of curves than in the source, but the offsets are
1459 * different, because points may have been added or removed. */
1460 dst_interm_curves_offsets[src_curve + 1] = dst_point + 1;
1461 }
1462
1463 /* Cyclic curves. */
1464 Array<bool> src_now_cyclic(src_curves_num);
1465 threading::parallel_for(src.curves_range(), 4096, [&](const IndexRange src_curves) {
1466 for (const int src_curve : src_curves) {
1467 const int pivot_point = src_pivot_point[src_curve];
1468
1469 if (pivot_point == -1) {
1470 /* Either the curve was not cyclic or it wasn't cut : no need to change it. */
1471 src_now_cyclic[src_curve] = src_cyclic[src_curve];
1472 continue;
1473 }
1474
1475 /* A cyclic curve was cut :
1476 * - this curve is not cyclic anymore,
1477 * - and we have to shift points to keep the closing segment.
1478 */
1479 src_now_cyclic[src_curve] = false;
1480
1481 const int dst_interm_first = dst_interm_curves_offsets[src_curve];
1482 const int dst_interm_last = dst_interm_curves_offsets[src_curve + 1];
1483 std::rotate(dst_transfer_data.begin() + dst_interm_first,
1484 dst_transfer_data.begin() + pivot_point,
1485 dst_transfer_data.begin() + dst_interm_last);
1486 }
1487 });
1488
1489 /* Compute the destination curve offsets. */
1490 Vector<int> dst_curves_offset;
1491 Vector<int> dst_to_src_curve;
1492 dst_curves_offset.append(0);
1493 for (int src_curve : src.curves_range()) {
1494 const IndexRange dst_points(dst_interm_curves_offsets[src_curve],
1495 dst_interm_curves_offsets[src_curve + 1] -
1496 dst_interm_curves_offsets[src_curve]);
1497 int length_of_current = 0;
1498
1499 for (int dst_point : dst_points) {
1500
1501 if ((length_of_current > 0) && dst_transfer_data[dst_point].is_cut) {
1502 /* This is the new first point of a curve. */
1503 dst_curves_offset.append(dst_point);
1504 dst_to_src_curve.append(src_curve);
1505 length_of_current = 0;
1506 }
1507 ++length_of_current;
1508 }
1509
1510 if (length_of_current != 0) {
1511 /* End of a source curve. */
1512 dst_curves_offset.append(dst_points.one_after_last());
1513 dst_to_src_curve.append(src_curve);
1514 }
1515 }
1516 const int dst_curves_num = dst_curves_offset.size() - 1;
1517 if (dst_curves_num == 0) {
1518 dst.resize(0, 0);
1519 return dst_transfer_data;
1520 }
1521
1522 /* Build destination curves geometry. */
1523 dst.resize(dst_points_num, dst_curves_num);
1524 array_utils::copy(dst_curves_offset.as_span(), dst.offsets_for_write());
1525 const OffsetIndices<int> dst_points_by_curve = dst.points_by_curve();
1526
1527 /* Vertex group names. */
1528 BLI_assert(BLI_listbase_count(&dst.vertex_group_names) == 0);
1529 BKE_defgroup_copy_list(&dst.vertex_group_names, &src.vertex_group_names);
1530
1531 /* Attributes. */
1532 const bke::AttributeAccessor src_attributes = src.attributes();
1533 bke::MutableAttributeAccessor dst_attributes = dst.attributes_for_write();
1534
1535 /* Copy curves attributes. */
1536 bke::gather_attributes(src_attributes,
1540 dst_to_src_curve,
1541 dst_attributes);
1542 if (src_cyclic.get_if_single().value_or(true)) {
1544 src_now_cyclic.as_span(), dst_to_src_curve.as_span(), dst.cyclic_for_write());
1545 }
1546
1547 dst.update_curve_types();
1548
1549 /* Display intersections with flat caps. */
1550 if (!keep_caps) {
1551 bke::SpanAttributeWriter<int8_t> dst_start_caps =
1552 dst_attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve);
1553 bke::SpanAttributeWriter<int8_t> dst_end_caps =
1555
1556 threading::parallel_for(dst.curves_range(), 4096, [&](const IndexRange dst_curves) {
1557 for (const int dst_curve : dst_curves) {
1558 const IndexRange dst_curve_points = dst_points_by_curve[dst_curve];
1559 const PointTransferData &start_point_transfer =
1560 dst_transfer_data[dst_curve_points.first()];
1561 const PointTransferData &end_point_transfer = dst_transfer_data[dst_curve_points.last()];
1562
1563 if (dst_start_caps && start_point_transfer.is_cut) {
1564 dst_start_caps.span[dst_curve] = GP_STROKE_CAP_TYPE_FLAT;
1565 }
1566 /* The is_cut flag does not work for end points, but any end point that isn't the source
1567 * point must also be a cut. */
1568 if (dst_end_caps && !end_point_transfer.is_src_end_point()) {
1569 dst_end_caps.span[dst_curve] = GP_STROKE_CAP_TYPE_FLAT;
1570 }
1571 }
1572 });
1573
1574 dst_start_caps.finish();
1575 dst_end_caps.finish();
1576 }
1577
1578 /* Copy/Interpolate point attributes. */
1579 for (bke::AttributeTransferData &attribute : bke::retrieve_attributes_for_transfer(
1580 src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT))
1581 {
1582 bke::attribute_math::convert_to_static_type(attribute.dst.span.type(), [&](auto dummy) {
1583 using T = decltype(dummy);
1584 auto src_attr = attribute.src.typed<T>();
1585 auto dst_attr = attribute.dst.span.typed<T>();
1586
1587 threading::parallel_for(dst.points_range(), 4096, [&](const IndexRange dst_points) {
1588 for (const int dst_point : dst_points) {
1589 const PointTransferData &point_transfer = dst_transfer_data[dst_point];
1590 if (point_transfer.is_src_point) {
1591 dst_attr[dst_point] = src_attr[point_transfer.src_point];
1592 }
1593 else {
1594 dst_attr[dst_point] = bke::attribute_math::mix2<T>(
1595 point_transfer.factor,
1596 src_attr[point_transfer.src_point],
1597 src_attr[point_transfer.src_next_point]);
1598 }
1599 }
1600 });
1601
1602 attribute.dst.finish();
1603 });
1604 }
1605
1606 return dst_transfer_data;
1607}
1608
1610 const ARegion *region,
1611 const float3 center,
1612 const float4x4 to_world,
1613 const float pixel_radius)
1614{
1615 const float2 xy_delta = float2(pixel_radius, 0.0f);
1616 const float3 loc = math::transform_point(to_world, center);
1617
1618 const float zfac = ED_view3d_calc_zfac(rv3d, loc);
1619 float3 delta;
1620 ED_view3d_win_to_delta(region, xy_delta, zfac, delta);
1621
1622 const float scale = math::length(
1624
1625 return math::safe_divide(math::length(delta), scale);
1626}
1627
1629 const ARegion *region,
1630 const Brush *brush,
1631 const float3 location,
1632 const float4x4 to_world)
1633{
1634 if ((brush->flag & BRUSH_LOCK_SIZE) == 0) {
1636 rv3d, region, location, to_world, float(brush->size) / 2.0f);
1637 }
1638 return brush->unprojected_size / 2.0f;
1639}
1640
1642 const ARegion *region,
1643 const Brush *brush,
1644 const float pressure,
1645 const float3 &location,
1646 const float4x4 &to_world,
1647 const BrushGpencilSettings *settings)
1648{
1649 float radius = brush_radius_at_location(rv3d, region, brush, location, to_world);
1651 radius *= BKE_curvemapping_evaluateF(settings->curve_sensitivity, 0, pressure);
1652 }
1653 return radius;
1654}
1655
1656float opacity_from_input_sample(const float pressure,
1657 const Brush *brush,
1658 const BrushGpencilSettings *settings)
1659{
1660 float opacity = brush->alpha;
1662 opacity *= BKE_curvemapping_evaluateF(settings->curve_strength, 0, pressure);
1663 }
1664 return opacity;
1665}
1666
1668 wmOperator *op,
1669 const bool use_duplicate_previous_key)
1670{
1671 const Scene *scene = CTX_data_scene(C);
1672 const Object *object = CTX_data_active_object(C);
1673 if (!object || object->type != OB_GREASE_PENCIL) {
1674 return OPERATOR_CANCELLED;
1675 }
1676
1677 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1678 if (!grease_pencil.has_active_layer()) {
1679 BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
1680 return OPERATOR_CANCELLED;
1681 }
1682
1685 if (brush == nullptr) {
1686 return OPERATOR_CANCELLED;
1687 }
1688
1689 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1690
1691 if (!active_layer.is_editable()) {
1692 BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
1693 return OPERATOR_CANCELLED;
1694 }
1695
1696 /* Ensure a drawing at the current keyframe. */
1697 bool inserted_keyframe = false;
1699 *scene, grease_pencil, active_layer, use_duplicate_previous_key, inserted_keyframe))
1700 {
1701 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
1702 return OPERATOR_CANCELLED;
1703 }
1704 if (inserted_keyframe) {
1706 }
1708}
1709
1711 const ARegion *region,
1712 const float2 &mouse,
1713 const DrawingPlacement &placement)
1714{
1715 float3 u_dir;
1716 float3 v_dir;
1717 /* Set the texture space origin to be the first point. */
1718 float3 origin = placement.project(mouse);
1719 /* Align texture with the drawing plane. */
1720 switch (scene->toolsettings->gp_sculpt.lock_axis) {
1721 case GP_LOCKAXIS_VIEW:
1722 u_dir = math::normalize(placement.project(float2(region->winx, 0.0f) + mouse) - origin);
1723 v_dir = math::normalize(placement.project(float2(0.0f, region->winy) + mouse) - origin);
1724 break;
1725 case GP_LOCKAXIS_Y:
1726 u_dir = float3(1.0f, 0.0f, 0.0f);
1727 v_dir = float3(0.0f, 0.0f, 1.0f);
1728 break;
1729 case GP_LOCKAXIS_X:
1730 u_dir = float3(0.0f, 1.0f, 0.0f);
1731 v_dir = float3(0.0f, 0.0f, 1.0f);
1732 break;
1733 case GP_LOCKAXIS_Z:
1734 u_dir = float3(1.0f, 0.0f, 0.0f);
1735 v_dir = float3(0.0f, 1.0f, 0.0f);
1736 break;
1737 case GP_LOCKAXIS_CURSOR: {
1738 const float3x3 mat = scene->cursor.matrix<float3x3>();
1739 u_dir = mat * float3(1.0f, 0.0f, 0.0f);
1740 v_dir = mat * float3(0.0f, 1.0f, 0.0f);
1741 origin = float3(scene->cursor.location);
1742 break;
1743 }
1744 }
1745
1746 return math::transpose(float2x4(float4(u_dir, -math::dot(u_dir, origin)),
1747 float4(v_dir, -math::dot(v_dir, origin))));
1748}
1749
1751{
1752 GreasePencil *grease_pencil = static_cast<GreasePencil *>(
1753 CTX_data_pointer_get_type(&C, "grease_pencil", &RNA_GreasePencil).data);
1754
1755 if (grease_pencil == nullptr) {
1756 Object *object = CTX_data_active_object(&C);
1757 if (object && object->type == OB_GREASE_PENCIL) {
1758 grease_pencil = static_cast<GreasePencil *>(object->data);
1759 }
1760 }
1761 return grease_pencil;
1762}
1763
1765{
1766 if (at_end) {
1767 const int num_old_points = curves.points_num();
1768 curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
1769 curves.offsets_for_write().last(1) = num_old_points;
1770 return;
1771 }
1772
1773 curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
1774 MutableSpan<int> offsets = curves.offsets_for_write();
1775 offsets.first() = 0;
1776
1777 /* Loop through backwards to not overwrite the data. */
1778 for (int i = curves.curves_num() - 2; i >= 0; i--) {
1779 offsets[i + 1] = offsets[i] + 1;
1780 }
1781
1782 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1783
1784 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1786 GMutableSpan attribute_data = dst.span;
1787
1788 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1789 using T = decltype(dummy);
1790 MutableSpan<T> span_data = attribute_data.typed<T>();
1791
1792 /* Loop through backwards to not overwrite the data. */
1793 for (int i = span_data.size() - 2; i >= 0; i--) {
1794 span_data[i + 1] = span_data[i];
1795 }
1796 });
1797 dst.finish();
1798 });
1799}
1800
1801void resize_single_curve(bke::CurvesGeometry &curves, const bool at_end, const int new_points_num)
1802{
1803 BLI_assert(new_points_num >= 0);
1804 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1805 const int curve_index = at_end ? curves.curves_range().last() : 0;
1806 const int current_points_num = points_by_curve[curve_index].size();
1807 if (new_points_num == current_points_num) {
1808 return;
1809 }
1810
1811 if (at_end) {
1812 const int diff_points_num = new_points_num - current_points_num;
1813 curves.resize(curves.points_num() + diff_points_num, curves.curves_num());
1814 curves.offsets_for_write().last() = curves.points_num();
1815 return;
1816 }
1817
1818 if (current_points_num < new_points_num) {
1819 const int last_active_point = points_by_curve[0].last();
1820
1821 const int added_points_num = new_points_num - current_points_num;
1822
1823 curves.resize(curves.points_num() + added_points_num, curves.curves_num());
1824 MutableSpan<int> offsets = curves.offsets_for_write();
1825 for (const int src_curve : curves.curves_range().drop_front(1)) {
1826 offsets[src_curve] = offsets[src_curve] + added_points_num;
1827 }
1828 offsets.last() = curves.points_num();
1829
1830 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1831 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1832 if (iter.domain != bke::AttrDomain::Point) {
1833 return;
1834 }
1835
1837 GMutableSpan attribute_data = dst.span;
1838
1839 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1840 using T = decltype(dummy);
1841 MutableSpan<T> span_data = attribute_data.typed<T>();
1842
1843 /* Loop through backwards to not overwrite the data. */
1844 for (int i = span_data.size() - 1 - added_points_num; i >= last_active_point; i--) {
1845 span_data[i + added_points_num] = span_data[i];
1846 }
1847 });
1848 dst.finish();
1849 });
1850 }
1851 else {
1852 /* First move the attribute data, then resize. */
1853 const int removed_points_num = current_points_num - new_points_num;
1854 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1855 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1856 if (iter.domain != bke::AttrDomain::Point) {
1857 return;
1858 }
1859
1861 GMutableSpan attribute_data = dst.span;
1862
1863 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
1864 using T = decltype(dummy);
1865 MutableSpan<T> span_data = attribute_data.typed<T>();
1866
1867 for (const int i :
1868 span_data.index_range().drop_front(new_points_num).drop_back(removed_points_num))
1869 {
1870 span_data[i] = span_data[i + removed_points_num];
1871 }
1872 });
1873 dst.finish();
1874 });
1875
1876 curves.resize(curves.points_num() - removed_points_num, curves.curves_num());
1877 MutableSpan<int> offsets = curves.offsets_for_write();
1878 for (const int src_curve : curves.curves_range().drop_front(1)) {
1879 offsets[src_curve] = offsets[src_curve] - removed_points_num;
1880 }
1881 offsets.last() = curves.points_num();
1882 }
1883}
1884
1885void apply_eval_grease_pencil_data(const GreasePencil &eval_grease_pencil,
1886 const int eval_frame,
1887 const IndexMask &orig_layers,
1888 GreasePencil &orig_grease_pencil)
1889{
1890 using namespace bke;
1891 using namespace bke::greasepencil;
1892 /* Build a set of pointers to the layers that we want to apply. */
1893 Set<const Layer *> orig_layers_to_apply;
1894 orig_layers.foreach_index([&](const int layer_i) {
1895 const Layer &layer = orig_grease_pencil.layer(layer_i);
1896 orig_layers_to_apply.add(&layer);
1897 });
1898
1899 /* Ensure that the layer names are unique by merging layers with the same name. */
1900 const int old_layers_num = eval_grease_pencil.layers().size();
1901 Vector<Vector<int>> layers_map;
1902 Map<StringRef, int> new_layer_index_by_name;
1903 for (const int layer_i : IndexRange(old_layers_num)) {
1904 const Layer &layer = eval_grease_pencil.layer(layer_i);
1905 const int new_layer_index = new_layer_index_by_name.lookup_or_add_cb(
1906 layer.name(), [&]() { return layers_map.append_and_get_index_as(); });
1907 layers_map[new_layer_index].append(layer_i);
1908 }
1909 GreasePencil &merged_layers_grease_pencil = *geometry::merge_layers(
1910 eval_grease_pencil, layers_map, {});
1911
1912 Map<const Layer *, const Layer *> eval_to_orig_layer_map;
1913 {
1914 /* Set of orig layers that require the drawing on `eval_frame` to be cleared. These are layers
1915 * that existed in original geometry but were removed in the evaluated data. */
1916 Set<Layer *> orig_layers_to_clear;
1917 for (Layer *layer : orig_grease_pencil.layers_for_write()) {
1918 /* Only allow clearing a layer if it is visible. */
1919 if (layer->is_visible()) {
1920 orig_layers_to_clear.add(layer);
1921 }
1922 }
1923 for (const TreeNode *node_eval : merged_layers_grease_pencil.nodes()) {
1924 /* Check if the original geometry has a layer with the same name. */
1925 TreeNode *node_orig = orig_grease_pencil.find_node_by_name(node_eval->name());
1926
1927 BLI_assert(node_eval != nullptr);
1928 if (!node_eval->is_layer()) {
1929 continue;
1930 }
1931 /* If the orig layer isn't valid then a new layer with a unique name will be generated. */
1932 const bool has_valid_orig_layer = (node_orig != nullptr && node_orig->is_layer());
1933 if (!has_valid_orig_layer) {
1934 /* Note: This name might be empty! This has to be resolved at a later stage! */
1935 Layer &layer_orig = orig_grease_pencil.add_layer(node_eval->name(), true);
1936 orig_layers_to_apply.add(&layer_orig);
1937 /* Make sure to add a new keyframe with a new drawing. */
1938 orig_grease_pencil.insert_frame(layer_orig, eval_frame);
1939 node_orig = &layer_orig.as_node();
1940 }
1941 BLI_assert(node_orig != nullptr);
1942 Layer &layer_orig = node_orig->as_layer();
1943 /* This layer has a matching evaluated layer, so don't clear its keyframe. */
1944 orig_layers_to_clear.remove(&layer_orig);
1945 /* Only map layers in `eval_to_orig_layer_map` that we want to apply. */
1946 if (orig_layers_to_apply.contains(&layer_orig)) {
1947 /* Copy layer properties to original geometry. */
1948 const Layer &layer_eval = node_eval->as_layer();
1949 layer_orig.opacity = layer_eval.opacity;
1950 layer_orig.set_local_transform(layer_eval.local_transform());
1951
1952 /* Add new mapping for `layer_eval` -> `layer_orig`. */
1953 eval_to_orig_layer_map.add_new(&layer_eval, &layer_orig);
1954 }
1955 }
1956
1957 /* Clear the keyframe of all the original layers that don't have a matching evaluated layer,
1958 * e.g. the ones that were "deleted" in the evaluated data. */
1959 for (Layer *layer_orig : orig_layers_to_clear) {
1960 /* Try inserting a frame. */
1961 Drawing *drawing_orig = orig_grease_pencil.insert_frame(*layer_orig, eval_frame);
1962 if (drawing_orig == nullptr) {
1963 /* If that fails, get the drawing for this frame. */
1964 drawing_orig = orig_grease_pencil.get_drawing_at(*layer_orig, eval_frame);
1965 }
1966 /* Clear the existing drawing. */
1967 drawing_orig->strokes_for_write() = {};
1968 drawing_orig->tag_topology_changed();
1969 }
1970 }
1971
1972 /* Gather the original vertex group names. */
1973 Set<StringRef> orig_vgroup_names;
1974 LISTBASE_FOREACH (bDeformGroup *, dg, &orig_grease_pencil.vertex_group_names) {
1975 orig_vgroup_names.add(dg->name);
1976 }
1977
1978 /* Update the drawings. */
1979 VectorSet<Drawing *> all_updated_drawings;
1980
1981 Set<StringRef> new_vgroup_names;
1982 for (auto [layer_eval, layer_orig] : eval_to_orig_layer_map.items()) {
1983 Drawing *drawing_eval = merged_layers_grease_pencil.get_drawing_at(*layer_eval, eval_frame);
1984 Drawing *drawing_orig = orig_grease_pencil.get_drawing_at(*layer_orig, eval_frame);
1985
1986 if (drawing_orig && drawing_eval) {
1987 CurvesGeometry &eval_strokes = drawing_eval->strokes_for_write();
1988
1989 /* Check for new vertex groups in CurvesGeometry. */
1990 LISTBASE_FOREACH (bDeformGroup *, dg, &eval_strokes.vertex_group_names) {
1991 if (!orig_vgroup_names.contains(dg->name)) {
1992 new_vgroup_names.add(dg->name);
1993 }
1994 }
1995
1996 /* Write the data to the original drawing. */
1997 drawing_orig->strokes_for_write() = std::move(eval_strokes);
1998 /* Anonymous attributes shouldn't be available on original geometry. */
2000 drawing_orig->tag_topology_changed();
2001 all_updated_drawings.add_new(drawing_orig);
2002 }
2003 }
2004
2005 /* Add new vertex groups to GreasePencil object. */
2006 for (StringRef new_vgroup_name : new_vgroup_names) {
2007 bDeformGroup *dst = MEM_callocN<bDeformGroup>(__func__);
2008 new_vgroup_name.copy_utf8_truncated(dst->name);
2009 BLI_addtail(&orig_grease_pencil.vertex_group_names, dst);
2010 }
2011
2012 /* Get the original material pointers from the result geometry. */
2013 VectorSet<Material *> original_materials;
2014 const Span<Material *> eval_materials = Span{eval_grease_pencil.material_array,
2015 eval_grease_pencil.material_array_num};
2016 for (Material *eval_material : eval_materials) {
2017 if (!eval_material) {
2018 return;
2019 }
2020 original_materials.add(DEG_get_original(eval_material));
2021 }
2022
2023 /* Build material indices mapping. This maps the materials indices on the original geometry to
2024 * the material indices used in the result geometry. The material indices for the drawings in
2025 * the result geometry are already correct, but this might not be the case for all drawings in
2026 * the original geometry (like for drawings that are not visible on the frame that the data is
2027 * being applied on). */
2028 const IndexRange orig_material_indices = IndexRange(orig_grease_pencil.material_array_num);
2029 Array<int> material_indices_map(orig_grease_pencil.material_array_num, -1);
2030 for (const int mat_i : orig_material_indices) {
2031 Material *material = orig_grease_pencil.material_array[mat_i];
2032 const int map_index = original_materials.index_of_try(material);
2033 if (map_index != -1) {
2034 material_indices_map[mat_i] = map_index;
2035 }
2036 }
2037
2038 /* Remap material indices for all other drawings. */
2039 if (!material_indices_map.is_empty() &&
2040 !array_utils::indices_are_range(material_indices_map, orig_material_indices))
2041 {
2042 for (GreasePencilDrawingBase *base : orig_grease_pencil.drawings()) {
2043 if (base->type != GP_DRAWING) {
2044 continue;
2045 }
2046 Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2047 if (all_updated_drawings.contains(&drawing)) {
2048 /* Skip remapping drawings that already have been updated. */
2049 continue;
2050 }
2052 if (!attributes.contains("material_index")) {
2053 continue;
2054 }
2055 SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
2056 "material_index", AttrDomain::Curve);
2057 for (int &material_index : material_indices.span) {
2058 if (material_indices_map.index_range().contains(material_index)) {
2059 material_index = material_indices_map[material_index];
2060 }
2061 }
2062 material_indices.finish();
2063 }
2064 }
2065
2066 /* Convert the layer map into an index mapping. */
2067 Map<int, int> eval_to_orig_layer_indices_map;
2068 for (const int layer_eval_i : merged_layers_grease_pencil.layers().index_range()) {
2069 const Layer *layer_eval = &merged_layers_grease_pencil.layer(layer_eval_i);
2070 if (eval_to_orig_layer_map.contains(layer_eval)) {
2071 const Layer *layer_orig = eval_to_orig_layer_map.lookup(layer_eval);
2072 const int layer_orig_index = *orig_grease_pencil.get_layer_index(*layer_orig);
2073 eval_to_orig_layer_indices_map.add(layer_eval_i, layer_orig_index);
2074 }
2075 }
2076
2077 /* Propagate layer attributes. */
2078 AttributeAccessor src_attributes = merged_layers_grease_pencil.attributes();
2079 MutableAttributeAccessor dst_attributes = orig_grease_pencil.attributes_for_write();
2080 src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
2081 /* Anonymous attributes shouldn't be available on original geometry. */
2083 return;
2084 }
2085 if (iter.data_type == bke::AttrType::String) {
2086 return;
2087 }
2088 const GVArraySpan src = *iter.get(AttrDomain::Layer);
2090 iter.name, AttrDomain::Layer, iter.data_type);
2091 if (!dst) {
2092 return;
2093 }
2094 attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
2095 using T = decltype(dummy);
2096 Span<T> src_span = src.typed<T>();
2097 MutableSpan<T> dst_span = dst.span.typed<T>();
2098 for (const auto [src_i, dst_i] : eval_to_orig_layer_indices_map.items()) {
2099 dst_span[dst_i] = src_span[src_i];
2100 }
2101 });
2102 dst.finish();
2103 });
2104
2105 /* Free temporary grease pencil struct. */
2106 BKE_id_free(nullptr, &merged_layers_grease_pencil);
2107}
2108
2110{
2111 if (!curves.attributes().contains(".is_fill_guide")) {
2112 return false;
2113 }
2114
2115 const bke::AttributeAccessor attributes = curves.attributes();
2116 const VArray<bool> is_fill_guide = *attributes.lookup<bool>(".is_fill_guide",
2118
2119 IndexMaskMemory memory;
2120 const IndexMask fill_guides = IndexMask::from_bools(is_fill_guide, memory);
2121 curves.remove_curves(fill_guides, {});
2122
2123 curves.attributes_for_write().remove(".is_fill_guide");
2124
2125 return true;
2126}
2127
2128} // namespace blender::ed::greasepencil
@ ATTR_DOMAIN_MASK_POINT
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1290
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1285
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Low-level operations for curves.
support for deformation groups and hooks.
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:73
Low-level operations for grease pencil.
void BKE_id_free(Main *bmain, void *idv)
General operations, lookup, etc. for materials.
Material * BKE_object_material_get(Object *ob, short act)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
float BKE_scene_ctime_get(const Scene *scene)
Definition scene.cc:2370
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:217
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, bool clip)
#define ELEM(...)
T * DEG_get_original(T *id)
@ BRUSH_LOCK_SIZE
@ GP_ONION_SKINNING_MODE_ABSOLUTE
@ GP_ONION_SKINNING_MODE_SELECTED
@ GP_ONION_SKINNING_MODE_RELATIVE
@ GP_ONION_SKINNING_SHOW_LOOP
@ GP_MATERIAL_LOCKED
@ GP_MATERIAL_HIDE
@ GP_MATERIAL_FILL_SHOW
Object is a sort of wrapper for general info.
@ OB_GREASE_PENCIL
@ GP_SCULPT_SETT_FLAG_FRAME_FALLOFF
@ GP_LOCKAXIS_X
@ GP_LOCKAXIS_VIEW
@ GP_LOCKAXIS_Y
@ GP_LOCKAXIS_Z
@ GP_LOCKAXIS_CURSOR
@ GP_PROJECT_VIEWSPACE
@ GP_PROJECT_DEPTH_VIEW
@ GP_PROJECT_CURSOR
@ GP_PROJECT_DEPTH_STROKE
@ GP_PROJECT_DEPTH_ONLY_SELECTED
@ GP_USE_MULTI_FRAME_EDITING
@ V3D_GP_FORCE_STROKE_ORDER_3D
@ CURVE_HANDLE_NONE
@ CURVE_HANDLE_ALL
@ OPERATOR_CANCELLED
@ OPERATOR_RUNNING_MODAL
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3], bool precise=false)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
bool ED_view3d_unproject_v3(const ARegion *region, float regionx, float regiony, float regionz, float world[3])
void ED_view3d_win_to_3d_with_shift(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3])
void ED_view3d_depths_free(ViewDepths *depths)
eV3DDepthOverrideMode
Definition ED_view3d.hh:188
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:198
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:192
@ V3D_DEPTH_GPENCIL_ONLY
Definition ED_view3d.hh:194
bool ED_view3d_win_to_3d_on_plane(const ARegion *region, const float plane[4], const float mval[2], bool do_clip, float r_out[3])
void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r_out[3])
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
#define C
Definition RandGen.cpp:29
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
switch((BMIterType) itype)
BPy_StructRNA * depsgraph
long long int int64_t
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
ItemIterator items() const &
Definition BLI_map.hh:902
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
IndexRange index_range() const
Definition BLI_array.hh:360
bool is_empty() const
Definition BLI_array.hh:264
const CPPType & type() const
const CPPType & type() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_ranges(OffsetIndices< T > offsets, const IndexMask &mask, IndexMaskMemory &memory)
static IndexMask from_intersection(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t last(const int64_t n=0) const
constexpr bool contains(int64_t value) const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
constexpr T & first() const
Definition BLI_span.hh:679
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
void add_new(const Key &key)
Definition BLI_set.hh:233
bool remove(const Key &key)
Definition BLI_set.hh:385
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
std::optional< T > get_if_single() const
int64_t index_of_try(const Key &key) const
bool add(const Key &key)
int64_t size() const
int64_t index_of(const Key &key) const
bool contains(const Key &key) const
void add_new(const Key &key)
int64_t size() const
void append(const T &value)
Span< T > as_span() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader get() const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
IndexRange points_range() const
void resize(int points_num, int curves_num)
VArray< bool > cyclic() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
int sorted_keys_index_at(int frame_number) const
float4x4 to_world_space(const Object &object) const
void set_local_transform(const float4x4 &transform)
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
std::optional< int > start_frame_at(int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
DrawingPlacement & operator=(const DrawingPlacement &other)
std::optional< float > get_depth(float2 co) const
void cache_viewport_depths(Depsgraph *depsgraph, ARegion *region, View3D *view3d)
float3 place(float2 co, float depth) const
std::optional< float3 > project_depth(float2 co) const
float3 project(float2 co, bool &clipped) const
bool contains(int64_t query_index) const
void foreach_index(Fn &&fn) const
nullptr float
uint pos
static char ** types
Definition makesdna.cc:71
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
bool indices_are_range(Span< int > indices, IndexRange range)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
IndexMask indices_for_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const CurveType type, const IndexMask &selection, IndexMaskMemory &memory)
bool attribute_name_is_anonymous(const StringRef name)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
IndexMask retrieve_all_selected_points(const bke::CurvesGeometry &curves, const int handle_display, IndexMaskMemory &memory)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_elements(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_handle_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, const int handle_display, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static Array< int > get_editable_frames_for_layer(const GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer, const int current_frame, const bool use_multi_frame_editing)
void resize_single_curve(bke::CurvesGeometry &curves, const bool at_end, const int new_points_num)
static float get_frame_falloff(const bool use_multi_frame_falloff, const int frame_number, const int active_frame, const std::optional< Bounds< int > > frame_bounds, const CurveMapping *falloff_curve)
static std::optional< int > get_frame_id(const bke::greasepencil::Layer &layer, const GreasePencilFrame &frame, const int frame_number, const int frame_index, const int current_frame, const int current_frame_index, const int last_frame, const int last_frame_index, const bool use_multi_frame_editing, const bool do_onion_skinning, const bool is_before_first, const GreasePencilOnionSkinningSettings onion_settings)
IndexMask retrieve_editable_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_handle_strokes(Object &object, const bke::greasepencil::Drawing &drawing, const int handle_display, IndexMaskMemory &memory)
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const bool duplicate_previous_key, bool &r_inserted_keyframe)
GreasePencil * from_context(bContext &C)
IndexMask retrieve_editable_and_all_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, int handle_display, IndexMaskMemory &memory)
IndexMask retrieve_editable_fill_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
float opacity_from_input_sample(const float pressure, const Brush *brush, const BrushGpencilSettings *settings)
IndexMask retrieve_editable_elements(Object &object, const MutableDrawingInfo &info, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
void add_single_curve(bke::CurvesGeometry &curves, const bool at_end)
bke::CurvesGeometry fill_strokes(const ViewContext &view_context, const Brush &brush, const Scene &scene, const bke::greasepencil::Layer &layer, const VArray< bool > &boundary_layers, Span< DrawingInfo > src_drawings, bool invert, const std::optional< float > alpha_threshold, const float2 &fill_point, const ExtensionData &extensions, FillToolFitMethod fit_method, int stroke_material_index, bool keep_images)
IndexMask retrieve_visible_points(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
IndexMask retrieve_editable_strokes_by_material(Object &object, const bke::greasepencil::Drawing &drawing, const int mat_i, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_fill_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
bool has_editable_layer(const GreasePencil &grease_pencil)
static float pixel_radius_to_world_space_radius(const RegionView3D *rv3d, const ARegion *region, const float3 center, const float4x4 to_world, const float pixel_radius)
IndexMask retrieve_visible_bezier_points(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
static float brush_radius_at_location(const RegionView3D *rv3d, const ARegion *region, const Brush *brush, const float3 location, const float4x4 to_world)
static Array< std::pair< int, int > > get_visible_frames_for_layer(const GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer, const int current_frame, const bool use_multi_frame_editing, const bool do_onion_skinning)
Array< PointTransferData > compute_topology_change(const bke::CurvesGeometry &src, bke::CurvesGeometry &dst, const Span< Vector< PointTransferData > > src_to_dst_points, const bool keep_caps)
IndexMask retrieve_visible_bezier_handle_elements(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, const bke::AttrDomain selection_domain, const int handle_display, IndexMaskMemory &memory)
static VectorSet< int > get_locked_material_indices(Object &object)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static std::optional< Bounds< int > > get_selected_frame_number_bounds(const bke::greasepencil::Layer &layer)
static VectorSet< int > get_fill_material_indices(Object &object)
static VectorSet< int > get_hidden_material_indices(Object &object)
static int get_active_frame_for_falloff(const bke::greasepencil::Layer &layer, const std::optional< Bounds< int > > frame_bounds, const int current_frame)
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
IndexMask retrieve_visible_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer_with_falloff(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
Vector< MutableDrawingInfo > retrieve_editable_drawings_with_falloff(const Scene &scene, GreasePencil &grease_pencil)
wmOperatorStatus grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op, const bool use_duplicate_previous_key)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
bool remove_fill_guides(bke::CurvesGeometry &curves)
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)
Array< Vector< MutableDrawingInfo > > retrieve_editable_drawings_grouped_per_frame(const Scene &scene, GreasePencil &grease_pencil)
void apply_eval_grease_pencil_data(const GreasePencil &eval_grease_pencil, const int eval_frame, const IndexMask &orig_layers, GreasePencil &orig_grease_pencil)
GreasePencil * merge_layers(const GreasePencil &src_grease_pencil, Span< Vector< int > > layers_to_merge, const bke::AttributeFilter &attribute_filter)
constexpr double inv_sqrt3
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
T clamp(const T &a, const T &min, const T &max)
T safe_divide(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
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
MatBase< float, 2, 4 > float2x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
float wrap(float value, float max, float min)
Definition node_math.h:103
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
ListBase vertex_group_names
struct CurveMapping * cur_falloff
struct Material ** material_array
GreasePencilOnionSkinningSettings onion_skinning_settings
struct MaterialGPencilStyle * gp_style
void * data
Definition RNA_types.hh:53
float viewinv[4][4]
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
struct GP_Sculpt_Settings gp_sculpt
float * depths
Definition ED_view3d.hh:89
struct ReportList * reports
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)