Blender V4.3
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
9#include "BKE_attribute.hh"
10#include "BKE_brush.hh"
11#include "BKE_colortools.hh"
12#include "BKE_context.hh"
13#include "BKE_grease_pencil.hh"
14#include "BKE_material.h"
15#include "BKE_paint.hh"
16#include "BKE_report.hh"
17#include "BKE_scene.hh"
18
19#include "BLI_math_geom.h"
20#include "BLI_math_numbers.hh"
21#include "BLI_math_vector.hh"
22#include "BLI_vector_set.hh"
23
24#include "DNA_brush_types.h"
25#include "DNA_material_types.h"
26#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28#include "DNA_view3d_types.h"
29
30#include "RNA_prototypes.hh"
31
32#include "ED_curves.hh"
33#include "ED_grease_pencil.hh"
34#include "ED_view3d.hh"
35
37
39 const ARegion &region,
40 const View3D &view3d,
41 const Object &eval_object,
42 const bke::greasepencil::Layer *layer)
43 : region_(&region), view3d_(&view3d)
44{
45 layer_space_to_world_space_ = (layer != nullptr) ? layer->to_world_space(eval_object) :
46 eval_object.object_to_world();
47 world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
48 /* Initialize DrawingPlacementPlane from toolsettings. */
49 switch (scene.toolsettings->gp_sculpt.lock_axis) {
50 case GP_LOCKAXIS_VIEW:
51 plane_ = DrawingPlacementPlane::View;
52 break;
53 case GP_LOCKAXIS_Y:
54 plane_ = DrawingPlacementPlane::Front;
55 placement_normal_ = float3(0, 1, 0);
56 break;
57 case GP_LOCKAXIS_X:
58 plane_ = DrawingPlacementPlane::Side;
59 placement_normal_ = float3(1, 0, 0);
60 break;
61 case GP_LOCKAXIS_Z:
62 plane_ = DrawingPlacementPlane::Top;
63 placement_normal_ = float3(0, 0, 1);
64 break;
65 case GP_LOCKAXIS_CURSOR: {
66 plane_ = DrawingPlacementPlane::Cursor;
67 placement_normal_ = scene.cursor.matrix<float3x3>() * float3(0, 0, 1);
68 break;
69 }
70 }
71
72 /* Account for layer transform. */
73 if (!ELEM(scene.toolsettings->gp_sculpt.lock_axis, GP_LOCKAXIS_VIEW, GP_LOCKAXIS_CURSOR)) {
74 /* Use the transpose inverse for normal. */
75 placement_normal_ = math::transform_direction(math::transpose(world_space_to_layer_space_),
76 placement_normal_);
77 }
78
79 /* Initialize DrawingPlacementDepth from toolsettings. */
80 const char align_flag = scene.toolsettings->gpencil_v3d_align;
81 if (align_flag & GP_PROJECT_VIEWSPACE) {
82 if (align_flag & GP_PROJECT_CURSOR) {
84 surface_offset_ = 0.0f;
85 placement_loc_ = float3(scene.cursor.location);
86 }
87 else if (align_flag & GP_PROJECT_DEPTH_VIEW) {
89 if (align_flag & GP_PROJECT_DEPTH_ONLY_SELECTED) {
90 use_project_only_selected_ = true;
91 }
92 surface_offset_ = scene.toolsettings->gpencil_surface_offset;
93 /* Default to view placement with the object origin if we don't hit a surface. */
94 placement_loc_ = layer_space_to_world_space_.location();
95 }
96 else if (align_flag & GP_PROJECT_DEPTH_STROKE) {
98 surface_offset_ = 0.0f;
99 /* Default to view placement with the object origin if we don't hit a stroke. */
100 placement_loc_ = layer_space_to_world_space_.location();
101 }
102 else {
104 surface_offset_ = 0.0f;
105 placement_loc_ = layer_space_to_world_space_.location();
106 }
107 }
108 else {
110 surface_offset_ = 0.0f;
111 placement_loc_ = float3(0.0f);
112 }
113
114 if (plane_ != DrawingPlacementPlane::View) {
115 plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
116 }
117}
118
120 const ARegion &region,
121 const View3D &view3d,
122 const Object &eval_object,
123 const bke::greasepencil::Layer *layer,
124 const ReprojectMode reproject_mode,
125 const float surface_offset,
126 ViewDepths *view_depths)
127 : region_(&region),
128 view3d_(&view3d),
129 depth_cache_(view_depths),
130 surface_offset_(surface_offset)
131{
132 layer_space_to_world_space_ = (layer != nullptr) ? layer->to_world_space(eval_object) :
133 eval_object.object_to_world();
134 world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
135 /* Initialize DrawingPlacementPlane from mode. */
136 switch (reproject_mode) {
139 break;
142 placement_normal_ = float3(0, 1, 0);
143 break;
146 placement_normal_ = float3(1, 0, 0);
147 break;
150 placement_normal_ = float3(0, 0, 1);
151 break;
154 placement_normal_ = scene.cursor.matrix<float3x3>() * float3(0, 0, 1);
155 break;
156 }
157 default:
158 break;
159 }
160
161 /* Account for layer transform. */
162 if (!ELEM(reproject_mode, ReprojectMode::View, ReprojectMode::Cursor)) {
163 /* Use the transpose inverse for normal. */
164 placement_normal_ = math::transform_direction(math::transpose(world_space_to_layer_space_),
165 placement_normal_);
166 }
167
168 /* Initialize DrawingPlacementDepth from mode. */
169 switch (reproject_mode) {
172 surface_offset_ = 0.0f;
173 placement_loc_ = float3(scene.cursor.location);
174 break;
177 surface_offset_ = 0.0f;
178 placement_loc_ = layer_space_to_world_space_.location();
179 break;
182 placement_loc_ = layer_space_to_world_space_.location();
183 break;
184 default:
186 surface_offset_ = 0.0f;
187 placement_loc_ = layer_space_to_world_space_.location();
188 break;
189 }
190
191 if (plane_ != DrawingPlacementPlane::View) {
192 plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
193 }
194}
195
197{
198 region_ = other.region_;
199 view3d_ = other.view3d_;
200
201 depth_ = other.depth_;
202 plane_ = other.plane_;
203
204 if (other.depth_cache_ != nullptr) {
205 depth_cache_ = static_cast<ViewDepths *>(MEM_dupallocN(other.depth_cache_));
206 depth_cache_->depths = static_cast<float *>(MEM_dupallocN(other.depth_cache_->depths));
207 }
208 use_project_only_selected_ = other.use_project_only_selected_;
209
210 surface_offset_ = other.surface_offset_;
211
212 placement_loc_ = other.placement_loc_;
213 placement_normal_ = other.placement_normal_;
214 placement_plane_ = other.placement_plane_;
215
216 layer_space_to_world_space_ = other.layer_space_to_world_space_;
217 world_space_to_layer_space_ = other.world_space_to_layer_space_;
218}
219
221{
222 region_ = other.region_;
223 view3d_ = other.view3d_;
224
225 depth_ = other.depth_;
226 plane_ = other.plane_;
227
228 std::swap(depth_cache_, other.depth_cache_);
229 use_project_only_selected_ = other.use_project_only_selected_;
230
231 surface_offset_ = other.surface_offset_;
232
233 placement_loc_ = other.placement_loc_;
234 placement_normal_ = other.placement_normal_;
235 placement_plane_ = other.placement_plane_;
236
237 layer_space_to_world_space_ = other.layer_space_to_world_space_;
238 world_space_to_layer_space_ = other.world_space_to_layer_space_;
239}
240
242{
243 if (this == &other) {
244 return *this;
245 }
246 std::destroy_at(this);
247 new (this) DrawingPlacement(other);
248 return *this;
249}
250
252{
253 if (this == &other) {
254 return *this;
255 }
256 std::destroy_at(this);
257 new (this) DrawingPlacement(std::move(other));
258 return *this;
259}
260
262{
263 if (depth_cache_ != nullptr) {
264 ED_view3d_depths_free(depth_cache_);
265 }
266}
267
272
277
279{
281
283 if (use_project_only_selected_) {
285 }
286 else {
288 }
289 }
290 ED_view3d_depth_override(depsgraph, region, view3d, nullptr, mode, false, &this->depth_cache_);
291}
292
294{
295 BLI_assert(depth_cache_ != nullptr);
296 float depth;
297 if (ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth)) {
298 float3 origin;
299 ED_view3d_depth_unproject_v3(region_, int2(co), depth, origin);
300
301 placement_loc_ = origin;
302 }
303 else {
304 /* If nothing was hit, use origin. */
305 placement_loc_ = layer_space_to_world_space_.location();
306 }
307 plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
308}
309
310float3 DrawingPlacement::project_depth(const float2 co) const
311{
312 float3 proj_point;
313 float depth;
314 if (depth_cache_ != nullptr && ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth)) {
315 ED_view3d_depth_unproject_v3(region_, int2(co), depth, proj_point);
316 float3 view_normal;
317 ED_view3d_win_to_vector(region_, co, view_normal);
318 proj_point -= view_normal * surface_offset_;
319 }
320 else {
321 /* Fallback to `View` placement. */
322 ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
323 }
324 return proj_point;
325}
326
328{
329 float3 proj_point;
330 if (depth_ == DrawingPlacementDepth::Surface) {
331 /* Project using the viewport depth cache. */
332 proj_point = this->project_depth(co);
333 }
334 else {
335 if (plane_ == DrawingPlacementPlane::View) {
336 ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
337 }
338 else {
339 ED_view3d_win_to_3d_on_plane(region_, placement_plane_, co, false, proj_point);
340 }
341 }
342 return math::transform_point(world_space_to_layer_space_, proj_point);
343}
344
346{
347 float3 proj_point;
348 if (depth_ == DrawingPlacementDepth::Surface) {
349 /* Project using the viewport depth cache. */
350 proj_point = this->project_depth(co);
351 }
352 else {
353 if (plane_ == DrawingPlacementPlane::View) {
354 ED_view3d_win_to_3d_with_shift(view3d_, region_, placement_loc_, co, proj_point);
355 }
356 else {
357 ED_view3d_win_to_3d_on_plane(region_, placement_plane_, co, false, proj_point);
358 }
359 }
360 return math::transform_point(world_space_to_layer_space_, proj_point);
361}
362
364{
365 threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
366 for (const int i : range) {
367 dst[i] = this->project(src[i]);
368 }
369 });
370}
371
373{
374 const float3 world_pos = math::transform_point(layer_space_to_world_space_, pos);
375 float3 proj_point;
376 if (depth_ == DrawingPlacementDepth::Surface) {
377 /* First project the position into view space. */
378 float2 co;
379 if (ED_view3d_project_float_global(region_, world_pos, co, V3D_PROJ_TEST_NOP)) {
380 /* Can't reproject the point. */
381 return pos;
382 }
383 /* Project using the viewport depth cache. */
384 proj_point = this->project_depth(co);
385 }
386 else {
387 /* Reproject the point onto the `placement_plane_` from the current view. */
388 RegionView3D *rv3d = static_cast<RegionView3D *>(region_->regiondata);
389
390 float3 ray_no;
391 if (rv3d->is_persp) {
392 ray_no = math::normalize(world_pos - float3(rv3d->viewinv[3]));
393 }
394 else {
395 ray_no = -float3(rv3d->viewinv[2]);
396 }
397 float4 plane;
398 if (plane_ == DrawingPlacementPlane::View) {
399 plane_from_point_normal_v3(plane, placement_loc_, rv3d->viewinv[2]);
400 }
401 else {
402 plane = placement_plane_;
403 }
404
405 float lambda;
406 if (isect_ray_plane_v3(world_pos, ray_no, plane, &lambda, false)) {
407 proj_point = world_pos + ray_no * lambda;
408 }
409 else {
410 return pos;
411 }
412 }
413 return math::transform_point(world_space_to_layer_space_, proj_point);
414}
415
417{
418 threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
419 for (const int i : range) {
420 dst[i] = this->reproject(src[i]);
421 }
422 });
423}
424
426{
427 return layer_space_to_world_space_;
428}
429
430static float get_multi_frame_falloff(const int frame_number,
431 const int center_frame,
432 const int min_frame,
433 const int max_frame,
434 const CurveMapping *falloff_curve)
435{
436 if (falloff_curve == nullptr) {
437 return 1.0f;
438 }
439
440 /* Frame right of the center frame. */
441 if (frame_number > center_frame) {
442 const float frame_factor = 0.5f * float(center_frame - min_frame) / (frame_number - min_frame);
443 return BKE_curvemapping_evaluateF(falloff_curve, 0, frame_factor);
444 }
445 /* Frame left of the center frame. */
446 if (frame_number < center_frame) {
447 const float frame_factor = 0.5f * float(center_frame - frame_number) /
448 (max_frame - frame_number);
449 return BKE_curvemapping_evaluateF(falloff_curve, 0, frame_factor + 0.5f);
450 }
451 /* Frame at center. */
452 return BKE_curvemapping_evaluateF(falloff_curve, 0, 0.5f);
453}
454
455static std::pair<int, int> get_minmax_selected_frame_numbers(const GreasePencil &grease_pencil,
456 const int current_frame)
457{
458 using namespace blender::bke::greasepencil;
459 int frame_min = current_frame;
460 int frame_max = current_frame;
461 Span<const Layer *> layers = grease_pencil.layers();
462 for (const int layer_i : layers.index_range()) {
463 const Layer &layer = *layers[layer_i];
464 if (!layer.is_editable()) {
465 continue;
466 }
467 for (const auto [frame_number, frame] : layer.frames().items()) {
468 if (frame_number != current_frame && frame.is_selected()) {
469 frame_min = math::min(frame_min, frame_number);
470 frame_max = math::max(frame_max, frame_number);
471 }
472 }
473 }
474 return std::pair<int, int>(frame_min, frame_max);
475}
476
477static std::optional<int> get_frame_id(const bke::greasepencil::Layer &layer,
478 const GreasePencilFrame &frame,
479 const int frame_number,
480 const int frame_index,
481 const int current_frame,
482 const int current_frame_index,
483 const int last_frame,
484 const int last_frame_index,
485 const bool use_multi_frame_editing,
486 const bool do_onion_skinning,
487 const bool is_before_first,
488 const GreasePencilOnionSkinningSettings onion_settings)
489{
490 if (use_multi_frame_editing) {
491 if (frame.is_selected()) {
492 if (do_onion_skinning) {
493 return (frame_number < current_frame) ? -1 : 1;
494 }
495 return 0;
496 }
497 return {};
498 }
499 if (do_onion_skinning && layer.use_onion_skinning()) {
500 /* Keyframe type filter. */
501 if (onion_settings.filter != 0 && (onion_settings.filter & (1 << frame.type)) == 0) {
502 return {};
503 }
504 /* Selected mode filter. */
505 if (onion_settings.mode == GP_ONION_SKINNING_MODE_SELECTED && !frame.is_selected()) {
506 return {};
507 }
508
509 int delta = 0;
510 if (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) {
511 delta = frame_number - current_frame;
512 }
513 else {
514 delta = frame_index - current_frame_index;
515 }
516
517 if (is_before_first) {
518 delta++;
519 }
520 if ((onion_settings.flag & GP_ONION_SKINNING_SHOW_LOOP) != 0 &&
521 (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after))
522 {
523 /* We wrap the value using the last frame and 0 as reference. */
524 /* FIXME: This might not be good for animations not starting at 0. */
525 int shift = 0;
526 if (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) {
527 shift = last_frame;
528 }
529 else {
530 shift = last_frame_index;
531 }
532 delta += (delta < 0) ? (shift + 1) : -(shift + 1);
533 }
534 /* Frame range filter. */
535 if (ELEM(onion_settings.mode,
538 (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after))
539 {
540 return {};
541 }
542
543 return delta;
544 }
545 return {};
546}
547
549 const GreasePencil &grease_pencil,
550 const bke::greasepencil::Layer &layer,
551 const int current_frame,
552 const bool use_multi_frame_editing,
553 const bool do_onion_skinning)
554{
555 GreasePencilOnionSkinningSettings onion_settings = grease_pencil.onion_skinning_settings;
556 Vector<std::pair<int, int>> frame_numbers;
557 const Span<int> sorted_keys = layer.sorted_keys();
558 if (sorted_keys.is_empty()) {
559 return {};
560 }
561 const int current_frame_index = std::max(layer.sorted_keys_index_at(current_frame), 0);
562 const int last_frame = sorted_keys.last();
563 const int last_frame_index = sorted_keys.index_range().last();
564 const bool is_before_first = (current_frame < sorted_keys.first());
565 const std::optional<int> current_start_frame = layer.start_frame_at(current_frame);
566 for (const int frame_i : sorted_keys.index_range()) {
567 const int frame_number = sorted_keys[frame_i];
568 if (current_start_frame && *current_start_frame == frame_number) {
569 continue;
570 }
571 const GreasePencilFrame &frame = layer.frames().lookup(frame_number);
572 const std::optional<int> frame_id = get_frame_id(layer,
573 frame,
574 frame_number,
575 frame_i,
576 current_frame,
577 current_frame_index,
578 last_frame,
579 last_frame_index,
580 use_multi_frame_editing,
581 do_onion_skinning,
582 is_before_first,
583 onion_settings);
584 if (!frame_id.has_value()) {
585 /* Drawing on this frame is not visible. */
586 continue;
587 }
588
589 frame_numbers.append({frame_number, *frame_id});
590 }
591
592 frame_numbers.append({current_frame, 0});
593
594 return frame_numbers.as_span();
595}
596
598 const bke::greasepencil::Layer &layer,
599 const int current_frame,
600 const bool use_multi_frame_editing)
601{
602 using namespace blender::bke::greasepencil;
603 Vector<int> frame_numbers;
604 Set<const Drawing *> added_drawings;
605 if (use_multi_frame_editing) {
606 const Drawing *current_drawing = grease_pencil.get_drawing_at(layer, current_frame);
607 for (const auto [frame_number, frame] : layer.frames().items()) {
608 if (!frame.is_selected()) {
609 continue;
610 }
611 frame_numbers.append(frame_number);
612 added_drawings.add(grease_pencil.get_drawing_at(layer, frame_number));
613 }
614 if (added_drawings.contains(current_drawing)) {
615 return frame_numbers.as_span();
616 }
617 }
618
619 frame_numbers.append(current_frame);
620 return frame_numbers.as_span();
621}
622
624 GreasePencil &grease_pencil)
625{
626 using namespace blender::bke::greasepencil;
627 const int current_frame = scene.r.cfra;
628 const ToolSettings *toolsettings = scene.toolsettings;
629 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
631
632 Vector<MutableDrawingInfo> editable_drawings;
633 Span<const Layer *> layers = grease_pencil.layers();
634 for (const int layer_i : layers.index_range()) {
635 const Layer &layer = *layers[layer_i];
636 if (!layer.is_editable()) {
637 continue;
638 }
639 const Array<int> frame_numbers = get_editable_frames_for_layer(
640 grease_pencil, layer, current_frame, use_multi_frame_editing);
641 for (const int frame_number : frame_numbers) {
642 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
643 editable_drawings.append({*drawing, layer_i, frame_number, 1.0f});
644 }
645 }
646 }
647
648 return editable_drawings;
649}
650
652 GreasePencil &grease_pencil)
653{
654 using namespace blender::bke::greasepencil;
655 const int current_frame = scene.r.cfra;
656 const ToolSettings *toolsettings = scene.toolsettings;
657 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
659 const bool use_multi_frame_falloff = use_multi_frame_editing &&
660 (toolsettings->gp_sculpt.flag &
662 int center_frame;
663 std::pair<int, int> minmax_frame;
664 if (use_multi_frame_falloff) {
666 minmax_frame = get_minmax_selected_frame_numbers(grease_pencil, current_frame);
667 center_frame = math::clamp(current_frame, minmax_frame.first, minmax_frame.second);
668 }
669
670 Vector<MutableDrawingInfo> editable_drawings;
671 Span<const Layer *> layers = grease_pencil.layers();
672 for (const int layer_i : layers.index_range()) {
673 const Layer &layer = *layers[layer_i];
674 if (!layer.is_editable()) {
675 continue;
676 }
677 const Array<int> frame_numbers = get_editable_frames_for_layer(
678 grease_pencil, layer, current_frame, use_multi_frame_editing);
679 for (const int frame_number : frame_numbers) {
680 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
681 const float falloff = use_multi_frame_falloff ?
682 get_multi_frame_falloff(frame_number,
683 center_frame,
684 minmax_frame.first,
685 minmax_frame.second,
686 toolsettings->gp_sculpt.cur_falloff) :
687 1.0f;
688 editable_drawings.append({*drawing, layer_i, frame_number, falloff});
689 }
690 }
691 }
692
693 return editable_drawings;
694}
695
697 const Scene &scene, GreasePencil &grease_pencil)
698{
699 using namespace blender::bke::greasepencil;
700 int current_frame = scene.r.cfra;
701 const ToolSettings *toolsettings = scene.toolsettings;
702 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
704 const bool use_multi_frame_falloff = use_multi_frame_editing &&
705 (toolsettings->gp_sculpt.flag &
707 if (use_multi_frame_falloff) {
709 }
710
711 /* Get a set of unique frame numbers with editable drawings on them. */
712 VectorSet<int> selected_frames;
713 int frame_min = current_frame, frame_max = current_frame;
714 Span<const Layer *> layers = grease_pencil.layers();
715 if (use_multi_frame_editing) {
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 for (const auto [frame_number, frame] : layer.frames().items()) {
722 if (frame_number != current_frame && frame.is_selected()) {
723 selected_frames.add(frame_number);
724 frame_min = math::min(frame_min, frame_number);
725 frame_max = math::max(frame_max, frame_number);
726 }
727 }
728 }
729 }
730 selected_frames.add(current_frame);
731
732 /* Get multi frame falloff factor per selected frame. */
733 Array<float> falloff_per_selected_frame(selected_frames.size(), 1.0f);
734 if (use_multi_frame_falloff) {
735 int frame_group = 0;
736 for (const int frame_number : selected_frames) {
737 falloff_per_selected_frame[frame_group] = get_multi_frame_falloff(
738 frame_number, current_frame, frame_min, frame_max, toolsettings->gp_sculpt.cur_falloff);
739 frame_group++;
740 }
741 }
742
743 /* Get drawings grouped per frame. */
744 Array<Vector<MutableDrawingInfo>> drawings_grouped_per_frame(selected_frames.size());
745 Set<const Drawing *> added_drawings;
746 for (const int layer_i : layers.index_range()) {
747 const Layer &layer = *layers[layer_i];
748 if (!layer.is_editable()) {
749 continue;
750 }
751 /* In multi frame editing mode, add drawings at selected frames. */
752 if (use_multi_frame_editing) {
753 for (const auto [frame_number, frame] : layer.frames().items()) {
754 Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number);
755 if (!frame.is_selected() || drawing == nullptr || added_drawings.contains(drawing)) {
756 continue;
757 }
758 const int frame_group = selected_frames.index_of(frame_number);
759 drawings_grouped_per_frame[frame_group].append(
760 {*drawing, layer_i, frame_number, falloff_per_selected_frame[frame_group]});
761 added_drawings.add_new(drawing);
762 }
763 }
764
765 /* Add drawing at current frame. */
766 Drawing *current_drawing = grease_pencil.get_drawing_at(layer, current_frame);
767 if (current_drawing != nullptr && !added_drawings.contains(current_drawing)) {
768 const int frame_group = selected_frames.index_of(current_frame);
769 drawings_grouped_per_frame[frame_group].append(
770 {*current_drawing, layer_i, current_frame, falloff_per_selected_frame[frame_group]});
771 added_drawings.add_new(current_drawing);
772 }
773 }
774
775 return drawings_grouped_per_frame;
776}
777
779 const Scene &scene,
780 GreasePencil &grease_pencil,
782{
783 using namespace blender::bke::greasepencil;
784 const int current_frame = scene.r.cfra;
785 const ToolSettings *toolsettings = scene.toolsettings;
786 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
788 const int layer_index = *grease_pencil.get_layer_index(layer);
789
790 Vector<MutableDrawingInfo> editable_drawings;
791 const Array<int> frame_numbers = get_editable_frames_for_layer(
792 grease_pencil, layer, current_frame, use_multi_frame_editing);
793 for (const int frame_number : frame_numbers) {
794 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
795 editable_drawings.append({*drawing, layer_index, frame_number, 1.0f});
796 }
797 }
798
799 return editable_drawings;
800}
801
803 const Scene &scene,
804 GreasePencil &grease_pencil,
806{
807 using namespace blender::bke::greasepencil;
808 const int current_frame = scene.r.cfra;
809 const ToolSettings *toolsettings = scene.toolsettings;
810 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
812 const bool use_multi_frame_falloff = use_multi_frame_editing &&
813 (toolsettings->gp_sculpt.flag &
815 const int layer_index = *grease_pencil.get_layer_index(layer);
816 int center_frame;
817 std::pair<int, int> minmax_frame;
818 if (use_multi_frame_falloff) {
820 minmax_frame = get_minmax_selected_frame_numbers(grease_pencil, current_frame);
821 center_frame = math::clamp(current_frame, minmax_frame.first, minmax_frame.second);
822 }
823
824 Vector<MutableDrawingInfo> editable_drawings;
825 const Array<int> frame_numbers = get_editable_frames_for_layer(
826 grease_pencil, layer, current_frame, use_multi_frame_editing);
827 for (const int frame_number : frame_numbers) {
828 if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
829 const float falloff = use_multi_frame_falloff ?
830 get_multi_frame_falloff(frame_number,
831 center_frame,
832 minmax_frame.first,
833 minmax_frame.second,
834 toolsettings->gp_sculpt.cur_falloff) :
835 1.0f;
836 editable_drawings.append({*drawing, layer_index, frame_number, falloff});
837 }
838 }
839
840 return editable_drawings;
841}
842
844 const GreasePencil &grease_pencil,
845 const bool do_onion_skinning)
846{
847 using namespace blender::bke::greasepencil;
848 const int current_frame = scene.r.cfra;
849 const ToolSettings *toolsettings = scene.toolsettings;
850 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
852
853 Vector<DrawingInfo> visible_drawings;
854 Span<const Layer *> layers = grease_pencil.layers();
855 for (const int layer_i : layers.index_range()) {
856 const Layer &layer = *layers[layer_i];
857 if (!layer.is_visible()) {
858 continue;
859 }
861 grease_pencil, layer, current_frame, use_multi_frame_editing, do_onion_skinning);
862 for (const auto &[frame_number, onion_id] : frames) {
863 if (const Drawing *drawing = grease_pencil.get_drawing_at(layer, frame_number)) {
864 visible_drawings.append({*drawing, layer_i, frame_number, onion_id});
865 }
866 }
867 }
868
869 return visible_drawings;
870}
871
873{
874 BLI_assert(object.type == OB_GREASE_PENCIL);
875 VectorSet<int> editable_material_indices;
876 for (const int mat_i : IndexRange(object.totcol)) {
877 Material *material = BKE_object_material_get(&object, mat_i + 1);
878 /* The editable materials are unlocked and not hidden. */
879 if (material != nullptr && material->gp_style != nullptr &&
880 (material->gp_style->flag & GP_MATERIAL_LOCKED) == 0 &&
881 (material->gp_style->flag & GP_MATERIAL_HIDE) == 0)
882 {
883 editable_material_indices.add_new(mat_i);
884 }
885 }
886 return editable_material_indices;
887}
888
890{
891 BLI_assert(object.type == OB_GREASE_PENCIL);
892 VectorSet<int> hidden_material_indices;
893 for (const int mat_i : IndexRange(object.totcol)) {
894 Material *material = BKE_object_material_get(&object, mat_i + 1);
895 if (material != nullptr && material->gp_style != nullptr &&
896 (material->gp_style->flag & GP_MATERIAL_HIDE) != 0)
897 {
898 hidden_material_indices.add_new(mat_i);
899 }
900 }
901 return hidden_material_indices;
902}
903
905{
906 BLI_assert(object.type == OB_GREASE_PENCIL);
907 VectorSet<int> fill_material_indices;
908 for (const int mat_i : IndexRange(object.totcol)) {
909 Material *material = BKE_object_material_get(&object, mat_i + 1);
910 if (material != nullptr && material->gp_style != nullptr &&
911 (material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0)
912 {
913 fill_material_indices.add_new(mat_i);
914 }
915 }
916 return fill_material_indices;
917}
918
920 const bke::greasepencil::Drawing &drawing,
921 int layer_index,
922 IndexMaskMemory &memory)
923{
924 using namespace blender;
925 const bke::CurvesGeometry &curves = drawing.strokes();
926 const IndexRange curves_range = curves.curves_range();
927
928 if (object.totcol == 0) {
929 return IndexMask(curves_range);
930 }
931
932 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
933 const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
934
935 /* If we're not using material locking, the entire curves range is editable. */
936 if (layer.ignore_locked_materials()) {
937 return IndexMask(curves_range);
938 }
939
940 /* Get all the editable material indices */
941 VectorSet<int> editable_material_indices = get_editable_material_indices(object);
942 if (editable_material_indices.is_empty()) {
943 return {};
944 }
945
946 const bke::AttributeAccessor attributes = curves.attributes();
947 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
948 if (!materials) {
949 /* If the attribute does not exist then the default is the first material. */
950 if (editable_material_indices.contains(0)) {
951 return curves_range;
952 }
953 return {};
954 }
955 /* Get all the strokes that have their material unlocked. */
957 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
958 return editable_material_indices.contains(materials[curve_i]);
959 });
960}
961
963 const bke::greasepencil::Drawing &drawing,
964 int layer_index,
965 IndexMaskMemory &memory)
966{
967 using namespace blender;
968 const IndexMask editable_strokes = retrieve_editable_strokes(
969 object, drawing, layer_index, memory);
970 if (editable_strokes.is_empty()) {
971 return {};
972 }
973
974 const bke::CurvesGeometry &curves = drawing.strokes();
975 const IndexRange curves_range = curves.curves_range();
976
977 const bke::AttributeAccessor attributes = curves.attributes();
978 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
979 const VectorSet<int> fill_material_indices = get_fill_material_indices(object);
980 if (!materials) {
981 /* If the attribute does not exist then the default is the first material. */
982 if (editable_strokes.contains(0) && fill_material_indices.contains(0)) {
983 return curves_range;
984 }
985 return {};
986 }
988 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
989 const int material_index = materials[curve_i];
990 return fill_material_indices.contains(material_index);
991 });
992 return IndexMask::from_intersection(editable_strokes, fill_strokes, memory);
993}
994
996 const bke::greasepencil::Drawing &drawing,
997 const int mat_i,
998 IndexMaskMemory &memory)
999{
1000 using namespace blender;
1001
1002 /* Get all the editable material indices */
1003 VectorSet<int> editable_material_indices = get_editable_material_indices(object);
1004 if (editable_material_indices.is_empty()) {
1005 return {};
1006 }
1007
1008 const bke::CurvesGeometry &curves = drawing.strokes();
1009 const IndexRange curves_range = drawing.strokes().curves_range();
1010 const bke::AttributeAccessor attributes = curves.attributes();
1011
1012 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
1013 if (!materials) {
1014 /* If the attribute does not exist then the default is the first material. */
1015 if (editable_material_indices.contains(0)) {
1016 return curves_range;
1017 }
1018 return {};
1019 }
1020 /* Get all the strokes that share the same material and have it unlocked. */
1022 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1023 const int material_index = materials[curve_i];
1024 if (material_index == mat_i) {
1025 return editable_material_indices.contains(material_index);
1026 }
1027 return false;
1028 });
1029}
1030
1032 const bke::greasepencil::Drawing &drawing,
1033 int layer_index,
1034 IndexMaskMemory &memory)
1035{
1036 const bke::CurvesGeometry &curves = drawing.strokes();
1037 const IndexRange points_range = curves.points_range();
1038
1039 if (object.totcol == 0) {
1040 return IndexMask(points_range);
1041 }
1042
1043 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1044 const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
1045
1046 /* If we're not using material locking, the entire points range is editable. */
1047 if (layer.ignore_locked_materials()) {
1048 return IndexMask(points_range);
1049 }
1050
1051 /* Get all the editable material indices */
1052 VectorSet<int> editable_material_indices = get_editable_material_indices(object);
1053 if (editable_material_indices.is_empty()) {
1054 return {};
1055 }
1056
1057 /* Propagate the material index to the points. */
1058 const bke::AttributeAccessor attributes = curves.attributes();
1059 const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Point);
1060 if (!materials) {
1061 /* If the attribute does not exist then the default is the first material. */
1062 if (editable_material_indices.contains(0)) {
1063 return points_range;
1064 }
1065 return {};
1066 }
1067 /* Get all the points that are part of a stroke with an unlocked material. */
1069 points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
1070 return editable_material_indices.contains(materials[point_i]);
1071 });
1072}
1073
1075 const MutableDrawingInfo &info,
1076 const bke::AttrDomain selection_domain,
1077 IndexMaskMemory &memory)
1078{
1079
1080 const bke::greasepencil::Drawing &drawing = info.drawing;
1081 if (selection_domain == bke::AttrDomain::Curve) {
1082 return ed::greasepencil::retrieve_editable_strokes(object, drawing, info.layer_index, memory);
1083 }
1084 else if (selection_domain == bke::AttrDomain::Point) {
1085 return ed::greasepencil::retrieve_editable_points(object, drawing, info.layer_index, memory);
1086 }
1087 return {};
1088}
1089
1091 const bke::greasepencil::Drawing &drawing,
1092 IndexMaskMemory &memory)
1093{
1094 using namespace blender;
1095
1096 /* Get all the hidden material indices. */
1097 VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
1098
1099 if (hidden_material_indices.is_empty()) {
1100 return drawing.strokes().curves_range();
1101 }
1102
1103 const bke::CurvesGeometry &curves = drawing.strokes();
1104 const IndexRange curves_range = drawing.strokes().curves_range();
1105 const bke::AttributeAccessor attributes = curves.attributes();
1106
1107 /* Get all the strokes that have their material visible. */
1108 const VArray<int> materials = *attributes.lookup_or_default<int>(
1109 "material_index", bke::AttrDomain::Curve, 0);
1111 curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
1112 const int material_index = materials[curve_i];
1113 return !hidden_material_indices.contains(material_index);
1114 });
1115}
1116
1118 const bke::greasepencil::Drawing &drawing,
1119 IndexMaskMemory &memory)
1120{
1121 /* Get all the hidden material indices. */
1122 VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
1123
1124 if (hidden_material_indices.is_empty()) {
1125 return drawing.strokes().points_range();
1126 }
1127
1128 const bke::CurvesGeometry &curves = drawing.strokes();
1129 const IndexRange points_range = curves.points_range();
1130 const bke::AttributeAccessor attributes = curves.attributes();
1131
1132 /* Propagate the material index to the points. */
1133 const VArray<int> materials = *attributes.lookup_or_default<int>(
1134 "material_index", bke::AttrDomain::Point, 0);
1135 if (const std::optional<int> single_material = materials.get_if_single()) {
1136 if (!hidden_material_indices.contains(*single_material)) {
1137 return points_range;
1138 }
1139 return {};
1140 }
1141
1142 /* Get all the points that are part of a stroke with a visible material. */
1144 points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
1145 const int material_index = materials[point_i];
1146 return !hidden_material_indices.contains(material_index);
1147 });
1148}
1149
1151 const bke::greasepencil::Drawing &drawing,
1152 const int layer_index,
1153 IndexMaskMemory &memory)
1154{
1155 const bke::CurvesGeometry &curves = drawing.strokes();
1156
1157 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
1158 return IndexMask(0);
1159 }
1160
1161 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1162 const VArray<int8_t> types = curves.curve_types();
1163
1164 const VArray<bool> selected_point = *curves.attributes().lookup_or_default<bool>(
1165 ".selection", bke::AttrDomain::Point, true);
1166 const VArray<bool> selected_left = *curves.attributes().lookup_or_default<bool>(
1167 ".selection_handle_left", bke::AttrDomain::Point, true);
1168 const VArray<bool> selected_right = *curves.attributes().lookup_or_default<bool>(
1169 ".selection_handle_right", bke::AttrDomain::Point, true);
1170
1172 object, drawing, layer_index, memory);
1173
1174 const IndexMask selected_points = IndexMask::from_predicate(
1175 curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
1176 const bool is_selected = selected_point[point_i] || selected_left[point_i] ||
1177 selected_right[point_i];
1178 const bool is_bezier = types[point_to_curve_map[point_i]] == CURVE_TYPE_BEZIER;
1179 return is_selected && is_bezier;
1180 });
1181
1182 return IndexMask::from_intersection(editable_points, selected_points, memory);
1183}
1184
1186 const bke::greasepencil::Drawing &drawing,
1187 const int layer_index,
1188 const bke::AttrDomain selection_domain,
1189 IndexMaskMemory &memory)
1190{
1191 if (selection_domain == bke::AttrDomain::Curve) {
1193 object, drawing, layer_index, memory);
1194 }
1195 else if (selection_domain == bke::AttrDomain::Point) {
1197 object, drawing, layer_index, memory);
1198 }
1199 return {};
1200}
1201
1203 const bke::greasepencil::Drawing &drawing,
1204 int layer_index,
1205 IndexMaskMemory &memory)
1206{
1207 using namespace blender;
1208 const bke::CurvesGeometry &curves = drawing.strokes();
1209
1210 const IndexMask editable_strokes = retrieve_editable_strokes(
1211 object, drawing, layer_index, memory);
1212 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1213
1214 return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
1215}
1216
1218 const bke::greasepencil::Drawing &drawing,
1219 int layer_index,
1220 IndexMaskMemory &memory)
1221{
1222 using namespace blender;
1223 const bke::CurvesGeometry &curves = drawing.strokes();
1224
1225 const IndexMask editable_strokes = retrieve_editable_fill_strokes(
1226 object, drawing, layer_index, memory);
1227 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
1228
1229 return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
1230}
1231
1233 const bke::greasepencil::Drawing &drawing,
1234 int layer_index,
1235 IndexMaskMemory &memory)
1236{
1237 const bke::CurvesGeometry &curves = drawing.strokes();
1238
1239 const IndexMask editable_points = retrieve_editable_points(object, drawing, layer_index, memory);
1240 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
1241
1242 return IndexMask::from_intersection(editable_points, selected_points, memory);
1243}
1244
1246 const bke::greasepencil::Drawing &drawing,
1247 int layer_index,
1248 const bke::AttrDomain selection_domain,
1249 IndexMaskMemory &memory)
1250{
1251 if (selection_domain == bke::AttrDomain::Curve) {
1253 object, drawing, layer_index, memory);
1254 }
1255 else if (selection_domain == bke::AttrDomain::Point) {
1257 object, drawing, layer_index, memory);
1258 }
1259 return {};
1260}
1261
1263 const bke::CurvesGeometry &src,
1265 const Span<Vector<PointTransferData>> src_to_dst_points,
1266 const bool keep_caps)
1267{
1268 const int src_curves_num = src.curves_num();
1269 const OffsetIndices<int> src_points_by_curve = src.points_by_curve();
1270 const VArray<bool> src_cyclic = src.cyclic();
1271
1272 int dst_points_num = 0;
1273 for (const Vector<PointTransferData> &src_transfer_data : src_to_dst_points) {
1274 dst_points_num += src_transfer_data.size();
1275 }
1276 if (dst_points_num == 0) {
1277 dst.resize(0, 0);
1278 return Array<PointTransferData>(0);
1279 }
1280
1281 /* Set the intersection parameters in the destination domain : a pair of int and float
1282 * numbers for which the integer is the index of the corresponding segment in the
1283 * source curves, and the float part is the (0,1) factor representing its position in
1284 * the segment.
1285 */
1286 Array<PointTransferData> dst_transfer_data(dst_points_num);
1287
1288 Array<int> src_pivot_point(src_curves_num, -1);
1289 Array<int> dst_interm_curves_offsets(src_curves_num + 1, 0);
1290 int dst_point = -1;
1291 for (const int src_curve : src.curves_range()) {
1292 const IndexRange src_points = src_points_by_curve[src_curve];
1293
1294 for (const int src_point : src_points) {
1295 for (const PointTransferData &dst_point_transfer : src_to_dst_points[src_point]) {
1296 if (dst_point_transfer.is_src_point) {
1297 dst_transfer_data[++dst_point] = dst_point_transfer;
1298 continue;
1299 }
1300
1301 /* Add an intersection with the eraser and mark it as a cut. */
1302 dst_transfer_data[++dst_point] = dst_point_transfer;
1303
1304 /* For cyclic curves, mark the pivot point as the last intersection with the eraser
1305 * that starts a new segment in the destination.
1306 */
1307 if (src_cyclic[src_curve] && dst_point_transfer.is_cut) {
1308 src_pivot_point[src_curve] = dst_point;
1309 }
1310 }
1311 }
1312 /* We store intermediate curve offsets represent an intermediate state of the
1313 * destination curves before cutting the curves at eraser's intersection. Thus, it
1314 * contains the same number of curves than in the source, but the offsets are
1315 * different, because points may have been added or removed. */
1316 dst_interm_curves_offsets[src_curve + 1] = dst_point + 1;
1317 }
1318
1319 /* Cyclic curves. */
1320 Array<bool> src_now_cyclic(src_curves_num);
1321 threading::parallel_for(src.curves_range(), 4096, [&](const IndexRange src_curves) {
1322 for (const int src_curve : src_curves) {
1323 const int pivot_point = src_pivot_point[src_curve];
1324
1325 if (pivot_point == -1) {
1326 /* Either the curve was not cyclic or it wasn't cut : no need to change it. */
1327 src_now_cyclic[src_curve] = src_cyclic[src_curve];
1328 continue;
1329 }
1330
1331 /* A cyclic curve was cut :
1332 * - this curve is not cyclic anymore,
1333 * - and we have to shift points to keep the closing segment.
1334 */
1335 src_now_cyclic[src_curve] = false;
1336
1337 const int dst_interm_first = dst_interm_curves_offsets[src_curve];
1338 const int dst_interm_last = dst_interm_curves_offsets[src_curve + 1];
1339 std::rotate(dst_transfer_data.begin() + dst_interm_first,
1340 dst_transfer_data.begin() + pivot_point,
1341 dst_transfer_data.begin() + dst_interm_last);
1342 }
1343 });
1344
1345 /* Compute the destination curve offsets. */
1346 Vector<int> dst_curves_offset;
1347 Vector<int> dst_to_src_curve;
1348 dst_curves_offset.append(0);
1349 for (int src_curve : src.curves_range()) {
1350 const IndexRange dst_points(dst_interm_curves_offsets[src_curve],
1351 dst_interm_curves_offsets[src_curve + 1] -
1352 dst_interm_curves_offsets[src_curve]);
1353 int length_of_current = 0;
1354
1355 for (int dst_point : dst_points) {
1356
1357 if ((length_of_current > 0) && dst_transfer_data[dst_point].is_cut) {
1358 /* This is the new first point of a curve. */
1359 dst_curves_offset.append(dst_point);
1360 dst_to_src_curve.append(src_curve);
1361 length_of_current = 0;
1362 }
1363 ++length_of_current;
1364 }
1365
1366 if (length_of_current != 0) {
1367 /* End of a source curve. */
1368 dst_curves_offset.append(dst_points.one_after_last());
1369 dst_to_src_curve.append(src_curve);
1370 }
1371 }
1372 const int dst_curves_num = dst_curves_offset.size() - 1;
1373 if (dst_curves_num == 0) {
1374 dst.resize(0, 0);
1375 return dst_transfer_data;
1376 }
1377
1378 /* Build destination curves geometry. */
1379 dst.resize(dst_points_num, dst_curves_num);
1380 array_utils::copy(dst_curves_offset.as_span(), dst.offsets_for_write());
1381 const OffsetIndices<int> dst_points_by_curve = dst.points_by_curve();
1382
1383 /* Attributes. */
1384 const bke::AttributeAccessor src_attributes = src.attributes();
1385 bke::MutableAttributeAccessor dst_attributes = dst.attributes_for_write();
1386
1387 /* Copy curves attributes. */
1388 bke::gather_attributes(src_attributes,
1392 dst_to_src_curve,
1393 dst_attributes);
1394 if (src_cyclic.get_if_single().value_or(true)) {
1396 src_now_cyclic.as_span(), dst_to_src_curve.as_span(), dst.cyclic_for_write());
1397 }
1398
1399 dst.update_curve_types();
1400
1401 /* Display intersections with flat caps. */
1402 if (!keep_caps) {
1403 bke::SpanAttributeWriter<int8_t> dst_start_caps =
1404 dst_attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve);
1405 bke::SpanAttributeWriter<int8_t> dst_end_caps =
1407
1408 threading::parallel_for(dst.curves_range(), 4096, [&](const IndexRange dst_curves) {
1409 for (const int dst_curve : dst_curves) {
1410 const IndexRange dst_curve_points = dst_points_by_curve[dst_curve];
1411 const PointTransferData &start_point_transfer =
1412 dst_transfer_data[dst_curve_points.first()];
1413 const PointTransferData &end_point_transfer = dst_transfer_data[dst_curve_points.last()];
1414
1415 if (start_point_transfer.is_cut) {
1416 dst_start_caps.span[dst_curve] = GP_STROKE_CAP_TYPE_FLAT;
1417 }
1418 /* The is_cut flag does not work for end points, but any end point that isn't the source
1419 * point must also be a cut. */
1420 if (!end_point_transfer.is_src_end_point()) {
1421 dst_end_caps.span[dst_curve] = GP_STROKE_CAP_TYPE_FLAT;
1422 }
1423 }
1424 });
1425
1426 dst_start_caps.finish();
1427 dst_end_caps.finish();
1428 }
1429
1430 /* Copy/Interpolate point attributes. */
1431 for (bke::AttributeTransferData &attribute : bke::retrieve_attributes_for_transfer(
1432 src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT))
1433 {
1434 bke::attribute_math::convert_to_static_type(attribute.dst.span.type(), [&](auto dummy) {
1435 using T = decltype(dummy);
1436 auto src_attr = attribute.src.typed<T>();
1437 auto dst_attr = attribute.dst.span.typed<T>();
1438
1439 threading::parallel_for(dst.points_range(), 4096, [&](const IndexRange dst_points) {
1440 for (const int dst_point : dst_points) {
1441 const PointTransferData &point_transfer = dst_transfer_data[dst_point];
1442 if (point_transfer.is_src_point) {
1443 dst_attr[dst_point] = src_attr[point_transfer.src_point];
1444 }
1445 else {
1446 dst_attr[dst_point] = bke::attribute_math::mix2<T>(
1447 point_transfer.factor,
1448 src_attr[point_transfer.src_point],
1449 src_attr[point_transfer.src_next_point]);
1450 }
1451 }
1452 });
1453
1454 attribute.dst.finish();
1455 });
1456 }
1457
1458 return dst_transfer_data;
1459}
1460
1462 const ARegion *region,
1463 const float3 center,
1464 const float4x4 to_world,
1465 const float pixel_radius)
1466{
1467 const float2 xy_delta = float2(pixel_radius, 0.0f);
1468 const float3 loc = math::transform_point(to_world, center);
1469
1470 const float zfac = ED_view3d_calc_zfac(rv3d, loc);
1471 float3 delta;
1472 ED_view3d_win_to_delta(region, xy_delta, zfac, delta);
1473
1474 const float scale = math::length(
1476
1477 return math::safe_divide(math::length(delta), scale);
1478}
1479
1481 const ARegion *region,
1482 const Brush *brush,
1483 const float3 location,
1484 const float4x4 to_world)
1485{
1486 if ((brush->flag & BRUSH_LOCK_SIZE) == 0) {
1487 return pixel_radius_to_world_space_radius(rv3d, region, location, to_world, brush->size);
1488 }
1489 return brush->unprojected_radius;
1490}
1491
1493 const ARegion *region,
1494 const Brush *brush,
1495 const float pressure,
1496 const float3 location,
1497 const float4x4 to_world,
1498 const BrushGpencilSettings *settings)
1499{
1500 float radius = brush_radius_at_location(rv3d, region, brush, location, to_world);
1501 if (BKE_brush_use_size_pressure(brush)) {
1502 radius *= BKE_curvemapping_evaluateF(settings->curve_sensitivity, 0, pressure);
1503 }
1504 return radius;
1505}
1506
1507float opacity_from_input_sample(const float pressure,
1508 const Brush *brush,
1509 const BrushGpencilSettings *settings)
1510{
1511 float opacity = brush->alpha;
1512 if (BKE_brush_use_alpha_pressure(brush)) {
1513 opacity *= BKE_curvemapping_evaluateF(settings->curve_strength, 0, pressure);
1514 }
1515 return opacity;
1516}
1517
1519 wmOperator *op,
1520 const bool use_duplicate_previous_key)
1521{
1522 const Scene *scene = CTX_data_scene(C);
1523 const Object *object = CTX_data_active_object(C);
1524 if (!object || object->type != OB_GREASE_PENCIL) {
1525 return OPERATOR_CANCELLED;
1526 }
1527
1528 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1529 if (!grease_pencil.has_active_layer()) {
1530 BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
1531 return OPERATOR_CANCELLED;
1532 }
1533
1535 const Brush *brush = BKE_paint_brush_for_read(paint);
1536 if (brush == nullptr) {
1537 return OPERATOR_CANCELLED;
1538 }
1539
1540 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
1541
1542 if (!active_layer.is_editable()) {
1543 BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
1544 return OPERATOR_CANCELLED;
1545 }
1546
1547 /* Ensure a drawing at the current keyframe. */
1548 bool inserted_keyframe = false;
1550 *scene, grease_pencil, active_layer, use_duplicate_previous_key, inserted_keyframe))
1551 {
1552 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
1553 return OPERATOR_CANCELLED;
1554 }
1555 if (inserted_keyframe) {
1557 }
1559}
1560
1562 const ARegion *region,
1563 const float2 &mouse,
1564 const DrawingPlacement &placement)
1565{
1566 float3 u_dir;
1567 float3 v_dir;
1568 /* Set the texture space origin to be the first point. */
1569 float3 origin = placement.project(mouse);
1570 /* Align texture with the drawing plane. */
1571 switch (scene->toolsettings->gp_sculpt.lock_axis) {
1572 case GP_LOCKAXIS_VIEW:
1573 u_dir = math::normalize(placement.project(float2(region->winx, 0.0f) + mouse) - origin);
1574 v_dir = math::normalize(placement.project(float2(0.0f, region->winy) + mouse) - origin);
1575 break;
1576 case GP_LOCKAXIS_Y:
1577 u_dir = float3(1.0f, 0.0f, 0.0f);
1578 v_dir = float3(0.0f, 0.0f, 1.0f);
1579 break;
1580 case GP_LOCKAXIS_X:
1581 u_dir = float3(0.0f, 1.0f, 0.0f);
1582 v_dir = float3(0.0f, 0.0f, 1.0f);
1583 break;
1584 case GP_LOCKAXIS_Z:
1585 u_dir = float3(1.0f, 0.0f, 0.0f);
1586 v_dir = float3(0.0f, 1.0f, 0.0f);
1587 break;
1588 case GP_LOCKAXIS_CURSOR: {
1589 const float3x3 mat = scene->cursor.matrix<float3x3>();
1590 u_dir = mat * float3(1.0f, 0.0f, 0.0f);
1591 v_dir = mat * float3(0.0f, 1.0f, 0.0f);
1592 origin = float3(scene->cursor.location);
1593 break;
1594 }
1595 }
1596
1597 return math::transpose(float2x4(float4(u_dir, -math::dot(u_dir, origin)),
1598 float4(v_dir, -math::dot(v_dir, origin))));
1599}
1600
1602{
1603 GreasePencil *grease_pencil = static_cast<GreasePencil *>(
1604 CTX_data_pointer_get_type(&C, "grease_pencil", &RNA_GreasePencilv3).data);
1605
1606 if (grease_pencil == nullptr) {
1607 Object *object = CTX_data_active_object(&C);
1608 if (object && object->type == OB_GREASE_PENCIL) {
1609 grease_pencil = static_cast<GreasePencil *>(object->data);
1610 }
1611 }
1612 return grease_pencil;
1613}
1614
1615} // namespace blender::ed::greasepencil
@ ATTR_DOMAIN_MASK_POINT
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1096
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1091
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.
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:215
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(...)
@ BRUSH_LOCK_SIZE
@ CURVE_TYPE_BEZIER
@ 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
@ 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:275
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])
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:184
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:194
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:188
@ V3D_DEPTH_GPENCIL_ONLY
Definition ED_view3d.hh:190
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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
#define C
Definition RandGen.cpp:29
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
BPy_StructRNA * depsgraph
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
ItemIterator items() const
Definition BLI_map.hh:864
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)
constexpr int64_t last(const int64_t n=0) const
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
void add_new(const Key &key)
Definition BLI_set.hh:233
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
std::optional< T > get_if_single() const
int64_t index_of(const Key &key) const
bool add(const Key &key)
void add_new(const Key &key)
int64_t size() const
bool contains(const Key &key) const
int64_t size() const
void append(const T &value)
Span< T > as_span() 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
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
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())
const bke::CurvesGeometry & strokes() const
int sorted_keys_index_at(int frame_number) const
float4x4 to_world_space(const Object &object) const
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)
void cache_viewport_depths(Depsgraph *depsgraph, ARegion *region, View3D *view3d)
bool contains(int64_t query_index) const
draw_view in_light_buf[] float
static char ** types
Definition makesdna.cc:71
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
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)
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)
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)
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op, const bool use_duplicate_previous_key)
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)
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)
static float get_multi_frame_falloff(const int frame_number, const int center_frame, const int min_frame, const int max_frame, const CurveMapping *falloff_curve)
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)
static std::pair< int, int > get_minmax_selected_frame_numbers(const GreasePencil &grease_pencil, const int current_frame)
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)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static VectorSet< int > get_editable_material_indices(Object &object)
static VectorSet< int > get_fill_material_indices(Object &object)
static VectorSet< int > get_hidden_material_indices(Object &object)
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)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
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)
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)
T min(const T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T max(const T &a, const T &b)
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:95
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
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
float alpha
float unprojected_radius
struct CurveMapping * cur_falloff
GreasePencilOnionSkinningSettings onion_skinning_settings
struct MaterialGPencilStyle * gp_style
void * data
Definition RNA_types.hh:42
float viewinv[4][4]
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
struct GP_Sculpt_Settings gp_sculpt
float * depths
Definition ED_view3d.hh:85
struct ReportList * reports
void WM_event_add_notifier(const bContext *C, uint type, void *reference)