Blender V4.5
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_report.hh"
19#include "BKE_screen.hh"
20
21#include "BLI_array_utils.hh"
22#include "BLI_assert.h"
23#include "BLI_bounds.hh"
24#include "BLI_color.hh"
25#include "BLI_index_mask.hh"
26#include "BLI_kdopbvh.hh"
27#include "BLI_kdtree.h"
28#include "BLI_math_geom.h"
29#include "BLI_math_matrix.hh"
30#include "BLI_math_vector.hh"
31#include "BLI_offset_indices.hh"
32#include "BLI_rect.h"
33
34#include "DNA_brush_enums.h"
35#include "DNA_brush_types.h"
36#include "DNA_scene_types.h"
37#include "DNA_view3d_types.h"
39
41
44#include "GEO_smooth_curves.hh"
45
46#include "ED_grease_pencil.hh"
47#include "ED_image.hh"
48#include "ED_object.hh"
49#include "ED_screen.hh"
50#include "ED_space_api.hh"
51#include "ED_view3d.hh"
52
53#include "MEM_guardedalloc.h"
54
55#include "RNA_access.hh"
56#include "RNA_define.hh"
57
58#include "UI_interface.hh"
59
60#include "BLT_translation.hh"
61
62#include "WM_api.hh"
63#include "WM_toolsystem.hh"
64#include "WM_types.hh"
65
67#include "paint_intern.hh"
68#include "wm_event_types.hh"
69
70#include <algorithm>
71#include <fmt/format.h>
72#include <optional>
73
75
76/* -------------------------------------------------------------------- */
79
80static bool stroke_get_location(bContext * /*C*/,
81 float out[3],
82 const float mouse[2],
83 bool /*force_original*/)
84{
85 out[0] = mouse[0];
86 out[1] = mouse[1];
87 out[2] = 0;
88 return true;
89}
90
91static std::unique_ptr<GreasePencilStrokeOperation> get_stroke_operation(bContext &C,
92 wmOperator *op)
93{
95 const Brush &brush = *BKE_paint_brush_for_read(paint);
97 const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
98
99 if (mode == PaintMode::GPencil) {
101 stroke_mode == BRUSH_STROKE_ERASE)
102 {
103 /* Special case: We're using the draw tool but with the eraser mode, so create an erase
104 * operation. */
106 }
107 /* FIXME: Somehow store the unique_ptr in the PaintStroke. */
108 switch (eBrushGPaintType(brush.gpencil_brush_type)) {
114 /* Fill tool keymap uses the paint operator to draw fill guides. */
115 return greasepencil::new_paint_operation(/* do_fill_guides = */ true);
118 }
119 }
120 else if (mode == PaintMode::SculptGPencil) {
121
122 if (stroke_mode == BRUSH_STROKE_SMOOTH) {
123 return greasepencil::new_smooth_operation(stroke_mode, true);
124 }
127 return greasepencil::new_smooth_operation(stroke_mode);
129 return greasepencil::new_thickness_operation(stroke_mode);
131 return greasepencil::new_strength_operation(stroke_mode);
133 return greasepencil::new_grab_operation(stroke_mode);
135 return greasepencil::new_push_operation(stroke_mode);
137 return greasepencil::new_twist_operation(stroke_mode);
139 return greasepencil::new_pinch_operation(stroke_mode);
141 return greasepencil::new_randomize_operation(stroke_mode);
143 return greasepencil::new_clone_operation(stroke_mode);
144 }
145 }
146 else if (mode == PaintMode::WeightGPencil) {
156 }
157 }
158 else if (mode == PaintMode::VertexGPencil) {
171 /* Unused. */
173 return nullptr;
174 }
175 }
176 return nullptr;
177}
178
179static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
180{
181 UNUSED_VARS(C, op, mouse);
182 return true;
183}
184
186 wmOperator *op,
187 PaintStroke *stroke,
188 PointerRNA *stroke_element)
189{
191 paint_stroke_mode_data(stroke));
192
194 RNA_float_get_array(stroke_element, "mouse", sample.mouse_position);
195 sample.pressure = RNA_float_get(stroke_element, "pressure");
196
197 if (!operation) {
198 std::unique_ptr<GreasePencilStrokeOperation> new_operation = get_stroke_operation(*C, op);
199 BLI_assert(new_operation != nullptr);
200 new_operation->on_stroke_begin(*C, sample);
201 paint_stroke_set_mode_data(stroke, std::move(new_operation));
202 }
203 else {
204 operation->on_stroke_extended(*C, sample);
205 }
206}
207
208static void stroke_redraw(const bContext *C, PaintStroke * /*stroke*/, bool /*final*/)
209{
211}
212
213static void stroke_done(const bContext *C, PaintStroke *stroke)
214{
216 paint_stroke_mode_data(stroke));
217 if (operation != nullptr) {
218 operation->on_stroke_done(*C);
219 }
220}
221
223
224/* -------------------------------------------------------------------- */
227
229{
231 return false;
232 }
234 return false;
235 }
236 return true;
237}
238
240 wmOperator *op,
241 const wmEvent *event)
242{
243 if (event->tablet.active == EVT_TABLET_ERASER) {
244 RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_ERASE);
245 }
246
247 const bool use_duplicate_previous_key = [&]() -> bool {
249 const Brush &brush = *BKE_paint_brush_for_read(paint);
251 const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
252
253 if (mode == PaintMode::GPencil) {
254 /* For the eraser and tint tool, we don't want auto-key to create an empty keyframe, so we
255 * duplicate the previous frame. */
259 {
260 return true;
261 }
262 /* Same for the temporary eraser when using the draw tool. */
264 stroke_mode == BRUSH_STROKE_ERASE)
265 {
266 return true;
267 }
268 }
269 return false;
270 }();
272 C, op, use_duplicate_previous_key);
273 if (retval != OPERATOR_RUNNING_MODAL) {
274 return retval;
275 }
276
278 op,
284 event->type);
285
286 retval = op->type->modal(C, op, event);
287 OPERATOR_RETVAL_CHECK(retval);
288
289 if (retval == OPERATOR_FINISHED) {
290 return OPERATOR_FINISHED;
291 }
292
295}
296
298 wmOperator *op,
299 const wmEvent *event)
300{
301 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
302}
303
305{
306 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
307}
308
310{
311 ot->name = "Grease Pencil Draw";
312 ot->idname = "GREASE_PENCIL_OT_brush_stroke";
313 ot->description = "Draw a new stroke in the active Grease Pencil object";
314
319
321
323}
324
326
327/* -------------------------------------------------------------------- */
330
332{
334 return false;
335 }
337 return false;
338 }
339 return true;
340}
341
343 wmOperator *op,
344 const wmEvent *event)
345{
346 const Scene *scene = CTX_data_scene(C);
347 const Object *object = CTX_data_active_object(C);
348 if (!object || object->type != OB_GREASE_PENCIL) {
349 return OPERATOR_CANCELLED;
350 }
351
352 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
353 if (!ed::greasepencil::has_editable_layer(grease_pencil)) {
354 BKE_report(op->reports, RPT_ERROR, "No editable Grease Pencil layer");
355 return OPERATOR_CANCELLED;
356 }
357
359 const Brush *brush = BKE_paint_brush_for_read(paint);
360 if (brush == nullptr) {
361 return OPERATOR_CANCELLED;
362 }
363
364 /* Ensure a drawing at the current keyframe. */
365 bool inserted_keyframe = false;
366 /* For the sculpt tools, we don't want the auto-key to create an empty keyframe, so we duplicate
367 * the previous key. */
368 const bool use_duplicate_previous_key = true;
369 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
370 if (layer->is_editable() &&
372 *scene, grease_pencil, *layer, use_duplicate_previous_key, inserted_keyframe))
373 {
374 inserted_keyframe = true;
375 }
376 }
377 if (!inserted_keyframe) {
378 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
379 return OPERATOR_CANCELLED;
380 }
382
384 op,
390 event->type);
391
392 const wmOperatorStatus retval = op->type->modal(C, op, event);
393 OPERATOR_RETVAL_CHECK(retval);
394
395 if (retval == OPERATOR_FINISHED) {
396 return OPERATOR_FINISHED;
397 }
398
401}
402
404 wmOperator *op,
405 const wmEvent *event)
406{
407 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
408}
409
411{
412 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
413}
414
416{
417 ot->name = "Grease Pencil Sculpt";
418 ot->idname = "GREASE_PENCIL_OT_sculpt_paint";
419 ot->description = "Sculpt strokes in the active Grease Pencil object";
420
425
427
429}
430
432
433/* -------------------------------------------------------------------- */
436
438{
440 return false;
441 }
443 return false;
444 }
445 return true;
446}
447
449 wmOperator *op,
450 const wmEvent *event)
451{
452 const Scene *scene = CTX_data_scene(C);
453 const Object *object = CTX_data_active_object(C);
454 if (!object || object->type != OB_GREASE_PENCIL) {
455 return OPERATOR_CANCELLED;
456 }
457
458 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
460 const Brush *brush = BKE_paint_brush_for_read(paint);
461 if (brush == nullptr) {
462 return OPERATOR_CANCELLED;
463 }
464
467 if (drawings.is_empty()) {
468 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw weight on");
469 return OPERATOR_CANCELLED;
470 }
471
472 const int active_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
473 if (active_defgroup_nr >= 0 && BKE_object_defgroup_active_is_locked(object)) {
474 BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
475 return OPERATOR_CANCELLED;
476 }
477
479 op,
485 event->type);
486
487 const wmOperatorStatus retval = op->type->modal(C, op, event);
488 OPERATOR_RETVAL_CHECK(retval);
489
490 if (retval == OPERATOR_FINISHED) {
491 return OPERATOR_FINISHED;
492 }
493
496}
497
499 wmOperator *op,
500 const wmEvent *event)
501{
502 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
503}
504
509
511{
512 ot->name = "Grease Pencil Paint Weight";
513 ot->idname = "GREASE_PENCIL_OT_weight_brush_stroke";
514 ot->description = "Draw weight on stroke points in the active Grease Pencil object";
515
520
522
524}
525
527
528/* -------------------------------------------------------------------- */
531
533{
535 return false;
536 }
538 return false;
539 }
540 return true;
541}
542
544 wmOperator *op,
545 const wmEvent *event)
546{
547 const Scene *scene = CTX_data_scene(C);
548 const Object *object = CTX_data_active_object(C);
549 if (!object || object->type != OB_GREASE_PENCIL) {
550 return OPERATOR_CANCELLED;
551 }
552
553 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
554 if (!ed::greasepencil::has_editable_layer(grease_pencil)) {
555 BKE_report(op->reports, RPT_ERROR, "No editable Grease Pencil layer");
556 return OPERATOR_CANCELLED;
557 }
558
560 const Brush *brush = BKE_paint_brush_for_read(paint);
561 if (brush == nullptr) {
562 return OPERATOR_CANCELLED;
563 }
564
565 /* Ensure a drawing at the current keyframe. */
566 bool inserted_keyframe = false;
567 /* For the vertex paint tools, we don't want the auto-key to create an empty keyframe, so we
568 * duplicate the previous key. */
569 const bool use_duplicate_previous_key = true;
570 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
571 if (layer->is_editable() &&
573 *scene, grease_pencil, *layer, use_duplicate_previous_key, inserted_keyframe))
574 {
575 inserted_keyframe = true;
576 }
577 }
578 if (!inserted_keyframe) {
579 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
580 return OPERATOR_CANCELLED;
581 }
583
585 op,
591 event->type);
592
593 const wmOperatorStatus retval = op->type->modal(C, op, event);
594 OPERATOR_RETVAL_CHECK(retval);
595
596 if (retval == OPERATOR_FINISHED) {
597 return OPERATOR_FINISHED;
598 }
599
602}
603
605 wmOperator *op,
606 const wmEvent *event)
607{
608 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
609}
610
615
617{
618 ot->name = "Grease Pencil Paint Vertex";
619 ot->idname = "GREASE_PENCIL_OT_vertex_brush_stroke";
620 ot->description = "Draw on vertex colors in the active Grease Pencil object";
621
626
628
630}
631
633
634/* -------------------------------------------------------------------- */
637
640
641 /* Material of the generated stroke. */
643 /* Toggle inverse filling. */
644 bool invert = false;
645 /* Toggle precision mode. */
646 bool precision = false;
647
648 /* Methods for gap filling. */
650 /* Length of extension lines. */
652 /* Cut off extension lines at first intersection. */
654
655 /* Draw boundaries stroke overlay. */
657 /* Draw extension lines overlay. */
659
660 /* Mouse position where fill was initialized */
662 /* Extension lines drag mode is enabled (middle mouse button). */
664 /* Mouse position where the extension mode was enabled. */
666
667 /* Overlay draw callback for helper lines, etc. */
669
672 const int material_index,
673 const bool invert,
674 const bool precision)
675 {
677
679 const Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
687 const bool brush_invert = brush.gpencil_settings->fill_direction == BRUSH_DIR_IN;
688 /* Both operator properties and brush properties can invert. Actual invert is XOR of both. */
689 const bool combined_invert = (invert != brush_invert);
690
691 return {layer,
693 combined_invert,
694 precision,
700 }
701};
702
703/* Find and cut extension lines at intersections with other lines and strokes. */
705 ed::greasepencil::ExtensionData &extension_data,
706 Span<int> origin_drawings,
707 Span<int> origin_points)
708{
709 const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
710 const Scene &scene = *CTX_data_scene(&C);
711 const Object &object = *CTX_data_active_object(&C);
712 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
713
714 const float4x4 view_matrix = float4x4(rv3d.viewmat);
715
717 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
718
719 const IndexRange bvh_extension_range = extension_data.lines.starts.index_range();
720 Array<int> bvh_curve_offsets_data(drawings.size() + 1);
721 for (const int i : drawings.index_range()) {
722 bvh_curve_offsets_data[i] = drawings[i].drawing.strokes().points_num();
723 }
725 bvh_curve_offsets_data, bvh_extension_range.size());
726
727 /* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
728 * necessary. Not all entries are added to the BVH tree. */
729 const int max_bvh_lines = bvh_curve_offsets.data().last();
730 /* Cached view positions for lines. */
731 Array<float2> view_starts(max_bvh_lines);
732 Array<float2> view_ends(max_bvh_lines);
733
734 BVHTree *tree = BLI_bvhtree_new(max_bvh_lines, 0.0f, 4, 6);
736
737 /* Insert extension lines for intersection.
738 * Note: These are added first, so that the extension index matches its index in BVH data. */
739 for (const int i_line : bvh_extension_range.index_range()) {
740 const float2 start =
741 math::transform_point(view_matrix, extension_data.lines.starts[i_line]).xy();
742 const float2 end = math::transform_point(view_matrix, extension_data.lines.ends[i_line]).xy();
743
744 const int bvh_index = bvh_extension_range[i_line];
745 view_starts[bvh_index] = start;
746 view_ends[bvh_index] = end;
747
748 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
749 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
750 }
751
752 /* Insert segments for cutting extensions on stroke intersection. */
753 for (const int i_drawing : drawings.index_range()) {
754 const ed::greasepencil::DrawingInfo &info = drawings[i_drawing];
756 const OffsetIndices points_by_curve = curves.points_by_curve();
757 const Span<float3> positions = curves.positions();
758 const VArray<bool> cyclic = curves.cyclic();
759 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
760 const float4x4 layer_to_view = view_matrix * layer.to_world_space(object);
761
762 for (const int i_curve : curves.curves_range()) {
763 const bool is_cyclic = cyclic[i_curve];
764 const IndexRange points = points_by_curve[i_curve];
765
766 for (const int i_point : points.drop_back(1)) {
767 const float2 start = math::transform_point(layer_to_view, positions[i_point]).xy();
768 const float2 end = math::transform_point(layer_to_view, positions[i_point + 1]).xy();
769
770 const int bvh_index = bvh_curve_offsets[i_drawing][i_point];
771 view_starts[bvh_index] = start;
772 view_ends[bvh_index] = end;
773
774 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
775 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
776 }
777 /* Last->first point segment only used for cyclic curves. */
778 if (is_cyclic) {
779 const float2 start = math::transform_point(layer_to_view, positions[points.last()]).xy();
780 const float2 end = math::transform_point(layer_to_view, positions[points.first()]).xy();
781
782 const int bvh_index = bvh_curve_offsets[i_drawing][points.last()];
783 view_starts[bvh_index] = start;
784 view_ends[bvh_index] = end;
785
786 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
787 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
788 }
789 }
790 }
791
793
794 struct RaycastArgs {
795 Span<float2> starts;
796 Span<float2> ends;
797 /* Indices that may need to be ignored to avoid self-intersection. */
798 int ignore_index1;
799 int ignore_index2;
800 };
801 BVHTree_RayCastCallback callback =
802 [](void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) {
803 using Result = math::isect_result<float2>;
804
805 const RaycastArgs &args = *static_cast<const RaycastArgs *>(userdata);
806 if (ELEM(index, args.ignore_index1, args.ignore_index2)) {
807 return;
808 }
809
810 const float2 ray_start = float2(ray->origin);
811 const float2 ray_end = ray_start + float2(ray->direction) * ray->radius;
812 const float2 &line_start = args.starts[index];
813 const float2 &line_end = args.ends[index];
814 Result result = math::isect_seg_seg(ray_start, ray_end, line_start, line_end);
815 if (result.kind <= 0) {
816 return;
817 }
818 const float dist = result.lambda * math::distance(ray_start, ray_end);
819 if (dist >= hit->dist) {
820 return;
821 }
822 /* These always need to be calculated for the BVH traversal function. */
823 hit->index = index;
824 hit->dist = result.lambda * math::distance(ray_start, ray_end);
825 /* Don't need the hit point, only the lambda. */
826 hit->no[0] = result.lambda;
827 };
828
829 /* Store intersections first before applying to the data, so that subsequent ray-casts use
830 * original end points until all intersections are found. */
831 Vector<float3> new_extension_ends(extension_data.lines.ends.size());
832 for (const int i_line : extension_data.lines.starts.index_range()) {
833 const float2 start =
834 math::transform_point(view_matrix, extension_data.lines.starts[i_line]).xy();
835 const float2 end = math::transform_point(view_matrix, extension_data.lines.ends[i_line]).xy();
836 float length;
837 const float2 dir = math::normalize_and_get_length(end - start, length);
838
839 const int bvh_index = i_line;
840 const int origin_drawing = origin_drawings[i_line];
841 const int origin_point = origin_points[i_line];
842 const int bvh_origin_index = bvh_curve_offsets[origin_drawing][origin_point];
843
844 RaycastArgs args = {view_starts, view_ends, bvh_index, bvh_origin_index};
845 BVHTreeRayHit hit;
846 hit.index = -1;
847 hit.dist = FLT_MAX;
849 tree, float3(start, 0.0f), float3(dir, 0.0f), length, &hit, callback, &args);
850
851 if (hit.index >= 0) {
852 const float lambda = hit.no[0];
853 new_extension_ends[i_line] = math::interpolate(
854 extension_data.lines.starts[i_line], extension_data.lines.ends[i_line], lambda);
855 }
856 else {
857 new_extension_ends[i_line] = extension_data.lines.ends[i_line];
858 }
859 }
860
861 extension_data.lines.ends = std::move(new_extension_ends);
862}
863
864/* Find closest point in each circle and generate extension lines between such pairs. */
866 const bContext &C,
867 ed::greasepencil::ExtensionData &extension_data,
868 Span<int> /*origin_drawings*/,
869 Span<int> /*origin_points*/)
870{
871 const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
872 const Scene &scene = *CTX_data_scene(&C);
873 const Object &object = *CTX_data_active_object(&C);
874 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
875
876 const float4x4 view_matrix = float4x4(rv3d.viewmat);
877
879 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
880
881 const IndexRange circles_range = extension_data.circles.centers.index_range();
882 /* TODO Include high-curvature feature points. */
883 const IndexRange feature_points_range = circles_range.after(0);
884 const IndexRange kd_points_range = IndexRange(circles_range.size() +
885 feature_points_range.size());
886
887 /* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
888 * necessary. Not all entries are added to the BVH tree. */
889 const int max_kd_entries = kd_points_range.size();
890 /* Cached view positions for lines. */
891 Array<float2> view_centers(max_kd_entries);
892 Array<float> view_radii(max_kd_entries);
893
894 KDTree_2d *kdtree = BLI_kdtree_2d_new(max_kd_entries);
895
896 /* Insert points for overlap tests. */
897 for (const int point_i : circles_range.index_range()) {
898 const float2 center =
899 math::transform_point(view_matrix, extension_data.circles.centers[point_i]).xy();
900 const float radius = math::average(math::to_scale(view_matrix)) *
901 extension_data.circles.radii[point_i];
902
903 const int kd_index = circles_range[point_i];
904 view_centers[kd_index] = center;
905 view_radii[kd_index] = radius;
906
907 BLI_kdtree_2d_insert(kdtree, kd_index, center);
908 }
909 for (const int i_point : feature_points_range.index_range()) {
910 /* TODO Insert feature points into the KDTree. */
911 UNUSED_VARS(i_point);
912 }
913 BLI_kdtree_2d_balance(kdtree);
914
915 struct {
916 Vector<float3> starts;
917 Vector<float3> ends;
918 } connection_lines;
919 /* Circles which can be kept because they generate no extension lines. */
920 Vector<int> keep_circle_indices;
921 keep_circle_indices.reserve(circles_range.size());
922
923 for (const int point_i : circles_range.index_range()) {
924 const int kd_index = circles_range[point_i];
925 const float2 center = view_centers[kd_index];
926 const float radius = view_radii[kd_index];
927
928 bool found = false;
929 BLI_kdtree_2d_range_search_cb_cpp(
930 kdtree,
931 center,
932 radius,
933 [&](const int other_point_i, const float * /*co*/, float /*dist_sq*/) {
934 if (other_point_i == kd_index) {
935 return true;
936 }
937
938 found = true;
939 connection_lines.starts.append(extension_data.circles.centers[point_i]);
940 if (circles_range.contains(other_point_i)) {
941 connection_lines.ends.append(extension_data.circles.centers[other_point_i]);
942 }
943 else if (feature_points_range.contains(other_point_i)) {
944 /* TODO copy feature point to connection_lines (beware of start index!). */
945 connection_lines.ends.append(float3(0));
946 }
947 else {
949 }
950 return true;
951 });
952 /* Keep the circle if no extension line was found. */
953 if (!found) {
954 keep_circle_indices.append(point_i);
955 }
956 }
957
958 BLI_kdtree_2d_free(kdtree);
959
960 /* Add new extension lines. */
961 extension_data.lines.starts.extend(connection_lines.starts);
962 extension_data.lines.ends.extend(connection_lines.ends);
963 /* Remove circles that formed extension lines. */
964 Vector<float3> old_centers = std::move(extension_data.circles.centers);
965 Vector<float> old_radii = std::move(extension_data.circles.radii);
966 extension_data.circles.centers.resize(keep_circle_indices.size());
967 extension_data.circles.radii.resize(keep_circle_indices.size());
968 array_utils::gather(old_centers.as_span(),
969 keep_circle_indices.as_span(),
970 extension_data.circles.centers.as_mutable_span());
971 array_utils::gather(old_radii.as_span(),
972 keep_circle_indices.as_span(),
973 extension_data.circles.radii.as_mutable_span());
974}
975
977 const bContext &C, const GreasePencilFillOpData &op_data)
978{
979 const Scene &scene = *CTX_data_scene(&C);
980 const Object &object = *CTX_data_active_object(&C);
981 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
982
984 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
985
986 ed::greasepencil::ExtensionData extension_data;
987 Vector<int> origin_points;
988 Vector<int> origin_drawings;
989 for (const int i_drawing : drawings.index_range()) {
990 const ed::greasepencil::DrawingInfo &info = drawings[i_drawing];
992 const OffsetIndices points_by_curve = curves.points_by_curve();
993 const Span<float3> positions = curves.positions();
994 const VArray<bool> cyclic = curves.cyclic();
995 const float4x4 layer_to_world = grease_pencil.layer(info.layer_index).to_world_space(object);
996
997 for (const int i_curve : curves.curves_range()) {
998 const IndexRange points = points_by_curve[i_curve];
999 /* No extension lines on cyclic curves. */
1000 if (cyclic[i_curve]) {
1001 continue;
1002 }
1003 /* Can't compute directions for single-point curves. */
1004 if (points.size() < 2) {
1005 continue;
1006 }
1007
1008 const float3 pos_head = math::transform_point(layer_to_world, positions[points[0]]);
1009 const float3 pos_tail = math::transform_point(layer_to_world, positions[points.last()]);
1010 const float3 pos_head_next = math::transform_point(layer_to_world, positions[points[1]]);
1011 const float3 pos_tail_prev = math::transform_point(layer_to_world,
1012 positions[points.last(1)]);
1013 const float3 dir_head = math::normalize(pos_head - pos_head_next);
1014 const float3 dir_tail = math::normalize(pos_tail - pos_tail_prev);
1015 /* Initial length before intersection tests. */
1016 const float length = op_data.extension_length;
1017
1018 switch (op_data.extension_mode) {
1020 extension_data.lines.starts.append(pos_head);
1021 extension_data.lines.ends.append(pos_head + dir_head * length);
1022 origin_drawings.append(i_drawing);
1023 origin_points.append(points.first());
1024
1025 extension_data.lines.starts.append(pos_tail);
1026 extension_data.lines.ends.append(pos_tail + dir_tail * length);
1027 origin_drawings.append(i_drawing);
1028 /* Segment index is the start point. */
1029 origin_points.append(points.last() - 1);
1030 break;
1032 extension_data.circles.centers.append(pos_head);
1033 extension_data.circles.radii.append(length);
1034 origin_drawings.append(i_drawing);
1035 origin_points.append(points.first());
1036
1037 extension_data.circles.centers.append(pos_tail);
1038 extension_data.circles.radii.append(length);
1039 origin_drawings.append(i_drawing);
1040 /* Segment index is the start point. */
1041 origin_points.append(points.last() - 1);
1042 break;
1043 }
1044 }
1045 }
1046
1047 switch (op_data.extension_mode) {
1049 /* Intersection test against strokes and other extension lines. */
1050 if (op_data.extension_cut) {
1051 grease_pencil_fill_extension_cut(C, extension_data, origin_drawings, origin_points);
1052 }
1053 break;
1056 C, extension_data, origin_drawings, origin_points);
1057 break;
1058 }
1059
1060 return extension_data;
1061}
1062
1064 const GreasePencilFillOpData &op_data)
1065{
1066 const bool is_extend = (op_data.extension_mode == GP_FILL_EMODE_EXTEND);
1067
1068 WorkspaceStatus status(&C);
1069 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
1070 status.item(IFACE_("Fill"), ICON_MOUSE_LMB);
1071 status.item(
1072 fmt::format("{} ({})", IFACE_("Mode"), (is_extend ? IFACE_("Extend") : IFACE_("Radius"))),
1073 ICON_EVENT_S);
1074 status.item(fmt::format("{} ({:.3f})",
1075 is_extend ? IFACE_("Length") : IFACE_("Radius"),
1076 op_data.extension_length),
1077 ICON_MOUSE_MMB_SCROLL);
1078 if (is_extend) {
1079 status.item_bool(IFACE_("Collision"), op_data.extension_cut, ICON_EVENT_D);
1080 }
1081}
1082
1083/* Draw callback for fill tool overlay. */
1084static void grease_pencil_fill_overlay_cb(const bContext *C, ARegion * /*region*/, void *arg)
1085{
1086 const ARegion &region = *CTX_wm_region(C);
1087 const RegionView3D &rv3d = *CTX_wm_region_view3d(C);
1088 const Scene &scene = *CTX_data_scene(C);
1089 const Object &object = *CTX_data_active_object(C);
1090 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
1091 auto &op_data = *static_cast<GreasePencilFillOpData *>(arg);
1092
1093 const float4x4 world_to_view = float4x4(rv3d.viewmat);
1094 /* Note; the initial view matrix is already set, clear to draw in view space. */
1096
1097 const ColorGeometry4f stroke_curves_color = ColorGeometry4f(1, 0, 0, 1);
1098 const ColorGeometry4f extension_lines_color = ColorGeometry4f(0, 1, 1, 1);
1099 const ColorGeometry4f extension_circles_color = ColorGeometry4f(1, 0.5, 0, 1);
1100
1102 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
1103
1104 if (op_data.show_boundaries) {
1106 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
1107
1108 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1109 const IndexMask curve_mask = info.drawing.strokes().curves_range();
1111 stroke_curves_color, info.drawing.strokes().points_num());
1112 const float4x4 layer_to_world = grease_pencil.layer(info.layer_index).to_world_space(object);
1113 const bool use_xray = false;
1114 const float radius_scale = 1.0f;
1115
1117 int2(region.winx, region.winy),
1118 object,
1119 info.drawing,
1120 layer_to_world,
1121 curve_mask,
1122 colors,
1123 use_xray,
1124 radius_scale);
1125 }
1126 }
1127
1128 if (op_data.show_extension) {
1130 *C, op_data);
1131
1132 const float line_width = 2.0f;
1133
1134 const IndexRange lines_range = extensions.lines.starts.index_range();
1135 if (!lines_range.is_empty()) {
1137 extension_lines_color, lines_range.size());
1138
1140 lines_range,
1141 extensions.lines.starts,
1142 extensions.lines.ends,
1143 line_colors,
1144 line_width);
1145 }
1146 const IndexRange circles_range = extensions.circles.centers.index_range();
1147 if (!circles_range.is_empty()) {
1149 extension_circles_color, circles_range.size());
1150
1152 world_to_view,
1153 circles_range,
1154 extensions.circles.centers,
1156 circle_colors,
1157 float2(region.winx, region.winy),
1158 line_width,
1159 false);
1160 }
1161 }
1162}
1163
1165 GreasePencilFillOpData &op_data)
1166{
1167 const bool needs_overlay = op_data.show_boundaries || op_data.show_extension;
1168
1169 if (needs_overlay) {
1170 if (op_data.overlay_cb_handle == nullptr) {
1173 }
1174 }
1175 else {
1176 if (op_data.overlay_cb_handle) {
1177 ED_region_draw_cb_exit(region.runtime->type, op_data.overlay_cb_handle);
1178 op_data.overlay_cb_handle = nullptr;
1179 }
1180 }
1181}
1182
1189
1190/* Layer mode defines layers where only marked boundary strokes are used. */
1192 eGP_FillLayerModes fill_layer_mode)
1193{
1194 BLI_assert(grease_pencil.has_active_layer());
1195 const IndexRange all_layers = grease_pencil.layers().index_range();
1196 const int active_layer_index = *grease_pencil.get_layer_index(*grease_pencil.get_active_layer());
1197
1198 switch (fill_layer_mode) {
1200 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1201 return index != active_layer_index;
1202 });
1204 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1205 return index != active_layer_index + 1;
1206 });
1208 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1209 return index != active_layer_index - 1;
1210 });
1212 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1213 return index <= active_layer_index;
1214 });
1216 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1217 return index >= active_layer_index;
1218 });
1220 return VArray<bool>::ForFunc(all_layers.size(), [grease_pencil](const int index) {
1221 return !grease_pencil.layers()[index]->is_visible();
1222 });
1223 }
1224 return {};
1225}
1226
1227/* Array of visible drawings to use as borders for generating a stroke in the editable drawing on
1228 * the active layer. This is provided for every frame in the multi-frame edit range. */
1233
1235 GreasePencil &grease_pencil,
1236 bke::greasepencil::Layer &target_layer)
1237{
1238 using namespace bke::greasepencil;
1241
1242 const ToolSettings *toolsettings = scene.toolsettings;
1243 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
1245 const bool use_autokey = blender::animrig::is_autokey_on(&scene);
1246 const bool use_duplicate_frame = (scene.toolsettings->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST);
1247 const int target_layer_index = *grease_pencil.get_layer_index(target_layer);
1248
1249 VectorSet<int> target_frames;
1250 /* Add drawing on the current frame. */
1251 target_frames.add(scene.r.cfra);
1252 /* Multi-frame edit: Add drawing on frames that are selected in any layer. */
1253 if (use_multi_frame_editing) {
1254 for (const Layer *layer : grease_pencil.layers()) {
1255 for (const auto [frame_number, frame] : layer->frames().items()) {
1256 if (frame.is_selected()) {
1257 target_frames.add(frame_number);
1258 }
1259 }
1260 }
1261 }
1262
1263 /* Create new drawings when autokey is enabled. */
1264 if (use_autokey) {
1265 for (const int frame_number : target_frames) {
1266 if (!target_layer.frames().contains(frame_number)) {
1267 if (use_duplicate_frame) {
1268 grease_pencil.insert_duplicate_frame(
1269 target_layer, *target_layer.start_frame_at(frame_number), frame_number, false);
1270 }
1271 else {
1272 grease_pencil.insert_frame(target_layer, frame_number);
1273 }
1274 }
1275 }
1276 }
1277
1279 for (const int frame_number : target_frames) {
1280 if (Drawing *target_drawing = grease_pencil.get_editable_drawing_at(target_layer,
1281 frame_number))
1282 {
1283 MutableDrawingInfo target = {*target_drawing, target_layer_index, frame_number, 1.0f};
1284
1285 Vector<DrawingInfo> sources;
1286 for (const Layer *source_layer : grease_pencil.layers()) {
1287 if (const Drawing *source_drawing = grease_pencil.get_drawing_at(*source_layer,
1288 frame_number))
1289 {
1290 const int source_layer_index = *grease_pencil.get_layer_index(*source_layer);
1291 sources.append({*source_drawing, source_layer_index, frame_number, 0});
1292 }
1293 }
1294
1295 drawings.append({std::move(target), std::move(sources)});
1296 }
1297 }
1298
1299 return drawings;
1300}
1301
1303{
1304 const int iterations = 20;
1305 if (curves.is_empty()) {
1306 return;
1307 }
1308 if (stroke_mask.is_empty()) {
1309 return;
1310 }
1311
1312 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1313 const OffsetIndices points_by_curve = curves.points_by_curve();
1314 const VArray<bool> cyclic = curves.cyclic();
1315 const VArray<bool> point_selection = VArray<bool>::ForSingle(true, curves.points_num());
1316
1317 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1319 points_by_curve,
1320 point_selection,
1321 cyclic,
1322 iterations,
1323 1.0f,
1324 false,
1325 true,
1326 positions.span);
1327 positions.finish();
1328 curves.tag_positions_changed();
1329}
1330
1332{
1333 const OffsetIndices points_by_curve = curves.points_by_curve();
1334 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1335
1336 IndexMaskMemory memory;
1337 IndexMask points_to_keep = IndexMask::from_predicate(
1338 curves.points_range(), GrainSize(2048), memory, [&](const int64_t i) {
1339 const int curve_i = point_to_curve_map[i];
1340 const IndexRange points = points_by_curve[curve_i];
1341 if (points.size() <= 2) {
1342 return true;
1343 }
1344 const int local_i = i - points.start();
1345 return (local_i % int(math::pow(2.0f, float(step))) == 0) || points.last() == i;
1346 });
1347
1348 return bke::curves_copy_point_selection(curves, points_to_keep, {});
1349}
1350
1351static bool grease_pencil_apply_fill(bContext &C, wmOperator &op, const wmEvent &event)
1352{
1356
1357 constexpr const ed::greasepencil::FillToolFitMethod fit_method =
1359 /* Debug setting: keep image data blocks for inspection. */
1360 constexpr const bool keep_images = false;
1361
1362 ARegion &region = *CTX_wm_region(&C);
1363 /* Perform bounds check. */
1364 const bool in_bounds = BLI_rcti_isect_pt_v(&region.winrct, event.xy);
1365 if (!in_bounds) {
1366 return false;
1367 }
1368
1369 wmWindow &win = *CTX_wm_window(&C);
1371 const Scene &scene = *CTX_data_scene(&C);
1372 Object &object = *CTX_data_active_object(&C);
1373 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1374 auto &op_data = *static_cast<GreasePencilFillOpData *>(op.customdata);
1375 const ToolSettings &ts = *CTX_data_tool_settings(&C);
1376 Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
1377 const float2 mouse_position = float2(event.mval);
1378 const int simplify_levels = brush.gpencil_settings->fill_simplylvl;
1379 const std::optional<float> alpha_threshold =
1381 std::nullopt :
1382 std::make_optional(brush.gpencil_settings->fill_threshold);
1383 const bool on_back = (ts.gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK);
1384 const bool auto_remove_fill_guides = (brush.gpencil_settings->flag &
1386
1387 if (!grease_pencil.has_active_layer()) {
1388 return false;
1389 }
1390 /* Add drawings in the active layer if autokey is enabled. */
1392 scene, grease_pencil, *grease_pencil.get_active_layer());
1393
1394 const VArray<bool> boundary_layers = get_fill_boundary_layers(
1395 grease_pencil, eGP_FillLayerModes(brush.gpencil_settings->fill_layer_mode));
1396
1397 bool did_create_fill = false;
1398 for (const FillToolTargetInfo &info : target_drawings) {
1399 const Layer &layer = *grease_pencil.layers()[info.target.layer_index];
1400
1402 C, op_data);
1403
1404 bke::CurvesGeometry fill_curves = fill_strokes(view_context,
1405 brush,
1406 scene,
1407 layer,
1408 boundary_layers,
1409 info.sources,
1410 op_data.invert,
1411 alpha_threshold,
1412 mouse_position,
1413 extensions,
1414 fit_method,
1415 op_data.material_index,
1416 keep_images);
1417 if (fill_curves.is_empty()) {
1418 continue;
1419 }
1420
1421 smooth_fill_strokes(fill_curves, fill_curves.curves_range());
1422
1423 if (simplify_levels > 0) {
1424 fill_curves = simplify_fixed(fill_curves, brush.gpencil_settings->fill_simplylvl);
1425 }
1426
1427 bke::CurvesGeometry &dst_curves = info.target.drawing.strokes_for_write();
1428 if (auto_remove_fill_guides) {
1429 /* Remove strokes that were created using the fill tool as boundary strokes. */
1431 }
1432
1433 /* If the `fill_strokes` function creates the "fill_opacity" attribute, make sure that we
1434 * initialize this to full opacity on the target geometry. */
1435 if (fill_curves.attributes().contains("fill_opacity") &&
1436 !dst_curves.attributes().contains("fill_opacity"))
1437 {
1438 bke::SpanAttributeWriter<float> fill_opacities =
1440 "fill_opacity",
1443 fill_opacities.finish();
1444 }
1445
1446 Curves *dst_curves_id = curves_new_nomain(dst_curves);
1447 Curves *fill_curves_id = curves_new_nomain(fill_curves);
1448 const Array<bke::GeometrySet> geometry_sets = {
1449 bke::GeometrySet::from_curves(on_back ? fill_curves_id : dst_curves_id),
1450 bke::GeometrySet::from_curves(on_back ? dst_curves_id : fill_curves_id)};
1451 const int num_new_curves = fill_curves.curves_num();
1452 const IndexRange new_curves_range = (on_back ?
1453 IndexRange(num_new_curves) :
1454 dst_curves.curves_range().after(num_new_curves));
1455
1456 bke::GeometrySet joined_geometry_set = geometry::join_geometries(geometry_sets, {});
1457 if (joined_geometry_set.has_curves()) {
1458 dst_curves = joined_geometry_set.get_curves_for_write()->geometry.wrap();
1459 info.target.drawing.tag_topology_changed();
1460
1461 /* Compute texture matrix for the new curves. */
1462 const ed::greasepencil::DrawingPlacement placement(
1463 scene, region, *view_context.v3d, object, &layer);
1465 &scene, &region, mouse_position, placement);
1466 Array<float4x2> texture_matrices(num_new_curves, texture_space);
1467 info.target.drawing.set_texture_matrices(texture_matrices, new_curves_range);
1468 }
1469
1470 did_create_fill = true;
1471 }
1472
1473 if (!did_create_fill) {
1474 BKE_reportf(op.reports, RPT_ERROR, "Unable to fill unclosed areas");
1475 }
1476
1478
1479 /* Save extend value for next operation. */
1480 brush.gpencil_settings->fill_extend_fac = op_data.extension_length /
1483
1484 return true;
1485}
1486
1488{
1490
1491 Main &bmain = *CTX_data_main(&C);
1492 Scene &scene = *CTX_data_scene(&C);
1494 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1496 Brush &brush = *BKE_paint_brush(&paint);
1497
1498 Layer *layer = grease_pencil.get_active_layer();
1499 /* Cannot paint in locked layer. */
1500 if (layer && layer->is_locked()) {
1501 return false;
1502 }
1503 if (layer == nullptr) {
1504 layer = &grease_pencil.add_layer("GP_Layer");
1505 }
1506
1507 if (brush.gpencil_settings == nullptr) {
1509 }
1519
1520 Material *material = BKE_grease_pencil_object_material_ensure_from_brush(&bmain, &ob, &brush);
1521 const int material_index = BKE_object_material_index_get(&ob, material);
1522
1523 const bool invert = RNA_boolean_get(op.ptr, "invert");
1524 const bool precision = RNA_boolean_get(op.ptr, "precision");
1525
1526 op.customdata = MEM_new<GreasePencilFillOpData>(
1527 __func__,
1528 GreasePencilFillOpData::from_context(C, *layer, material_index, invert, precision));
1529 return true;
1530}
1531
1533{
1534 const ARegion &region = *CTX_wm_region(&C);
1536 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1537
1539
1540 if (op.customdata) {
1541 auto &op_data = *static_cast<GreasePencilFillOpData *>(op.customdata);
1542
1543 if (op_data.overlay_cb_handle) {
1544 ED_region_draw_cb_exit(region.runtime->type, op_data.overlay_cb_handle);
1545 op_data.overlay_cb_handle = nullptr;
1546 }
1547
1548 MEM_delete(static_cast<GreasePencilFillOpData *>(op.customdata));
1549 op.customdata = nullptr;
1550 }
1551
1552 /* Clear status message area. */
1553 ED_workspace_status_text(&C, nullptr);
1554
1556
1559}
1560
1562 wmOperator *op,
1563 const wmEvent * /*event*/)
1564{
1565 const ARegion &region = *CTX_wm_region(C);
1567 Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
1569 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1570
1571 /* Fill tool needs a material (cannot use default material). */
1573 brush.gpencil_settings->material == nullptr)
1574 {
1575 BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material");
1576 return OPERATOR_CANCELLED;
1577 }
1578 if (BKE_object_material_get(&ob, ob.actcol) == nullptr) {
1579 BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material");
1580 return OPERATOR_CANCELLED;
1581 }
1582 if (!grease_pencil_fill_init(*C, *op)) {
1584 return OPERATOR_CANCELLED;
1585 }
1586 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1587
1590 grease_pencil_fill_update_overlay(region, op_data);
1591
1594
1595 /* Add a modal handler for this operator. */
1597
1599}
1600
1612
1614 wmOperator *op,
1615 const wmEvent *event)
1616{
1617 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1618 /* Extension line length increment, for normal and precise mode respectively. */
1619 const float extension_delta = (op_data.precision ? 0.002f : 0.02f);
1620
1621 switch (event->val) {
1622 case int(FillToolModalKey::Cancel):
1623 return OPERATOR_CANCELLED;
1624
1625 case int(FillToolModalKey::Confirm): {
1626 /* Ignore in extension mode. */
1627 if (op_data.is_extension_drag_active) {
1628 break;
1629 }
1630
1631 op_data.fill_mouse_pos = float2(event->mval);
1633 }
1634
1636 if (op_data.show_extension) {
1637 /* Toggle mode. */
1638 if (op_data.extension_mode == GP_FILL_EMODE_EXTEND) {
1639 op_data.extension_mode = GP_FILL_EMODE_RADIUS;
1640 }
1641 else {
1642 op_data.extension_mode = GP_FILL_EMODE_EXTEND;
1643 }
1644 grease_pencil_update_extend(*C, op_data);
1645 }
1646 break;
1647
1649 op_data.extension_length = std::max(op_data.extension_length - extension_delta, 0.0f);
1650 grease_pencil_update_extend(*C, op_data);
1651 break;
1652
1654 op_data.extension_length = std::min(op_data.extension_length + extension_delta, 10.0f);
1655 grease_pencil_update_extend(*C, op_data);
1656 break;
1657
1659 if (event->val == KM_PRESS) {
1660 /* Consider initial offset as zero position. */
1661 op_data.is_extension_drag_active = true;
1662 /* TODO This is the GPv2 logic and it's weird. Should be reconsidered, for now use the
1663 * same method. */
1664 const float2 base_pos = float2(event->mval);
1665 constexpr const float gap = 300.0f;
1666 op_data.extension_mouse_pos = (math::distance(base_pos, op_data.fill_mouse_pos) >= gap ?
1667 base_pos :
1668 base_pos - float2(gap, 0));
1670 }
1671 if (event->val == KM_RELEASE) {
1673 op_data.is_extension_drag_active = false;
1674 }
1675 /* Update cursor line. */
1678 break;
1679 }
1680
1682 if (op_data.show_extension) {
1683 op_data.extension_cut = !op_data.extension_cut;
1684 grease_pencil_update_extend(*C, op_data);
1685 }
1686 break;
1687
1688 case int(FillToolModalKey::Invert):
1689 op_data.invert = !op_data.invert;
1690 break;
1691
1693 op_data.precision = !op_data.precision;
1694 break;
1695
1696 default:
1698 break;
1699 }
1701}
1702
1704{
1705 const RegionView3D &rv3d = *CTX_wm_region_view3d(C);
1706
1707 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1708
1710 if (!op_data.show_extension) {
1711 /* Apply fill immediately if "Visual Aids" (aka. extension lines) is disabled. */
1712 op_data.fill_mouse_pos = float2(event->mval);
1713 estate = (grease_pencil_apply_fill(*C, *op, *event) ? OPERATOR_FINISHED : OPERATOR_CANCELLED);
1714 }
1715 else {
1716 estate = OPERATOR_RUNNING_MODAL;
1717 switch (event->type) {
1718 case EVT_MODAL_MAP:
1719 estate = grease_pencil_fill_event_modal_map(C, op, event);
1720 break;
1721 case MOUSEMOVE: {
1722 if (!op_data.is_extension_drag_active) {
1723 break;
1724 }
1725
1726 const Object &ob = *CTX_data_active_object(C);
1727 const float pixel_size = ED_view3d_pixel_size(&rv3d, ob.loc);
1728 const float2 mouse_pos = float2(event->mval);
1729 const float initial_dist = math::distance(op_data.extension_mouse_pos,
1730 op_data.fill_mouse_pos);
1731 const float current_dist = math::distance(mouse_pos, op_data.fill_mouse_pos);
1732
1733 float delta = (current_dist - initial_dist) * pixel_size * 0.5f;
1734 op_data.extension_length = std::clamp(op_data.extension_length + delta, 0.0f, 10.0f);
1735
1736 /* Update cursor line and extend lines. */
1739
1740 grease_pencil_update_extend(*C, op_data);
1741 break;
1742 }
1743 default:
1744 break;
1745 }
1746 }
1747
1748 /* Process last operations before exiting. */
1749 switch (estate) {
1750 case OPERATOR_FINISHED:
1753 break;
1754
1755 case OPERATOR_CANCELLED:
1757 break;
1758
1759 default:
1760 break;
1761 }
1762
1763 return estate;
1764}
1765
1767{
1769}
1770
1772{
1773 PropertyRNA *prop;
1774
1775 ot->name = "Grease Pencil Fill";
1776 ot->idname = "GREASE_PENCIL_OT_fill";
1777 ot->description = "Fill with color the shape formed by strokes";
1778
1780 ot->invoke = grease_pencil_fill_invoke;
1782 ot->cancel = grease_pencil_fill_cancel;
1783
1784 ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1785
1786 prop = RNA_def_boolean(
1787 ot->srna, "invert", false, "Invert", "Find boundary of unfilled instead of filled regions");
1789
1790 prop = RNA_def_boolean(
1791 ot->srna, "precision", false, "Precision", "Use precision movement for extension lines");
1793}
1794
1796 const Scene &scene, GreasePencil &grease_pencil, const int layer_index)
1797{
1798 using namespace bke::greasepencil;
1799 const int current_frame = scene.r.cfra;
1800 Layer &layer = grease_pencil.layer(layer_index);
1801 if (!layer.has_drawing_at(current_frame) && !blender::animrig::is_autokey_on(&scene)) {
1802 return nullptr;
1803 }
1804
1805 const std::optional<int> previous_key_frame_start = layer.start_frame_at(current_frame);
1806 const bool has_previous_key = previous_key_frame_start.has_value();
1807 if (blender::animrig::is_autokey_on(&scene) && has_previous_key) {
1808 grease_pencil.insert_duplicate_frame(layer, *previous_key_frame_start, current_frame, false);
1809 }
1810 return grease_pencil.get_drawing_at(layer, current_frame);
1811}
1812
1814 const Scene &scene,
1815 GreasePencil &grease_pencil,
1817 const Span<IndexMask> points_to_remove_per_drawing)
1818{
1819 using namespace bke::greasepencil;
1820 using namespace ed::greasepencil;
1821 bool changed = false;
1822 for (const int drawing_i : drawings.index_range()) {
1823 const MutableDrawingInfo &info = drawings[drawing_i];
1824 const IndexMask points_to_remove = points_to_remove_per_drawing[drawing_i];
1825 if (points_to_remove.is_empty()) {
1826 continue;
1827 }
1828
1830 scene, grease_pencil, info.layer_index))
1831 {
1832 drawing->strokes_for_write() = geometry::remove_points_and_split(drawing->strokes(),
1833 points_to_remove);
1834 drawing->tag_topology_changed();
1835 changed = true;
1836 }
1837 }
1838
1839 return changed;
1840}
1841
1842static inline bool is_point_inside_bounds(const Bounds<int2> bounds, const int2 point)
1843{
1844 if (point.x < bounds.min.x) {
1845 return false;
1846 }
1847 if (point.x > bounds.max.x) {
1848 return false;
1849 }
1850 if (point.y < bounds.min.y) {
1851 return false;
1852 }
1853 if (point.y > bounds.max.y) {
1854 return false;
1855 }
1856 return true;
1857}
1858
1859static inline bool is_point_inside_lasso(const Array<int2> lasso, const int2 point)
1860{
1862 point, reinterpret_cast<const int(*)[2]>(lasso.data()), uint(lasso.size()));
1863}
1864
1866{
1867 using namespace bke::greasepencil;
1868 using namespace ed::greasepencil;
1869 const Scene *scene = CTX_data_scene(C);
1870 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1871 const ARegion *region = CTX_wm_region(C);
1872 Object *object = CTX_data_active_object(C);
1873 const Object *ob_eval = DEG_get_evaluated(depsgraph, object);
1874 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1875
1876 const Array<int2> lasso = WM_gesture_lasso_path_to_array(C, op);
1877 if (lasso.is_empty()) {
1878 return OPERATOR_FINISHED;
1879 }
1880
1881 const Bounds<int2> lasso_bounds_int = *bounds::min_max(lasso.as_span());
1882 const Bounds<float2> lasso_bounds(float2(lasso_bounds_int.min), float2(lasso_bounds_int.max));
1883
1885 *scene, grease_pencil);
1886 Array<IndexMaskMemory> memories(drawings.size());
1887 Array<IndexMask> points_to_remove_per_drawing(drawings.size());
1888 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange range) {
1889 for (const int drawing_i : range) {
1890 const MutableDrawingInfo &info = drawings[drawing_i];
1891 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
1892 const bke::crazyspace::GeometryDeformation deformation =
1893 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
1894 ob_eval, *object, info.drawing);
1895 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
1896
1897 const bke::CurvesGeometry &curves = info.drawing.strokes();
1898 Array<float2> screen_space_positions(curves.points_num());
1899 threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange points) {
1900 for (const int point : points) {
1901 const float3 pos = math::transform_point(layer_to_world, deformation.positions[point]);
1902 eV3DProjStatus result = ED_view3d_project_float_global(
1903 region, pos, screen_space_positions[point], V3D_PROJ_TEST_NOP);
1904 if (result != V3D_PROJ_RET_OK) {
1905 screen_space_positions[point] = float2(0);
1906 }
1907 }
1908 });
1909
1910 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1911 Array<Bounds<float2>> screen_space_curve_bounds(curves.curves_num());
1912 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
1913 for (const int curve : range) {
1914 screen_space_curve_bounds[curve] = *bounds::min_max(
1915 screen_space_positions.as_span().slice(points_by_curve[curve]));
1916 }
1917 });
1918
1919 IndexMaskMemory &memory = memories[drawing_i];
1920 const IndexMask curve_selection = IndexMask::from_predicate(
1921 curves.curves_range(), GrainSize(512), memory, [&](const int64_t index) {
1922 /* For a single point curve, its screen_space_curve_bounds Bounds will be empty (by
1923 * definition), so intersecting will fail. Check if the single point is in the bounds
1924 * instead. */
1925 const IndexRange points = points_by_curve[index];
1926 if (points.size() == 1) {
1927 return is_point_inside_bounds(lasso_bounds_int,
1928 int2(screen_space_positions[points.first()]));
1929 }
1930
1931 return bounds::intersect(lasso_bounds, screen_space_curve_bounds[index]).has_value();
1932 });
1933
1934 if (curve_selection.is_empty()) {
1935 return;
1936 }
1937
1938 Array<bool> points_to_remove(curves.points_num(), false);
1939 curve_selection.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
1940 for (const int point : points_by_curve[curve_i]) {
1941 points_to_remove[point] = is_point_inside_lasso(lasso,
1942 int2(screen_space_positions[point]));
1943 }
1944 });
1945 points_to_remove_per_drawing[drawing_i] = IndexMask::from_bools(points_to_remove, memory);
1946 }
1947 });
1948
1949 const bool changed = remove_points_and_split_from_drawings(
1950 *scene, grease_pencil, drawings.as_span(), points_to_remove_per_drawing);
1951 if (changed) {
1952 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1954 }
1955
1956 return OPERATOR_FINISHED;
1957}
1958
1960{
1961 ot->name = "Grease Pencil Erase Lasso";
1962 ot->idname = "GREASE_PENCIL_OT_erase_lasso";
1963 ot->description = "Erase points in the lasso region";
1964
1966 ot->invoke = WM_gesture_lasso_invoke;
1968 ot->modal = WM_gesture_lasso_modal;
1969 ot->cancel = WM_gesture_lasso_cancel;
1970
1971 ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1972
1974}
1975
1977{
1978 using namespace bke::greasepencil;
1979 using namespace ed::greasepencil;
1980 const Scene *scene = CTX_data_scene(C);
1981 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1982 const ARegion *region = CTX_wm_region(C);
1983 Object *object = CTX_data_active_object(C);
1984 const Object *ob_eval = DEG_get_evaluated(depsgraph, object);
1985 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1986
1988 if (box_bounds.is_empty()) {
1989 return OPERATOR_FINISHED;
1990 }
1991
1993 *scene, grease_pencil);
1994 Array<IndexMaskMemory> memories(drawings.size());
1995 Array<IndexMask> points_to_remove_per_drawing(drawings.size());
1996 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange range) {
1997 for (const int drawing_i : range) {
1998 const MutableDrawingInfo &info = drawings[drawing_i];
1999 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
2000 const bke::crazyspace::GeometryDeformation deformation =
2001 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
2002 ob_eval, *object, info.drawing);
2003 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
2004
2005 const bke::CurvesGeometry &curves = info.drawing.strokes();
2006 Array<float2> screen_space_positions(curves.points_num());
2007 threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange points) {
2008 for (const int point : points) {
2009 const float3 pos = math::transform_point(layer_to_world, deformation.positions[point]);
2010 eV3DProjStatus result = ED_view3d_project_float_global(
2011 region, pos, screen_space_positions[point], V3D_PROJ_TEST_NOP);
2012 if (result != V3D_PROJ_RET_OK) {
2013 screen_space_positions[point] = float2(0);
2014 }
2015 }
2016 });
2017
2018 IndexMaskMemory &memory = memories[drawing_i];
2019 points_to_remove_per_drawing[drawing_i] = IndexMask::from_predicate(
2020 curves.points_range(), GrainSize(4096), memory, [&](const int64_t index) {
2021 return is_point_inside_bounds(box_bounds, int2(screen_space_positions[index]));
2022 });
2023 }
2024 });
2025
2026 const bool changed = remove_points_and_split_from_drawings(
2027 *scene, grease_pencil, drawings.as_span(), points_to_remove_per_drawing);
2028 if (changed) {
2029 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2031 }
2032
2033 return OPERATOR_FINISHED;
2034}
2035
2037{
2038 ot->name = "Grease Pencil Box Erase";
2039 ot->idname = "GREASE_PENCIL_OT_erase_box";
2040 ot->description = "Erase points in the box region";
2041
2043 ot->invoke = WM_gesture_box_invoke;
2045 ot->modal = WM_gesture_box_modal;
2046 ot->cancel = WM_gesture_box_cancel;
2047
2048 ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
2049
2051}
2052
2054
2055} // namespace blender::ed::sculpt_paint
2056
2057/* -------------------------------------------------------------------- */
2060
2072
2074{
2075 using namespace blender::ed::greasepencil;
2077
2078 static const EnumPropertyItem modal_items[] = {
2079 {int(FillToolModalKey::Cancel), "CANCEL", 0, "Cancel", ""},
2080 {int(FillToolModalKey::Confirm), "CONFIRM", 0, "Confirm", ""},
2082 "EXTENSION_MODE_TOGGLE",
2083 0,
2084 "Toggle Extension Mode",
2085 ""},
2087 "EXTENSION_LENGTHEN",
2088 0,
2089 "Lengthen Extensions",
2090 ""},
2091 {int(FillToolModalKey::ExtensionShorten), "EXTENSION_SHORTEN", 0, "Shorten Extensions", ""},
2092 {int(FillToolModalKey::ExtensionDrag), "EXTENSION_DRAG", 0, "Drag Extensions", ""},
2093 {int(FillToolModalKey::ExtensionCollide), "EXTENSION_COLLIDE", 0, "Collide Extensions", ""},
2094 {int(FillToolModalKey::Invert), "INVERT", 0, "Invert", ""},
2095 {int(FillToolModalKey::Precision), "PRECISION", 0, "Precision", ""},
2096 {0, nullptr, 0, nullptr, nullptr},
2097 };
2098
2099 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Fill Tool Modal Map");
2100
2101 /* This function is called for each space-type, only needs to add map once. */
2102 if (keymap && keymap->modal_items) {
2103 return;
2104 }
2105
2106 keymap = WM_modalkeymap_ensure(keyconf, "Fill Tool Modal Map", modal_items);
2107
2108 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_fill");
2109}
2110
Functions to insert, delete or modify keyframes.
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:604
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:720
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:596
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)
PaintMode
Definition BKE_paint.hh:93
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:641
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:496
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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:962
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
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:1040
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
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:330
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define NA_EDITED
Definition WM_types.hh:581
#define NC_GPENCIL
Definition WM_types.hh:396
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
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()
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:995
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
int64_t size() const
Definition BLI_array.hh:245
Span< T > as_span() const
Definition BLI_array.hh:232
const T * data() const
Definition BLI_array.hh:301
bool is_empty() const
Definition BLI_array.hh:253
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 ForSingle(T value, const int64_t size)
static VArray ForSpan(Span< T > values)
static VArray ForFunc(const int64_t size, GetFunc get_func)
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, eCustomDataType 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::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)
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)
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
Definition BLI_color.hh:342
VecBase< float, 3 > float3
BrushStrokeMode
@ BRUSH_STROKE_SMOOTH
@ BRUSH_STROKE_ERASE
void paint_stroke_operator_properties(wmOperatorType *ot)
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::@333024162336310320257044262376131053107053120315 lines
struct blender::ed::greasepencil::ExtensionData::@320262211341312142156102310113274113077346355201 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:754
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
int mval[2]
Definition WM_types.hh:760
wmTabletData tablet
Definition WM_types.hh:783
const void * modal_items
wmOperatorStatus(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1078
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:4226
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:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
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)