Blender V5.0
grease_pencil_draw_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "ANIM_keyframing.hh"
6
7#include "BKE_brush.hh"
8#include "BKE_colortools.hh"
9#include "BKE_context.hh"
10#include "BKE_crazyspace.hh"
11#include "BKE_curves.hh"
12#include "BKE_deform.hh"
13#include "BKE_geometry_set.hh"
14#include "BKE_grease_pencil.hh"
15#include "BKE_material.hh"
16#include "BKE_object_deform.h"
17#include "BKE_paint.hh"
18#include "BKE_paint_types.hh"
19#include "BKE_report.hh"
20#include "BKE_screen.hh"
21
22#include "BLI_array_utils.hh"
23#include "BLI_assert.h"
24#include "BLI_bounds.hh"
25#include "BLI_color.hh"
26#include "BLI_index_mask.hh"
27#include "BLI_kdopbvh.hh"
28#include "BLI_kdtree.h"
29#include "BLI_math_geom.h"
30#include "BLI_math_matrix.hh"
31#include "BLI_math_vector.hh"
32#include "BLI_offset_indices.hh"
33#include "BLI_rect.h"
34
35#include "DNA_brush_enums.h"
36#include "DNA_brush_types.h"
37#include "DNA_scene_types.h"
38#include "DNA_view3d_types.h"
40
42
45#include "GEO_smooth_curves.hh"
46
47#include "ED_grease_pencil.hh"
48#include "ED_image.hh"
49#include "ED_object.hh"
50#include "ED_screen.hh"
51#include "ED_space_api.hh"
52#include "ED_view3d.hh"
53
54#include "MEM_guardedalloc.h"
55
56#include "RNA_access.hh"
57#include "RNA_define.hh"
58
59#include "UI_interface.hh"
60
61#include "BLT_translation.hh"
62
63#include "WM_api.hh"
64#include "WM_toolsystem.hh"
65#include "WM_types.hh"
66
68#include "paint_intern.hh"
69#include "wm_event_types.hh"
70
71#include <algorithm>
72#include <fmt/format.h>
73#include <optional>
74
76
77/* -------------------------------------------------------------------- */
80
81static bool stroke_get_location(bContext * /*C*/,
82 float out[3],
83 const float mouse[2],
84 bool /*force_original*/)
85{
86 out[0] = mouse[0];
87 out[1] = mouse[1];
88 out[2] = 0;
89 return true;
90}
91
92static std::unique_ptr<GreasePencilStrokeOperation> get_stroke_operation(bContext &C,
93 wmOperator *op)
94{
96 const Brush &brush = *BKE_paint_brush_for_read(paint);
98 const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
99
100 if (mode == PaintMode::GPencil) {
102 stroke_mode == BRUSH_STROKE_ERASE)
103 {
104 /* Special case: We're using the draw tool but with the eraser mode, so create an erase
105 * operation. */
107 }
108 /* FIXME: Somehow store the unique_ptr in the PaintStroke. */
109 switch (eBrushGPaintType(brush.gpencil_brush_type)) {
115 /* Fill tool keymap uses the paint operator to draw fill guides. */
116 return greasepencil::new_paint_operation(/* do_fill_guides = */ true);
119 }
120 }
121 else if (mode == PaintMode::SculptGPencil) {
122
123 if (stroke_mode == BRUSH_STROKE_SMOOTH) {
124 return greasepencil::new_smooth_operation(stroke_mode, true);
125 }
128 return greasepencil::new_smooth_operation(stroke_mode);
130 return greasepencil::new_thickness_operation(stroke_mode);
132 return greasepencil::new_strength_operation(stroke_mode);
134 return greasepencil::new_grab_operation(stroke_mode);
136 return greasepencil::new_push_operation(stroke_mode);
138 return greasepencil::new_twist_operation(stroke_mode);
140 return greasepencil::new_pinch_operation(stroke_mode);
142 return greasepencil::new_randomize_operation(stroke_mode);
144 return greasepencil::new_clone_operation(stroke_mode);
145 }
146 }
147 else if (mode == PaintMode::WeightGPencil) {
157 }
158 }
159 else if (mode == PaintMode::VertexGPencil) {
172 /* Unused. */
174 return nullptr;
175 }
176 }
177 return nullptr;
178}
179
180static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
181{
182 UNUSED_VARS(C, op, mouse);
183 return true;
184}
185
187 wmOperator *op,
188 PaintStroke *stroke,
189 PointerRNA *stroke_element)
190{
192 paint_stroke_mode_data(stroke));
193
195 RNA_float_get_array(stroke_element, "mouse", sample.mouse_position);
196 sample.pressure = RNA_float_get(stroke_element, "pressure");
197
198 if (!operation) {
199 std::unique_ptr<GreasePencilStrokeOperation> new_operation = get_stroke_operation(*C, op);
200 BLI_assert(new_operation != nullptr);
201 new_operation->on_stroke_begin(*C, sample);
202 paint_stroke_set_mode_data(stroke, std::move(new_operation));
203 }
204 else {
205 operation->on_stroke_extended(*C, sample);
206 }
207}
208
209static void stroke_redraw(const bContext *C, PaintStroke * /*stroke*/, bool /*final*/)
210{
212}
213
214static void stroke_done(const bContext *C, PaintStroke *stroke)
215{
217 paint_stroke_mode_data(stroke));
218 if (operation != nullptr) {
219 operation->on_stroke_done(*C);
220 }
221}
222
224
225/* -------------------------------------------------------------------- */
228
230{
232 return false;
233 }
235 return false;
236 }
237 return true;
238}
239
241 wmOperator *op,
242 const wmEvent *event)
243{
244 if (event->tablet.active == EVT_TABLET_ERASER) {
245 RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_ERASE);
246 }
247
248 const bool use_duplicate_previous_key = [&]() -> bool {
250 const Brush &brush = *BKE_paint_brush_for_read(paint);
252 const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
253
254 if (mode == PaintMode::GPencil) {
255 /* For the eraser and tint tool, we don't want auto-key to create an empty keyframe, so we
256 * duplicate the previous frame. */
260 {
261 return true;
262 }
263 /* Same for the temporary eraser when using the draw tool. */
265 stroke_mode == BRUSH_STROKE_ERASE)
266 {
267 return true;
268 }
269 }
270 return false;
271 }();
273 C, op, use_duplicate_previous_key);
274 if (retval != OPERATOR_RUNNING_MODAL) {
275 return retval;
276 }
277
279 op,
285 event->type);
286
287 retval = op->type->modal(C, op, event);
288 OPERATOR_RETVAL_CHECK(retval);
289
290 if (retval == OPERATOR_FINISHED) {
291 return OPERATOR_FINISHED;
292 }
293
296}
297
299 wmOperator *op,
300 const wmEvent *event)
301{
302 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
303}
304
306{
307 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
308}
309
311{
312 ot->name = "Grease Pencil Draw";
313 ot->idname = "GREASE_PENCIL_OT_brush_stroke";
314 ot->description = "Draw a new stroke in the active Grease Pencil object";
315
320
322
324}
325
327
328/* -------------------------------------------------------------------- */
331
333{
335 return false;
336 }
338 return false;
339 }
340 return true;
341}
342
344 wmOperator *op,
345 const wmEvent *event)
346{
347 const Scene *scene = CTX_data_scene(C);
348 const Object *object = CTX_data_active_object(C);
349 if (!object || object->type != OB_GREASE_PENCIL) {
350 return OPERATOR_CANCELLED;
351 }
352
353 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
354 if (!ed::greasepencil::has_editable_layer(grease_pencil)) {
355 BKE_report(op->reports, RPT_ERROR, "No editable Grease Pencil layer");
356 return OPERATOR_CANCELLED;
357 }
358
360 const Brush *brush = BKE_paint_brush_for_read(paint);
361 if (brush == nullptr) {
362 return OPERATOR_CANCELLED;
363 }
364
365 /* Ensure a drawing at the current keyframe. */
366 bool inserted_keyframe = false;
367 /* For the sculpt tools, we don't want the auto-key to create an empty keyframe, so we duplicate
368 * the previous key. */
369 const bool use_duplicate_previous_key = true;
370 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
371 if (layer->is_editable() &&
373 *scene, grease_pencil, *layer, use_duplicate_previous_key, inserted_keyframe))
374 {
375 inserted_keyframe = true;
376 }
377 }
378 if (!inserted_keyframe) {
379 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
380 return OPERATOR_CANCELLED;
381 }
383
385 op,
391 event->type);
392
393 const wmOperatorStatus retval = op->type->modal(C, op, event);
394 OPERATOR_RETVAL_CHECK(retval);
395
396 if (retval == OPERATOR_FINISHED) {
397 return OPERATOR_FINISHED;
398 }
399
402}
403
405 wmOperator *op,
406 const wmEvent *event)
407{
408 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
409}
410
412{
413 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
414}
415
417{
418 ot->name = "Grease Pencil Sculpt";
419 ot->idname = "GREASE_PENCIL_OT_sculpt_paint";
420 ot->description = "Sculpt strokes in the active Grease Pencil object";
421
426
428
430}
431
433
434/* -------------------------------------------------------------------- */
437
439{
441 return false;
442 }
444 return false;
445 }
446 return true;
447}
448
450 wmOperator *op,
451 const wmEvent *event)
452{
453 const Scene *scene = CTX_data_scene(C);
454 const Object *object = CTX_data_active_object(C);
455 if (!object || object->type != OB_GREASE_PENCIL) {
456 return OPERATOR_CANCELLED;
457 }
458
459 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
461 const Brush *brush = BKE_paint_brush_for_read(paint);
462 if (brush == nullptr) {
463 return OPERATOR_CANCELLED;
464 }
465
468 if (drawings.is_empty()) {
469 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw weight on");
470 return OPERATOR_CANCELLED;
471 }
472
473 const int active_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
474 if (active_defgroup_nr >= 0 && BKE_object_defgroup_active_is_locked(object)) {
475 BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
476 return OPERATOR_CANCELLED;
477 }
478
480 op,
486 event->type);
487
488 const wmOperatorStatus retval = op->type->modal(C, op, event);
489 OPERATOR_RETVAL_CHECK(retval);
490
491 if (retval == OPERATOR_FINISHED) {
492 return OPERATOR_FINISHED;
493 }
494
497}
498
500 wmOperator *op,
501 const wmEvent *event)
502{
503 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
504}
505
510
512{
513 ot->name = "Grease Pencil Paint Weight";
514 ot->idname = "GREASE_PENCIL_OT_weight_brush_stroke";
515 ot->description = "Draw weight on stroke points in the active Grease Pencil object";
516
521
523
525}
526
528
529/* -------------------------------------------------------------------- */
532
534{
536 return false;
537 }
539 return false;
540 }
541 return true;
542}
543
545 wmOperator *op,
546 const wmEvent *event)
547{
548 const Scene *scene = CTX_data_scene(C);
549 const Object *object = CTX_data_active_object(C);
550 if (!object || object->type != OB_GREASE_PENCIL) {
551 return OPERATOR_CANCELLED;
552 }
553
554 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
555 if (!ed::greasepencil::has_editable_layer(grease_pencil)) {
556 BKE_report(op->reports, RPT_ERROR, "No editable Grease Pencil layer");
557 return OPERATOR_CANCELLED;
558 }
559
561 const Brush *brush = BKE_paint_brush_for_read(paint);
562 if (brush == nullptr) {
563 return OPERATOR_CANCELLED;
564 }
565
566 /* Ensure a drawing at the current keyframe. */
567 bool inserted_keyframe = false;
568 /* For the vertex paint tools, we don't want the auto-key to create an empty keyframe, so we
569 * duplicate the previous key. */
570 const bool use_duplicate_previous_key = true;
571 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
572 if (layer->is_editable() &&
574 *scene, grease_pencil, *layer, use_duplicate_previous_key, inserted_keyframe))
575 {
576 inserted_keyframe = true;
577 }
578 }
579 if (!inserted_keyframe) {
580 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
581 return OPERATOR_CANCELLED;
582 }
584
586 op,
592 event->type);
593
594 const wmOperatorStatus retval = op->type->modal(C, op, event);
595 OPERATOR_RETVAL_CHECK(retval);
596
597 if (retval == OPERATOR_FINISHED) {
598 return OPERATOR_FINISHED;
599 }
600
603}
604
606 wmOperator *op,
607 const wmEvent *event)
608{
609 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
610}
611
616
618{
619 ot->name = "Grease Pencil Paint Vertex";
620 ot->idname = "GREASE_PENCIL_OT_vertex_brush_stroke";
621 ot->description = "Draw on vertex colors in the active Grease Pencil object";
622
627
629
631}
632
634
635/* -------------------------------------------------------------------- */
638
641
642 /* Material of the generated stroke. */
644 /* Toggle inverse filling. */
645 bool invert = false;
646 /* Toggle precision mode. */
647 bool precision = false;
648
649 /* Methods for gap filling. */
651 /* Length of extension lines. */
653 /* Cut off extension lines at first intersection. */
655
656 /* Draw boundaries stroke overlay. */
658 /* Draw extension lines overlay. */
660
661 /* Mouse position where fill was initialized */
663 /* Extension lines drag mode is enabled (middle mouse button). */
665 /* Mouse position where the extension mode was enabled. */
667
668 /* Overlay draw callback for helper lines, etc. */
670
673 const int material_index,
674 const bool invert,
675 const bool precision)
676 {
678
680 const Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
688 const bool brush_invert = brush.gpencil_settings->fill_direction == BRUSH_DIR_IN;
689 /* Both operator properties and brush properties can invert. Actual invert is XOR of both. */
690 const bool combined_invert = (invert != brush_invert);
691
692 return {layer,
694 combined_invert,
695 precision,
701 }
702};
703
704/* Find and cut extension lines at intersections with other lines and strokes. */
706 ed::greasepencil::ExtensionData &extension_data,
707 Span<int> origin_drawings,
708 Span<int> origin_points)
709{
710 const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
711 const Scene &scene = *CTX_data_scene(&C);
712 const Object &object = *CTX_data_active_object(&C);
713 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
714
715 const float4x4 view_matrix = float4x4(rv3d.viewmat);
716
718 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
719
720 const IndexRange bvh_extension_range = extension_data.lines.starts.index_range();
721 Array<int> bvh_curve_offsets_data(drawings.size() + 1);
722 for (const int i : drawings.index_range()) {
723 bvh_curve_offsets_data[i] = drawings[i].drawing.strokes().points_num();
724 }
726 bvh_curve_offsets_data, bvh_extension_range.size());
727
728 /* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
729 * necessary. Not all entries are added to the BVH tree. */
730 const int max_bvh_lines = bvh_curve_offsets.data().last();
731 /* Cached view positions for lines. */
732 Array<float2> view_starts(max_bvh_lines);
733 Array<float2> view_ends(max_bvh_lines);
734
735 BVHTree *tree = BLI_bvhtree_new(max_bvh_lines, 0.0f, 4, 6);
737
738 /* Insert extension lines for intersection.
739 * Note: These are added first, so that the extension index matches its index in BVH data. */
740 for (const int i_line : bvh_extension_range.index_range()) {
741 const float2 start =
742 math::transform_point(view_matrix, extension_data.lines.starts[i_line]).xy();
743 const float2 end = math::transform_point(view_matrix, extension_data.lines.ends[i_line]).xy();
744
745 const int bvh_index = bvh_extension_range[i_line];
746 view_starts[bvh_index] = start;
747 view_ends[bvh_index] = end;
748
749 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
750 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
751 }
752
753 /* Insert segments for cutting extensions on stroke intersection. */
754 for (const int i_drawing : drawings.index_range()) {
755 const ed::greasepencil::DrawingInfo &info = drawings[i_drawing];
757 const OffsetIndices points_by_curve = curves.points_by_curve();
758 const Span<float3> positions = curves.positions();
759 const VArray<bool> cyclic = curves.cyclic();
760 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
761 const float4x4 layer_to_view = view_matrix * layer.to_world_space(object);
762
763 for (const int i_curve : curves.curves_range()) {
764 const bool is_cyclic = cyclic[i_curve];
765 const IndexRange points = points_by_curve[i_curve];
766
767 for (const int i_point : points.drop_back(1)) {
768 const float2 start = math::transform_point(layer_to_view, positions[i_point]).xy();
769 const float2 end = math::transform_point(layer_to_view, positions[i_point + 1]).xy();
770
771 const int bvh_index = bvh_curve_offsets[i_drawing][i_point];
772 view_starts[bvh_index] = start;
773 view_ends[bvh_index] = end;
774
775 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
776 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
777 }
778 /* Last->first point segment only used for cyclic curves. */
779 if (is_cyclic) {
780 const float2 start = math::transform_point(layer_to_view, positions[points.last()]).xy();
781 const float2 end = math::transform_point(layer_to_view, positions[points.first()]).xy();
782
783 const int bvh_index = bvh_curve_offsets[i_drawing][points.last()];
784 view_starts[bvh_index] = start;
785 view_ends[bvh_index] = end;
786
787 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
788 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
789 }
790 }
791 }
792
794
795 struct RaycastArgs {
796 Span<float2> starts;
797 Span<float2> ends;
798 /* Indices that may need to be ignored to avoid self-intersection. */
799 int ignore_index1;
800 int ignore_index2;
801 };
802 BVHTree_RayCastCallback callback =
803 [](void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) {
804 using Result = math::isect_result<float2>;
805
806 const RaycastArgs &args = *static_cast<const RaycastArgs *>(userdata);
807 if (ELEM(index, args.ignore_index1, args.ignore_index2)) {
808 return;
809 }
810
811 const float2 ray_start = float2(ray->origin);
812 const float2 ray_end = ray_start + float2(ray->direction) * ray->radius;
813 const float2 &line_start = args.starts[index];
814 const float2 &line_end = args.ends[index];
815 Result result = math::isect_seg_seg(ray_start, ray_end, line_start, line_end);
816 if (result.kind <= 0) {
817 return;
818 }
819 const float dist = result.lambda * math::distance(ray_start, ray_end);
820 if (dist >= hit->dist) {
821 return;
822 }
823 /* These always need to be calculated for the BVH traversal function. */
824 hit->index = index;
825 hit->dist = result.lambda * math::distance(ray_start, ray_end);
826 /* Don't need the hit point, only the lambda. */
827 hit->no[0] = result.lambda;
828 };
829
830 /* Store intersections first before applying to the data, so that subsequent ray-casts use
831 * original end points until all intersections are found. */
832 Vector<float3> new_extension_ends(extension_data.lines.ends.size());
833 for (const int i_line : extension_data.lines.starts.index_range()) {
834 const float2 start =
835 math::transform_point(view_matrix, extension_data.lines.starts[i_line]).xy();
836 const float2 end = math::transform_point(view_matrix, extension_data.lines.ends[i_line]).xy();
837 float length;
838 const float2 dir = math::normalize_and_get_length(end - start, length);
839
840 const int bvh_index = i_line;
841 const int origin_drawing = origin_drawings[i_line];
842 const int origin_point = origin_points[i_line];
843 const int bvh_origin_index = bvh_curve_offsets[origin_drawing][origin_point];
844
845 RaycastArgs args = {view_starts, view_ends, bvh_index, bvh_origin_index};
846 BVHTreeRayHit hit;
847 hit.index = -1;
848 hit.dist = FLT_MAX;
850 tree, float3(start, 0.0f), float3(dir, 0.0f), length, &hit, callback, &args);
851
852 if (hit.index >= 0) {
853 const float lambda = hit.no[0];
854 new_extension_ends[i_line] = math::interpolate(
855 extension_data.lines.starts[i_line], extension_data.lines.ends[i_line], lambda);
856 }
857 else {
858 new_extension_ends[i_line] = extension_data.lines.ends[i_line];
859 }
860 }
861
862 extension_data.lines.ends = std::move(new_extension_ends);
863}
864
865/* Find closest point in each circle and generate extension lines between such pairs. */
867 const bContext &C,
868 ed::greasepencil::ExtensionData &extension_data,
869 Span<int> /*origin_drawings*/,
870 Span<int> /*origin_points*/)
871{
872 const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
873 const Scene &scene = *CTX_data_scene(&C);
874 const Object &object = *CTX_data_active_object(&C);
875 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
876
877 const float4x4 view_matrix = float4x4(rv3d.viewmat);
878
880 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
881
882 const IndexRange circles_range = extension_data.circles.centers.index_range();
883 /* TODO Include high-curvature feature points. */
884 const IndexRange feature_points_range = circles_range.after(0);
885 const IndexRange kd_points_range = IndexRange(circles_range.size() +
886 feature_points_range.size());
887
888 /* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
889 * necessary. Not all entries are added to the BVH tree. */
890 const int max_kd_entries = kd_points_range.size();
891 /* Cached view positions for lines. */
892 Array<float2> view_centers(max_kd_entries);
893 Array<float> view_radii(max_kd_entries);
894
895 KDTree_2d *kdtree = BLI_kdtree_2d_new(max_kd_entries);
896
897 /* Insert points for overlap tests. */
898 for (const int point_i : circles_range.index_range()) {
899 const float2 center =
900 math::transform_point(view_matrix, extension_data.circles.centers[point_i]).xy();
901 const float radius = math::average(math::to_scale(view_matrix)) *
902 extension_data.circles.radii[point_i];
903
904 const int kd_index = circles_range[point_i];
905 view_centers[kd_index] = center;
906 view_radii[kd_index] = radius;
907
908 BLI_kdtree_2d_insert(kdtree, kd_index, center);
909 }
910 for (const int i_point : feature_points_range.index_range()) {
911 /* TODO Insert feature points into the KDTree. */
912 UNUSED_VARS(i_point);
913 }
914 BLI_kdtree_2d_balance(kdtree);
915
916 struct {
917 Vector<float3> starts;
918 Vector<float3> ends;
919 } connection_lines;
920 /* Circles which can be kept because they generate no extension lines. */
921 Vector<int> keep_circle_indices;
922 keep_circle_indices.reserve(circles_range.size());
923
924 for (const int point_i : circles_range.index_range()) {
925 const int kd_index = circles_range[point_i];
926 const float2 center = view_centers[kd_index];
927 const float radius = view_radii[kd_index];
928
929 bool found = false;
930 BLI_kdtree_2d_range_search_cb_cpp(
931 kdtree,
932 center,
933 radius,
934 [&](const int other_point_i, const float * /*co*/, float /*dist_sq*/) {
935 if (other_point_i == kd_index) {
936 return true;
937 }
938
939 found = true;
940 connection_lines.starts.append(extension_data.circles.centers[point_i]);
941 if (circles_range.contains(other_point_i)) {
942 connection_lines.ends.append(extension_data.circles.centers[other_point_i]);
943 }
944 else if (feature_points_range.contains(other_point_i)) {
945 /* TODO copy feature point to connection_lines (beware of start index!). */
946 connection_lines.ends.append(float3(0));
947 }
948 else {
950 }
951 return true;
952 });
953 /* Keep the circle if no extension line was found. */
954 if (!found) {
955 keep_circle_indices.append(point_i);
956 }
957 }
958
959 BLI_kdtree_2d_free(kdtree);
960
961 /* Add new extension lines. */
962 extension_data.lines.starts.extend(connection_lines.starts);
963 extension_data.lines.ends.extend(connection_lines.ends);
964 /* Remove circles that formed extension lines. */
965 Vector<float3> old_centers = std::move(extension_data.circles.centers);
966 Vector<float> old_radii = std::move(extension_data.circles.radii);
967 extension_data.circles.centers.resize(keep_circle_indices.size());
968 extension_data.circles.radii.resize(keep_circle_indices.size());
969 array_utils::gather(old_centers.as_span(),
970 keep_circle_indices.as_span(),
971 extension_data.circles.centers.as_mutable_span());
972 array_utils::gather(old_radii.as_span(),
973 keep_circle_indices.as_span(),
974 extension_data.circles.radii.as_mutable_span());
975}
976
978 const bContext &C, const GreasePencilFillOpData &op_data)
979{
980 const Scene &scene = *CTX_data_scene(&C);
981 const Object &object = *CTX_data_active_object(&C);
982 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
983
985 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
986
987 ed::greasepencil::ExtensionData extension_data;
988 Vector<int> origin_points;
989 Vector<int> origin_drawings;
990 for (const int i_drawing : drawings.index_range()) {
991 const ed::greasepencil::DrawingInfo &info = drawings[i_drawing];
993 const OffsetIndices points_by_curve = curves.points_by_curve();
994 const Span<float3> positions = curves.positions();
995 const VArray<bool> cyclic = curves.cyclic();
996 const float4x4 layer_to_world = grease_pencil.layer(info.layer_index).to_world_space(object);
997
998 for (const int i_curve : curves.curves_range()) {
999 const IndexRange points = points_by_curve[i_curve];
1000 /* No extension lines on cyclic curves. */
1001 if (cyclic[i_curve]) {
1002 continue;
1003 }
1004 /* Can't compute directions for single-point curves. */
1005 if (points.size() < 2) {
1006 continue;
1007 }
1008
1009 const float3 pos_head = math::transform_point(layer_to_world, positions[points[0]]);
1010 const float3 pos_tail = math::transform_point(layer_to_world, positions[points.last()]);
1011 const float3 pos_head_next = math::transform_point(layer_to_world, positions[points[1]]);
1012 const float3 pos_tail_prev = math::transform_point(layer_to_world,
1013 positions[points.last(1)]);
1014 const float3 dir_head = math::normalize(pos_head - pos_head_next);
1015 const float3 dir_tail = math::normalize(pos_tail - pos_tail_prev);
1016 /* Initial length before intersection tests. */
1017 const float length = op_data.extension_length;
1018
1019 switch (op_data.extension_mode) {
1021 extension_data.lines.starts.append(pos_head);
1022 extension_data.lines.ends.append(pos_head + dir_head * length);
1023 origin_drawings.append(i_drawing);
1024 origin_points.append(points.first());
1025
1026 extension_data.lines.starts.append(pos_tail);
1027 extension_data.lines.ends.append(pos_tail + dir_tail * length);
1028 origin_drawings.append(i_drawing);
1029 /* Segment index is the start point. */
1030 origin_points.append(points.last() - 1);
1031 break;
1033 extension_data.circles.centers.append(pos_head);
1034 extension_data.circles.radii.append(length);
1035 origin_drawings.append(i_drawing);
1036 origin_points.append(points.first());
1037
1038 extension_data.circles.centers.append(pos_tail);
1039 extension_data.circles.radii.append(length);
1040 origin_drawings.append(i_drawing);
1041 /* Segment index is the start point. */
1042 origin_points.append(points.last() - 1);
1043 break;
1044 }
1045 }
1046 }
1047
1048 switch (op_data.extension_mode) {
1050 /* Intersection test against strokes and other extension lines. */
1051 if (op_data.extension_cut) {
1052 grease_pencil_fill_extension_cut(C, extension_data, origin_drawings, origin_points);
1053 }
1054 break;
1057 C, extension_data, origin_drawings, origin_points);
1058 break;
1059 }
1060
1061 return extension_data;
1062}
1063
1065 const GreasePencilFillOpData &op_data)
1066{
1067 const bool is_extend = (op_data.extension_mode == GP_FILL_EMODE_EXTEND);
1068
1070 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
1071 status.item(IFACE_("Fill"), ICON_MOUSE_LMB);
1072 status.item(
1073 fmt::format("{} ({})", IFACE_("Mode"), (is_extend ? IFACE_("Extend") : IFACE_("Radius"))),
1074 ICON_EVENT_S);
1075 status.item(fmt::format("{} ({:.3f})",
1076 is_extend ? IFACE_("Length") : IFACE_("Radius"),
1077 op_data.extension_length),
1078 ICON_MOUSE_MMB_SCROLL);
1079 if (is_extend) {
1080 status.item_bool(IFACE_("Collision"), op_data.extension_cut, ICON_EVENT_D);
1081 }
1082}
1083
1084/* Draw callback for fill tool overlay. */
1085static void grease_pencil_fill_overlay_cb(const bContext *C, ARegion * /*region*/, void *arg)
1086{
1087 const ARegion &region = *CTX_wm_region(C);
1088 const RegionView3D &rv3d = *CTX_wm_region_view3d(C);
1089 const Scene &scene = *CTX_data_scene(C);
1090 const Object &object = *CTX_data_active_object(C);
1091 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
1092 auto &op_data = *static_cast<GreasePencilFillOpData *>(arg);
1093
1094 const float4x4 world_to_view = float4x4(rv3d.viewmat);
1095 /* Note; the initial view matrix is already set, clear to draw in view space. */
1097
1098 const ColorGeometry4f stroke_curves_color = ColorGeometry4f(1, 0, 0, 1);
1099 const ColorGeometry4f extension_lines_color = ColorGeometry4f(0, 1, 1, 1);
1100 const ColorGeometry4f extension_circles_color = ColorGeometry4f(1, 0.5, 0, 1);
1101
1103 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
1104
1105 if (op_data.show_boundaries) {
1107 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
1108
1109 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1110 const IndexMask curve_mask = info.drawing.strokes().curves_range();
1112 stroke_curves_color, info.drawing.strokes().points_num());
1113 const float4x4 layer_to_world = grease_pencil.layer(info.layer_index).to_world_space(object);
1114 const bool use_xray = false;
1115 const float radius_scale = 1.0f;
1116
1118 int2(region.winx, region.winy),
1119 object,
1120 info.drawing,
1121 layer_to_world,
1122 curve_mask,
1123 colors,
1124 use_xray,
1125 radius_scale);
1126 }
1127 }
1128
1129 if (op_data.show_extension) {
1131 *C, op_data);
1132
1133 const float line_width = 2.0f;
1134
1135 const IndexRange lines_range = extensions.lines.starts.index_range();
1136 if (!lines_range.is_empty()) {
1138 extension_lines_color, lines_range.size());
1139
1141 lines_range,
1142 extensions.lines.starts,
1143 extensions.lines.ends,
1144 line_colors,
1145 line_width);
1146 }
1147 const IndexRange circles_range = extensions.circles.centers.index_range();
1148 if (!circles_range.is_empty()) {
1150 extension_circles_color, circles_range.size());
1151
1153 world_to_view,
1154 circles_range,
1155 extensions.circles.centers,
1157 circle_colors,
1158 float2(region.winx, region.winy),
1159 line_width,
1160 false);
1161 }
1162 }
1163}
1164
1166 GreasePencilFillOpData &op_data)
1167{
1168 const bool needs_overlay = op_data.show_boundaries || op_data.show_extension;
1169
1170 if (needs_overlay) {
1171 if (op_data.overlay_cb_handle == nullptr) {
1174 }
1175 }
1176 else {
1177 if (op_data.overlay_cb_handle) {
1178 ED_region_draw_cb_exit(region.runtime->type, op_data.overlay_cb_handle);
1179 op_data.overlay_cb_handle = nullptr;
1180 }
1181 }
1182}
1183
1190
1191/* Layer mode defines layers where only marked boundary strokes are used. */
1193 eGP_FillLayerModes fill_layer_mode)
1194{
1195 BLI_assert(grease_pencil.has_active_layer());
1196 const IndexRange all_layers = grease_pencil.layers().index_range();
1197 const int active_layer_index = *grease_pencil.get_layer_index(*grease_pencil.get_active_layer());
1198
1199 switch (fill_layer_mode) {
1201 return VArray<bool>::from_std_func(all_layers.size(), [active_layer_index](const int index) {
1202 return index != active_layer_index;
1203 });
1205 return VArray<bool>::from_std_func(all_layers.size(), [active_layer_index](const int index) {
1206 return index != active_layer_index + 1;
1207 });
1209 return VArray<bool>::from_std_func(all_layers.size(), [active_layer_index](const int index) {
1210 return index != active_layer_index - 1;
1211 });
1213 return VArray<bool>::from_std_func(all_layers.size(), [active_layer_index](const int index) {
1214 return index <= active_layer_index;
1215 });
1217 return VArray<bool>::from_std_func(all_layers.size(), [active_layer_index](const int index) {
1218 return index >= active_layer_index;
1219 });
1221 return VArray<bool>::from_std_func(all_layers.size(), [grease_pencil](const int index) {
1222 return !grease_pencil.layers()[index]->is_visible();
1223 });
1224 }
1225 return {};
1226}
1227
1228/* Array of visible drawings to use as borders for generating a stroke in the editable drawing on
1229 * the active layer. This is provided for every frame in the multi-frame edit range. */
1234
1236 GreasePencil &grease_pencil,
1237 bke::greasepencil::Layer &target_layer)
1238{
1239 using namespace bke::greasepencil;
1242
1243 const ToolSettings *toolsettings = scene.toolsettings;
1244 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
1246 const bool use_autokey = blender::animrig::is_autokey_on(&scene);
1247 const bool use_duplicate_frame = (scene.toolsettings->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST);
1248 const int target_layer_index = *grease_pencil.get_layer_index(target_layer);
1249
1250 VectorSet<int> target_frames;
1251 /* Add drawing on the current frame. */
1252 target_frames.add(scene.r.cfra);
1253 /* Multi-frame edit: Add drawing on frames that are selected in any layer. */
1254 if (use_multi_frame_editing) {
1255 for (const Layer *layer : grease_pencil.layers()) {
1256 for (const auto [frame_number, frame] : layer->frames().items()) {
1257 if (frame.is_selected()) {
1258 target_frames.add(frame_number);
1259 }
1260 }
1261 }
1262 }
1263
1264 /* Create new drawings when autokey is enabled. */
1265 if (use_autokey) {
1266 for (const int frame_number : target_frames) {
1267 if (!target_layer.frames().contains(frame_number)) {
1268 if (use_duplicate_frame) {
1269 grease_pencil.insert_duplicate_frame(
1270 target_layer, *target_layer.start_frame_at(frame_number), frame_number, false);
1271 }
1272 else {
1273 grease_pencil.insert_frame(target_layer, frame_number);
1274 }
1275 }
1276 }
1277 }
1278
1280 for (const int frame_number : target_frames) {
1281 if (Drawing *target_drawing = grease_pencil.get_editable_drawing_at(target_layer,
1282 frame_number))
1283 {
1284 MutableDrawingInfo target = {*target_drawing, target_layer_index, frame_number, 1.0f};
1285
1286 Vector<DrawingInfo> sources;
1287 for (const Layer *source_layer : grease_pencil.layers()) {
1288 if (const Drawing *source_drawing = grease_pencil.get_drawing_at(*source_layer,
1289 frame_number))
1290 {
1291 const int source_layer_index = *grease_pencil.get_layer_index(*source_layer);
1292 sources.append({*source_drawing, source_layer_index, frame_number, 0});
1293 }
1294 }
1295
1296 drawings.append({std::move(target), std::move(sources)});
1297 }
1298 }
1299
1300 return drawings;
1301}
1302
1304{
1305 const int iterations = 20;
1306 if (curves.is_empty()) {
1307 return;
1308 }
1309 if (stroke_mask.is_empty()) {
1310 return;
1311 }
1312
1313 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1314 const OffsetIndices points_by_curve = curves.points_by_curve();
1315 const VArray<bool> cyclic = curves.cyclic();
1316 const VArray<bool> point_selection = VArray<bool>::from_single(true, curves.points_num());
1317
1318 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1320 points_by_curve,
1321 point_selection,
1322 cyclic,
1323 iterations,
1324 1.0f,
1325 false,
1326 true,
1327 positions.span);
1328 positions.finish();
1329 curves.tag_positions_changed();
1330}
1331
1333{
1334 const OffsetIndices points_by_curve = curves.points_by_curve();
1335 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1336
1337 IndexMaskMemory memory;
1338 IndexMask points_to_keep = IndexMask::from_predicate(
1339 curves.points_range(), GrainSize(2048), memory, [&](const int64_t i) {
1340 const int curve_i = point_to_curve_map[i];
1341 const IndexRange points = points_by_curve[curve_i];
1342 if (points.size() <= 2) {
1343 return true;
1344 }
1345 const int local_i = i - points.start();
1346 return (local_i % int(math::pow(2.0f, float(step))) == 0) || points.last() == i;
1347 });
1348
1349 return bke::curves_copy_point_selection(curves, points_to_keep, {});
1350}
1351
1352static bool grease_pencil_apply_fill(bContext &C, wmOperator &op, const wmEvent &event)
1353{
1357
1358 constexpr const ed::greasepencil::FillToolFitMethod fit_method =
1360 /* Debug setting: keep image data blocks for inspection. */
1361 constexpr const bool keep_images = false;
1362
1363 ARegion &region = *CTX_wm_region(&C);
1364 /* Perform bounds check. */
1365 const bool in_bounds = BLI_rcti_isect_pt_v(&region.winrct, event.xy);
1366 if (!in_bounds) {
1367 return false;
1368 }
1369
1370 wmWindow &win = *CTX_wm_window(&C);
1372 const Scene &scene = *CTX_data_scene(&C);
1373 Object &object = *CTX_data_active_object(&C);
1374 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1375 auto &op_data = *static_cast<GreasePencilFillOpData *>(op.customdata);
1376 const ToolSettings &ts = *CTX_data_tool_settings(&C);
1377 Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
1378 const float2 mouse_position = float2(event.mval);
1379 const int simplify_levels = brush.gpencil_settings->fill_simplylvl;
1380 const std::optional<float> alpha_threshold =
1382 std::nullopt :
1383 std::make_optional(brush.gpencil_settings->fill_threshold);
1384 const bool on_back = (ts.gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK);
1385 const bool auto_remove_fill_guides = (brush.gpencil_settings->flag &
1387
1388 if (!grease_pencil.has_active_layer()) {
1389 return false;
1390 }
1391 /* Add drawings in the active layer if autokey is enabled. */
1393 scene, grease_pencil, *grease_pencil.get_active_layer());
1394
1395 const VArray<bool> boundary_layers = get_fill_boundary_layers(
1396 grease_pencil, eGP_FillLayerModes(brush.gpencil_settings->fill_layer_mode));
1397
1398 bool did_create_fill = false;
1399 for (const FillToolTargetInfo &info : target_drawings) {
1400 const Layer &layer = *grease_pencil.layers()[info.target.layer_index];
1401
1403 C, op_data);
1404
1405 bke::CurvesGeometry fill_curves = fill_strokes(view_context,
1406 brush,
1407 scene,
1408 layer,
1409 boundary_layers,
1410 info.sources,
1411 op_data.invert,
1412 alpha_threshold,
1413 mouse_position,
1414 extensions,
1415 fit_method,
1416 op_data.material_index,
1417 keep_images);
1418 if (fill_curves.is_empty()) {
1419 continue;
1420 }
1421
1422 smooth_fill_strokes(fill_curves, fill_curves.curves_range());
1423
1424 if (simplify_levels > 0) {
1425 fill_curves = simplify_fixed(fill_curves, brush.gpencil_settings->fill_simplylvl);
1426 }
1427
1428 bke::CurvesGeometry &dst_curves = info.target.drawing.strokes_for_write();
1429 if (auto_remove_fill_guides) {
1430 /* Remove strokes that were created using the fill tool as boundary strokes. */
1432 }
1433
1434 /* If the `fill_strokes` function creates the "fill_opacity" attribute, make sure that we
1435 * initialize this to full opacity on the target geometry. */
1436 if (fill_curves.attributes().contains("fill_opacity") &&
1437 !dst_curves.attributes().contains("fill_opacity"))
1438 {
1439 bke::SpanAttributeWriter<float> fill_opacities =
1441 "fill_opacity",
1444 fill_opacities.finish();
1445 }
1446
1447 Curves *dst_curves_id = curves_new_nomain(dst_curves);
1448 Curves *fill_curves_id = curves_new_nomain(fill_curves);
1449 const Array<bke::GeometrySet> geometry_sets = {
1450 bke::GeometrySet::from_curves(on_back ? fill_curves_id : dst_curves_id),
1451 bke::GeometrySet::from_curves(on_back ? dst_curves_id : fill_curves_id)};
1452 const int num_new_curves = fill_curves.curves_num();
1453 const IndexRange new_curves_range = (on_back ?
1454 IndexRange(num_new_curves) :
1455 dst_curves.curves_range().after(num_new_curves));
1456
1457 bke::GeometrySet joined_geometry_set = geometry::join_geometries(geometry_sets, {});
1458 if (joined_geometry_set.has_curves()) {
1459 dst_curves = joined_geometry_set.get_curves_for_write()->geometry.wrap();
1460 info.target.drawing.tag_topology_changed();
1461
1462 /* Compute texture matrix for the new curves. */
1463 const ed::greasepencil::DrawingPlacement placement(
1464 scene, region, *view_context.v3d, object, &layer);
1466 &scene, &region, mouse_position, placement);
1467 Array<float4x2> texture_matrices(num_new_curves, texture_space);
1468 info.target.drawing.set_texture_matrices(texture_matrices, new_curves_range);
1469 }
1470
1471 did_create_fill = true;
1472 }
1473
1474 if (!did_create_fill) {
1475 BKE_reportf(op.reports, RPT_ERROR, "Unable to fill unclosed areas");
1476 }
1477
1479
1480 /* Save extend value for next operation. */
1481 brush.gpencil_settings->fill_extend_fac = op_data.extension_length /
1484
1485 return true;
1486}
1487
1489{
1491
1492 Main &bmain = *CTX_data_main(&C);
1493 Scene &scene = *CTX_data_scene(&C);
1495 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1497 Brush &brush = *BKE_paint_brush(&paint);
1498
1499 Layer *layer = grease_pencil.get_active_layer();
1500 /* Cannot paint in locked layer. */
1501 if (layer && layer->is_locked()) {
1502 return false;
1503 }
1504 if (layer == nullptr) {
1505 layer = &grease_pencil.add_layer("GP_Layer");
1506 }
1507
1508 if (brush.gpencil_settings == nullptr) {
1510 }
1520
1521 Material *material = BKE_grease_pencil_object_material_ensure_from_brush(&bmain, &ob, &brush);
1522 const int material_index = BKE_object_material_index_get(&ob, material);
1523
1524 const bool invert = RNA_boolean_get(op.ptr, "invert");
1525 const bool precision = RNA_boolean_get(op.ptr, "precision");
1526
1527 op.customdata = MEM_new<GreasePencilFillOpData>(
1528 __func__,
1529 GreasePencilFillOpData::from_context(C, *layer, material_index, invert, precision));
1530 return true;
1531}
1532
1534{
1535 const ARegion &region = *CTX_wm_region(&C);
1537 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1538
1540
1541 if (op.customdata) {
1542 auto &op_data = *static_cast<GreasePencilFillOpData *>(op.customdata);
1543
1544 if (op_data.overlay_cb_handle) {
1545 ED_region_draw_cb_exit(region.runtime->type, op_data.overlay_cb_handle);
1546 op_data.overlay_cb_handle = nullptr;
1547 }
1548
1549 MEM_delete(static_cast<GreasePencilFillOpData *>(op.customdata));
1550 op.customdata = nullptr;
1551 }
1552
1553 /* Clear status message area. */
1554 ED_workspace_status_text(&C, nullptr);
1555
1557
1560}
1561
1563 wmOperator *op,
1564 const wmEvent * /*event*/)
1565{
1566 const ARegion &region = *CTX_wm_region(C);
1568 Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
1570 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1571
1572 /* Fill tool needs a material (cannot use default material). */
1574 brush.gpencil_settings->material == nullptr)
1575 {
1576 BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material");
1577 return OPERATOR_CANCELLED;
1578 }
1579 if (BKE_object_material_get(&ob, ob.actcol) == nullptr) {
1580 BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material");
1581 return OPERATOR_CANCELLED;
1582 }
1583 if (!grease_pencil_fill_init(*C, *op)) {
1585 return OPERATOR_CANCELLED;
1586 }
1587 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1588
1591 grease_pencil_fill_update_overlay(region, op_data);
1592
1595
1596 /* Add a modal handler for this operator. */
1598
1600}
1601
1613
1615 wmOperator *op,
1616 const wmEvent *event)
1617{
1618 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1619 /* Extension line length increment, for normal and precise mode respectively. */
1620 const float extension_delta = (op_data.precision ? 0.002f : 0.02f);
1621
1622 switch (event->val) {
1623 case int(FillToolModalKey::Cancel):
1624 return OPERATOR_CANCELLED;
1625
1626 case int(FillToolModalKey::Confirm): {
1627 /* Ignore in extension mode. */
1628 if (op_data.is_extension_drag_active) {
1629 break;
1630 }
1631
1632 op_data.fill_mouse_pos = float2(event->mval);
1634 }
1635
1637 if (op_data.show_extension) {
1638 /* Toggle mode. */
1639 if (op_data.extension_mode == GP_FILL_EMODE_EXTEND) {
1640 op_data.extension_mode = GP_FILL_EMODE_RADIUS;
1641 }
1642 else {
1643 op_data.extension_mode = GP_FILL_EMODE_EXTEND;
1644 }
1645 grease_pencil_update_extend(*C, op_data);
1646 }
1647 break;
1648
1650 op_data.extension_length = std::max(op_data.extension_length - extension_delta, 0.0f);
1651 grease_pencil_update_extend(*C, op_data);
1652 break;
1653
1655 op_data.extension_length = std::min(op_data.extension_length + extension_delta, 10.0f);
1656 grease_pencil_update_extend(*C, op_data);
1657 break;
1658
1660 if (event->val == KM_PRESS) {
1661 /* Consider initial offset as zero position. */
1662 op_data.is_extension_drag_active = true;
1663 /* TODO This is the GPv2 logic and it's weird. Should be reconsidered, for now use the
1664 * same method. */
1665 const float2 base_pos = float2(event->mval);
1666 constexpr const float gap = 300.0f;
1667 op_data.extension_mouse_pos = (math::distance(base_pos, op_data.fill_mouse_pos) >= gap ?
1668 base_pos :
1669 base_pos - float2(gap, 0));
1671 }
1672 if (event->val == KM_RELEASE) {
1674 op_data.is_extension_drag_active = false;
1675 }
1676 /* Update cursor line. */
1679 break;
1680 }
1681
1683 if (op_data.show_extension) {
1684 op_data.extension_cut = !op_data.extension_cut;
1685 grease_pencil_update_extend(*C, op_data);
1686 }
1687 break;
1688
1689 case int(FillToolModalKey::Invert):
1690 op_data.invert = !op_data.invert;
1691 break;
1692
1694 op_data.precision = !op_data.precision;
1695 break;
1696
1697 default:
1699 break;
1700 }
1702}
1703
1705{
1706 const RegionView3D &rv3d = *CTX_wm_region_view3d(C);
1707
1708 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1709
1711 if (!op_data.show_extension) {
1712 /* Apply fill immediately if "Visual Aids" (aka. extension lines) is disabled. */
1713 op_data.fill_mouse_pos = float2(event->mval);
1714 estate = (grease_pencil_apply_fill(*C, *op, *event) ? OPERATOR_FINISHED : OPERATOR_CANCELLED);
1715 }
1716 else {
1717 estate = OPERATOR_RUNNING_MODAL;
1718 switch (event->type) {
1719 case EVT_MODAL_MAP:
1720 estate = grease_pencil_fill_event_modal_map(C, op, event);
1721 break;
1722 case MOUSEMOVE: {
1723 if (!op_data.is_extension_drag_active) {
1724 break;
1725 }
1726
1727 const Object &ob = *CTX_data_active_object(C);
1728 const float pixel_size = ED_view3d_pixel_size(&rv3d, ob.loc);
1729 const float2 mouse_pos = float2(event->mval);
1730 const float initial_dist = math::distance(op_data.extension_mouse_pos,
1731 op_data.fill_mouse_pos);
1732 const float current_dist = math::distance(mouse_pos, op_data.fill_mouse_pos);
1733
1734 float delta = (current_dist - initial_dist) * pixel_size * 0.5f;
1735 op_data.extension_length = std::clamp(op_data.extension_length + delta, 0.0f, 10.0f);
1736
1737 /* Update cursor line and extend lines. */
1740
1741 grease_pencil_update_extend(*C, op_data);
1742 break;
1743 }
1744 default:
1745 break;
1746 }
1747 }
1748
1749 /* Process last operations before exiting. */
1750 switch (estate) {
1751 case OPERATOR_FINISHED:
1754 break;
1755
1756 case OPERATOR_CANCELLED:
1758 break;
1759
1760 default:
1761 break;
1762 }
1763
1764 return estate;
1765}
1766
1768{
1770}
1771
1773{
1774 PropertyRNA *prop;
1775
1776 ot->name = "Grease Pencil Fill";
1777 ot->idname = "GREASE_PENCIL_OT_fill";
1778 ot->description = "Fill with color the shape formed by strokes";
1779
1781 ot->invoke = grease_pencil_fill_invoke;
1783 ot->cancel = grease_pencil_fill_cancel;
1784
1785 ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1786
1787 prop = RNA_def_boolean(
1788 ot->srna, "invert", false, "Invert", "Find boundary of unfilled instead of filled regions");
1790
1791 prop = RNA_def_boolean(
1792 ot->srna, "precision", false, "Precision", "Use precision movement for extension lines");
1794}
1795
1797 const Scene &scene, GreasePencil &grease_pencil, const int layer_index)
1798{
1799 using namespace bke::greasepencil;
1800 const int current_frame = scene.r.cfra;
1801 Layer &layer = grease_pencil.layer(layer_index);
1802 if (!layer.has_drawing_at(current_frame) && !blender::animrig::is_autokey_on(&scene)) {
1803 return nullptr;
1804 }
1805
1806 const std::optional<int> previous_key_frame_start = layer.start_frame_at(current_frame);
1807 const bool has_previous_key = previous_key_frame_start.has_value();
1808 if (blender::animrig::is_autokey_on(&scene) && has_previous_key) {
1809 grease_pencil.insert_duplicate_frame(layer, *previous_key_frame_start, current_frame, false);
1810 }
1811 return grease_pencil.get_drawing_at(layer, current_frame);
1812}
1813
1815 const Scene &scene,
1816 GreasePencil &grease_pencil,
1818 const Span<IndexMask> points_to_remove_per_drawing)
1819{
1820 using namespace bke::greasepencil;
1821 using namespace ed::greasepencil;
1822 bool changed = false;
1823 for (const int drawing_i : drawings.index_range()) {
1824 const MutableDrawingInfo &info = drawings[drawing_i];
1825 const IndexMask points_to_remove = points_to_remove_per_drawing[drawing_i];
1826 if (points_to_remove.is_empty()) {
1827 continue;
1828 }
1829
1831 scene, grease_pencil, info.layer_index))
1832 {
1833 drawing->strokes_for_write() = geometry::remove_points_and_split(drawing->strokes(),
1834 points_to_remove);
1835 drawing->tag_topology_changed();
1836 changed = true;
1837 }
1838 }
1839
1840 return changed;
1841}
1842
1843static inline bool is_point_inside_bounds(const Bounds<int2> bounds, const int2 point)
1844{
1845 if (point.x < bounds.min.x) {
1846 return false;
1847 }
1848 if (point.x > bounds.max.x) {
1849 return false;
1850 }
1851 if (point.y < bounds.min.y) {
1852 return false;
1853 }
1854 if (point.y > bounds.max.y) {
1855 return false;
1856 }
1857 return true;
1858}
1859
1860static inline bool is_point_inside_lasso(const Array<int2> lasso, const int2 point)
1861{
1863 point, reinterpret_cast<const int (*)[2]>(lasso.data()), uint(lasso.size()));
1864}
1865
1867{
1868 using namespace bke::greasepencil;
1869 using namespace ed::greasepencil;
1870 const Scene *scene = CTX_data_scene(C);
1871 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1872 const ARegion *region = CTX_wm_region(C);
1873 Object *object = CTX_data_active_object(C);
1874 const Object *ob_eval = DEG_get_evaluated(depsgraph, object);
1875 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1876
1877 const Array<int2> lasso = WM_gesture_lasso_path_to_array(C, op);
1878 if (lasso.is_empty()) {
1879 return OPERATOR_FINISHED;
1880 }
1881
1882 const Bounds<int2> lasso_bounds_int = *bounds::min_max(lasso.as_span());
1883 const Bounds<float2> lasso_bounds(float2(lasso_bounds_int.min), float2(lasso_bounds_int.max));
1884
1886 *scene, grease_pencil);
1887 Array<IndexMaskMemory> memories(drawings.size());
1888 Array<IndexMask> points_to_remove_per_drawing(drawings.size());
1889 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange range) {
1890 for (const int drawing_i : range) {
1891 const MutableDrawingInfo &info = drawings[drawing_i];
1892 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
1893 const bke::crazyspace::GeometryDeformation deformation =
1894 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
1895 ob_eval, *object, info.drawing);
1896 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
1897
1898 const bke::CurvesGeometry &curves = info.drawing.strokes();
1899 Array<float2> screen_space_positions(curves.points_num());
1900 threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange points) {
1901 for (const int point : points) {
1902 const float3 pos = math::transform_point(layer_to_world, deformation.positions[point]);
1903 eV3DProjStatus result = ED_view3d_project_float_global(
1904 region, pos, screen_space_positions[point], V3D_PROJ_TEST_NOP);
1905 if (result != V3D_PROJ_RET_OK) {
1906 screen_space_positions[point] = float2(0);
1907 }
1908 }
1909 });
1910
1911 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1912 Array<Bounds<float2>> screen_space_curve_bounds(curves.curves_num());
1913 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
1914 for (const int curve : range) {
1915 screen_space_curve_bounds[curve] = *bounds::min_max(
1916 screen_space_positions.as_span().slice(points_by_curve[curve]));
1917 }
1918 });
1919
1920 IndexMaskMemory &memory = memories[drawing_i];
1921 const IndexMask curve_selection = IndexMask::from_predicate(
1922 curves.curves_range(), GrainSize(512), memory, [&](const int64_t index) {
1923 /* For a single point curve, its screen_space_curve_bounds Bounds will be empty (by
1924 * definition), so intersecting will fail. Check if the single point is in the bounds
1925 * instead. */
1926 const IndexRange points = points_by_curve[index];
1927 if (points.size() == 1) {
1928 return is_point_inside_bounds(lasso_bounds_int,
1929 int2(screen_space_positions[points.first()]));
1930 }
1931
1932 return bounds::intersect(lasso_bounds, screen_space_curve_bounds[index]).has_value();
1933 });
1934
1935 if (curve_selection.is_empty()) {
1936 return;
1937 }
1938
1939 Array<bool> points_to_remove(curves.points_num(), false);
1940 curve_selection.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
1941 for (const int point : points_by_curve[curve_i]) {
1942 points_to_remove[point] = is_point_inside_lasso(lasso,
1943 int2(screen_space_positions[point]));
1944 }
1945 });
1946 points_to_remove_per_drawing[drawing_i] = IndexMask::from_bools(points_to_remove, memory);
1947 }
1948 });
1949
1950 const bool changed = remove_points_and_split_from_drawings(
1951 *scene, grease_pencil, drawings.as_span(), points_to_remove_per_drawing);
1952 if (changed) {
1953 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1955 }
1956
1957 return OPERATOR_FINISHED;
1958}
1959
1961{
1962 ot->name = "Grease Pencil Erase Lasso";
1963 ot->idname = "GREASE_PENCIL_OT_erase_lasso";
1964 ot->description = "Erase points in the lasso region";
1965
1967 ot->invoke = WM_gesture_lasso_invoke;
1969 ot->modal = WM_gesture_lasso_modal;
1970 ot->cancel = WM_gesture_lasso_cancel;
1971
1972 ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1973
1975}
1976
1978{
1979 using namespace bke::greasepencil;
1980 using namespace ed::greasepencil;
1981 const Scene *scene = CTX_data_scene(C);
1982 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1983 const ARegion *region = CTX_wm_region(C);
1984 Object *object = CTX_data_active_object(C);
1985 const Object *ob_eval = DEG_get_evaluated(depsgraph, object);
1986 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1987
1989 if (box_bounds.is_empty()) {
1990 return OPERATOR_FINISHED;
1991 }
1992
1994 *scene, grease_pencil);
1995 Array<IndexMaskMemory> memories(drawings.size());
1996 Array<IndexMask> points_to_remove_per_drawing(drawings.size());
1997 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange range) {
1998 for (const int drawing_i : range) {
1999 const MutableDrawingInfo &info = drawings[drawing_i];
2000 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
2001 const bke::crazyspace::GeometryDeformation deformation =
2002 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
2003 ob_eval, *object, info.drawing);
2004 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
2005
2006 const bke::CurvesGeometry &curves = info.drawing.strokes();
2007 Array<float2> screen_space_positions(curves.points_num());
2008 threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange points) {
2009 for (const int point : points) {
2010 const float3 pos = math::transform_point(layer_to_world, deformation.positions[point]);
2011 eV3DProjStatus result = ED_view3d_project_float_global(
2012 region, pos, screen_space_positions[point], V3D_PROJ_TEST_NOP);
2013 if (result != V3D_PROJ_RET_OK) {
2014 screen_space_positions[point] = float2(0);
2015 }
2016 }
2017 });
2018
2019 IndexMaskMemory &memory = memories[drawing_i];
2020 points_to_remove_per_drawing[drawing_i] = IndexMask::from_predicate(
2021 curves.points_range(), GrainSize(4096), memory, [&](const int64_t index) {
2022 return is_point_inside_bounds(box_bounds, int2(screen_space_positions[index]));
2023 });
2024 }
2025 });
2026
2027 const bool changed = remove_points_and_split_from_drawings(
2028 *scene, grease_pencil, drawings.as_span(), points_to_remove_per_drawing);
2029 if (changed) {
2030 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2032 }
2033
2034 return OPERATOR_FINISHED;
2035}
2036
2038{
2039 ot->name = "Grease Pencil Box Erase";
2040 ot->idname = "GREASE_PENCIL_OT_erase_box";
2041 ot->description = "Erase points in the box region";
2042
2044 ot->invoke = WM_gesture_box_invoke;
2046 ot->modal = WM_gesture_box_modal;
2047 ot->cancel = WM_gesture_box_cancel;
2048
2049 ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
2050
2052}
2053
2055
2056} // namespace blender::ed::sculpt_paint
2057
2058/* -------------------------------------------------------------------- */
2061
2073
2075{
2076 using namespace blender::ed::greasepencil;
2078
2079 static const EnumPropertyItem modal_items[] = {
2080 {int(FillToolModalKey::Cancel), "CANCEL", 0, "Cancel", ""},
2081 {int(FillToolModalKey::Confirm), "CONFIRM", 0, "Confirm", ""},
2083 "EXTENSION_MODE_TOGGLE",
2084 0,
2085 "Toggle Extension Mode",
2086 ""},
2088 "EXTENSION_LENGTHEN",
2089 0,
2090 "Lengthen Extensions",
2091 ""},
2092 {int(FillToolModalKey::ExtensionShorten), "EXTENSION_SHORTEN", 0, "Shorten Extensions", ""},
2093 {int(FillToolModalKey::ExtensionDrag), "EXTENSION_DRAG", 0, "Drag Extensions", ""},
2094 {int(FillToolModalKey::ExtensionCollide), "EXTENSION_COLLIDE", 0, "Collide Extensions", ""},
2095 {int(FillToolModalKey::Invert), "INVERT", 0, "Invert", ""},
2096 {int(FillToolModalKey::Precision), "PRECISION", 0, "Precision", ""},
2097 {0, nullptr, 0, nullptr, nullptr},
2098 };
2099
2100 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Fill Tool Modal Map");
2101
2102 /* This function is called for each space-type, only needs to add map once. */
2103 if (keymap && keymap->modal_items) {
2104 return;
2105 }
2106
2107 keymap = WM_modalkeymap_ensure(keyconf, "Fill Tool Modal Map", modal_items);
2108
2109 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_fill");
2110}
2111
Functions to insert, delete or modify keyframes.
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:648
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:764
void BKE_curvemapping_init(CurveMapping *cumap)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Low-level operations for curves.
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:607
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
General operations, lookup, etc. for materials.
Material * BKE_object_material_get(Object *ob, short act)
int BKE_object_material_index_get(Object *ob, const Material *ma)
Functions for dealing with objects and deform verts, used by painting and tools.
bool BKE_object_defgroup_active_is_locked(const struct Object *ob)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:505
PaintMode
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
void(*)(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) BVHTree_RayCastCallback
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
bool isect_point_poly_v2_int(const int pt[2], const int verts[][2], unsigned int nr)
#define BLI_SCOPED_DEFER(function_to_defer)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
unsigned int uint
#define UNUSED_VARS(...)
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
eBrushGPVertexType
@ GPVERTEX_BRUSH_TYPE_BLUR
@ GPVERTEX_BRUSH_TYPE_DRAW
@ GPVERTEX_BRUSH_TYPE_TINT
@ GPVERTEX_BRUSH_TYPE_REPLACE
@ GPVERTEX_BRUSH_TYPE_AVERAGE
@ GPVERTEX_BRUSH_TYPE_SMEAR
eBrushGPaintType
@ GPAINT_BRUSH_TYPE_TINT
@ GPAINT_BRUSH_TYPE_FILL
@ GPAINT_BRUSH_TYPE_DRAW
@ GPAINT_BRUSH_TYPE_ERASE
eGP_FillExtendModes
@ GP_FILL_EMODE_RADIUS
@ GP_FILL_EMODE_EXTEND
@ GP_BRUSH_FILL_SHOW_HELPLINES
@ GP_BRUSH_FILL_HIDE
@ GP_BRUSH_FILL_AUTO_REMOVE_FILL_GUIDES
@ GP_BRUSH_FILL_STROKE_COLLIDE
@ GP_BRUSH_MATERIAL_PINNED
@ GP_BRUSH_FILL_SHOW_EXTENDLINES
@ BRUSH_DIR_IN
eBrushGPWeightType
@ GPWEIGHT_BRUSH_TYPE_AVERAGE
@ GPWEIGHT_BRUSH_TYPE_DRAW
@ GPWEIGHT_BRUSH_TYPE_SMEAR
@ GPWEIGHT_BRUSH_TYPE_BLUR
eBrushGPSculptType
@ GPSCULPT_BRUSH_TYPE_SMOOTH
@ GPSCULPT_BRUSH_TYPE_PUSH
@ GPSCULPT_BRUSH_TYPE_CLONE
@ GPSCULPT_BRUSH_TYPE_TWIST
@ GPSCULPT_BRUSH_TYPE_RANDOMIZE
@ GPSCULPT_BRUSH_TYPE_GRAB
@ GPSCULPT_BRUSH_TYPE_PINCH
@ GPSCULPT_BRUSH_TYPE_THICKNESS
@ GPSCULPT_BRUSH_TYPE_STRENGTH
eGP_FillLayerModes
@ GP_FILL_GPLMODE_ABOVE
@ GP_FILL_GPLMODE_ALL_ABOVE
@ GP_FILL_GPLMODE_VISIBLE
@ GP_FILL_GPLMODE_ALL_BELOW
@ GP_FILL_GPLMODE_BELOW
@ GP_FILL_GPLMODE_ACTIVE
@ OB_GREASE_PENCIL
@ GP_TOOL_FLAG_RETAIN_LAST
@ GP_TOOL_FLAG_PAINT_ONBACK
@ GP_USE_MULTI_FRAME_EDITING
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
#define OPERATOR_RETVAL_CHECK(ret)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
BPy_StructRNA * depsgraph
long long int int64_t
bool contains(const Key &key) const
Definition BLI_map.hh:353
void append(const T &value)
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
int64_t size() const
Definition BLI_array.hh:256
Span< T > as_span() const
Definition BLI_array.hh:243
const T * data() const
Definition BLI_array.hh:312
bool is_empty() const
Definition BLI_array.hh:264
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr IndexRange after(int64_t n) const
constexpr int64_t start() const
constexpr bool contains(int64_t value) const
constexpr IndexRange index_range() const
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static VArray from_std_func(const int64_t size, std::function< T(int64_t index)> get_func)
static VArray from_single(T value, const int64_t size)
static VArray from_span(Span< T > values)
bool add(const Key &key)
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
void reserve(const int64_t min_capacity)
Span< T > as_span() const
bool contains(StringRef attribute_id) const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
AttributeAccessor attributes() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
const bke::CurvesGeometry & strokes() const
float4x4 to_world_space(const Object &object) const
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
bool has_drawing_at(const int frame_number) const
std::optional< int > start_frame_at(int frame_number) const
virtual void on_stroke_extended(const bContext &C, const InputSample &extension_sample)=0
virtual void on_stroke_done(const bContext &C)=0
static bool is_cyclic(const Nurb *nu)
KDTree_3d * tree
#define out
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
float length(VecOp< float, D >) RET
void ED_filltool_modal_keymap(wmKeyConfig *keyconf)
void ED_operatortypes_grease_pencil_draw()
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
bool is_autokey_on(const Scene *scene)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
constexpr float LEGACY_RADIUS_CONVERSION_FACTOR
CurvesGeometry curves_copy_point_selection(const CurvesGeometry &curves, const IndexMask &points_to_copy, const AttributeFilter &attribute_filter)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
void draw_lines(const float4x4 &transform, IndexRange indices, Span< float3 > start_positions, Span< float3 > end_positions, const VArray< ColorGeometry4f > &colors, float line_width)
void draw_grease_pencil_strokes(const RegionView3D &rv3d, const int2 &win_size, const Object &object, const bke::greasepencil::Drawing &drawing, const float4x4 &transform, const IndexMask &strokes_mask, const VArray< ColorGeometry4f > &colors, const bool use_xray, const float radius_scale)
void draw_circles(const float4x4 &transform, const IndexRange indices, Span< float3 > centers, const VArray< float > &radii, const VArray< ColorGeometry4f > &colors, const float2 &viewport_size, const float line_width, const bool fill)
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const bool duplicate_previous_key, bool &r_inserted_keyframe)
bool grease_pencil_vertex_painting_poll(bContext *C)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
bool has_editable_layer(const GreasePencil &grease_pencil)
bool grease_pencil_sculpting_poll(bContext *C)
bool grease_pencil_weight_painting_poll(bContext *C)
wmOperatorStatus grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op, const bool use_duplicate_previous_key)
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)
bool grease_pencil_painting_poll(bContext *C)
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_replace_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_erase_operation(const bool temp_eraser)
std::unique_ptr< GreasePencilStrokeOperation > new_weight_paint_average_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_strength_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_grab_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_weight_paint_blur_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_tint_operation(bool temp_eraser=false)
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_paint_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_paint_operation(bool do_fill_guides=false)
std::unique_ptr< GreasePencilStrokeOperation > new_clone_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_randomize_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_blur_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_weight_paint_draw_operation(const BrushStrokeMode &stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_thickness_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_pinch_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_push_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_weight_paint_smear_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_average_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_twist_operation(BrushStrokeMode stroke_mode)
std::unique_ptr< GreasePencilStrokeOperation > new_smooth_operation(BrushStrokeMode stroke_mode, bool temp_smooth=false)
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_smear_operation()
static void grease_pencil_fill_extension_lines_from_circles(const bContext &C, ed::greasepencil::ExtensionData &extension_data, Span< int >, Span< int >)
static bool is_point_inside_bounds(const Bounds< int2 > bounds, const int2 point)
static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
static bool is_point_inside_lasso(const Array< int2 > lasso, const int2 point)
static bool grease_pencil_weight_brush_stroke_poll(bContext *C)
static void grease_pencil_sculpt_paint_cancel(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_weight_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void grease_pencil_fill_exit(bContext &C, wmOperator &op)
static wmOperatorStatus grease_pencil_vertex_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool remove_points_and_split_from_drawings(const Scene &scene, GreasePencil &grease_pencil, const Span< ed::greasepencil::MutableDrawingInfo > drawings, const Span< IndexMask > points_to_remove_per_drawing)
static void stroke_update_step(bContext *C, wmOperator *op, PaintStroke *, PointerRNA *stroke_element)
static void GREASE_PENCIL_OT_erase_lasso(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool grease_pencil_sculpt_paint_poll(bContext *C)
static void grease_pencil_fill_cancel(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_sculpt_paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void GREASE_PENCIL_OT_weight_brush_stroke(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_vertex_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bool stroke_get_location(bContext *C, float out[3], const float mouse[2], bool)
static void grease_pencil_brush_stroke_cancel(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_brush_stroke(wmOperatorType *ot)
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
static void GREASE_PENCIL_OT_vertex_brush_stroke(wmOperatorType *ot)
static void stroke_done(const bContext *C, PaintStroke *stroke)
static void GREASE_PENCIL_OT_sculpt_paint(wmOperatorType *ot)
static void grease_pencil_fill_status_indicators(bContext &C, const GreasePencilFillOpData &op_data)
static wmOperatorStatus grease_pencil_fill_event_modal_map(bContext *C, wmOperator *op, const wmEvent *event)
static bke::greasepencil::Drawing * get_current_drawing_or_duplicate_for_autokey(const Scene &scene, GreasePencil &grease_pencil, const int layer_index)
static bool grease_pencil_vertex_brush_stroke_poll(bContext *C)
static void grease_pencil_vertex_brush_stroke_cancel(bContext *C, wmOperator *op)
wmOperatorStatus paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
static wmOperatorStatus grease_pencil_sculpt_paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus grease_pencil_weight_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bke::CurvesGeometry simplify_fixed(bke::CurvesGeometry &curves, const int step)
static wmOperatorStatus grease_pencil_erase_box_exec(bContext *C, wmOperator *op)
static void stroke_redraw(const bContext *C, PaintStroke *, bool)
static void GREASE_PENCIL_OT_erase_box(wmOperatorType *ot)
static VArray< bool > get_fill_boundary_layers(const GreasePencil &grease_pencil, eGP_FillLayerModes fill_layer_mode)
static Vector< FillToolTargetInfo > ensure_editable_drawings(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &target_layer)
static void grease_pencil_weight_brush_stroke_cancel(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void GREASE_PENCIL_OT_fill(wmOperatorType *ot)
static void grease_pencil_fill_overlay_cb(const bContext *C, ARegion *, void *arg)
void * paint_stroke_mode_data(PaintStroke *stroke)
static wmOperatorStatus grease_pencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus grease_pencil_erase_lasso_exec(bContext *C, wmOperator *op)
static bool grease_pencil_apply_fill(bContext &C, wmOperator &op, const wmEvent &event)
static wmOperatorStatus grease_pencil_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void grease_pencil_fill_update_overlay(const ARegion &region, GreasePencilFillOpData &op_data)
PaintStroke * paint_stroke_new(bContext *C, wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeRedraw redraw, StrokeDone done, int event_type)
static ed::greasepencil::ExtensionData grease_pencil_fill_get_extension_data(const bContext &C, const GreasePencilFillOpData &op_data)
static void grease_pencil_fill_extension_cut(const bContext &C, ed::greasepencil::ExtensionData &extension_data, Span< int > origin_drawings, Span< int > origin_points)
static void smooth_fill_strokes(bke::CurvesGeometry &curves, const IndexMask &stroke_mask)
static std::unique_ptr< GreasePencilStrokeOperation > get_stroke_operation(bContext &C, wmOperator *op)
static bool grease_pencil_brush_stroke_poll(bContext *C)
static void grease_pencil_update_extend(bContext &C, GreasePencilFillOpData &op_data)
void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr< PaintModeData > mode_data)
static bool grease_pencil_fill_init(bContext &C, wmOperator &op)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt, bool allow_merging_instance_references=true)
bke::CurvesGeometry remove_points_and_split(const bke::CurvesGeometry &curves, const IndexMask &mask)
void smooth_curve_attribute(const IndexMask &curves_to_smooth, const OffsetIndices< int > points_by_curve, const VArray< bool > &point_selection, const VArray< bool > &cyclic, int iterations, float influence, bool smooth_ends, bool keep_shape, GMutableSpan attribute_data)
T pow(const T &x, const T &power)
isect_result< VecBase< T, Size > > isect_seg_seg(const VecBase< T, Size > &v1, const VecBase< T, Size > &v2, const VecBase< T, Size > &v3, const VecBase< T, Size > &v4)
T distance(const T &a, const T &b)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
T average(const VecBase< T, Size > &a)
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
BrushStrokeMode
@ BRUSH_STROKE_SMOOTH
@ BRUSH_STROKE_ERASE
void paint_stroke_operator_properties(wmOperatorType *ot)
const int status
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
#define FLT_MAX
Definition stdcycles.h:14
ARegionRuntimeHandle * runtime
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
struct CurveMapping * curve_jitter
struct CurveMapping * curve_rand_pressure
struct CurveMapping * curve_rand_strength
struct CurveMapping * curve_rand_uv
struct Material * material
struct CurveMapping * curve_rand_saturation
struct CurveMapping * curve_rand_hue
char gpencil_sculpt_brush_type
struct CurveMapping * curve_rand_value
char gpencil_weight_brush_type
struct BrushGpencilSettings * gpencil_settings
char gpencil_vertex_brush_type
char gpencil_brush_type
CurvesGeometry geometry
float loc[3]
float viewmat[4][4]
struct ToolSettings * toolsettings
struct RenderData r
View3D * v3d
Definition ED_view3d.hh:78
bool is_empty() const
VecBase< T, 2 > xy() const
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const bke::greasepencil::Drawing & drawing
struct blender::ed::greasepencil::ExtensionData::@020012301277233322117257155067364007210377153176 lines
struct blender::ed::greasepencil::ExtensionData::@137034222204205036366070174242001167366362372317 circles
Vector< ed::greasepencil::DrawingInfo > sources
static GreasePencilFillOpData from_context(bContext &C, blender::bke::greasepencil::Layer &layer, const int material_index, const bool invert, const bool precision)
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int xy[2]
Definition WM_types.hh:761
int mval[2]
Definition WM_types.hh:763
wmTabletData tablet
Definition WM_types.hh:786
const void * modal_items
wmOperatorStatus(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1081
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_set(wmWindow *win, int curs)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_PAINT_BRUSH
Definition wm_cursors.hh:34
@ WM_CURSOR_EW_ARROW
Definition wm_cursors.hh:46
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_TABLET_ERASER
@ EVT_MODAL_MAP
@ MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:932
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:959
blender::Bounds< blender::int2 > WM_operator_properties_border_to_bounds(wmOperator *op)
void WM_operator_properties_border(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
bool WM_toolsystem_active_tool_is_brush(const bContext *C)