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