Blender V4.3
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_curves.hh"
11#include "BKE_deform.hh"
12#include "BKE_geometry_set.hh"
13#include "BKE_grease_pencil.hh"
14#include "BKE_material.h"
15#include "BKE_object_deform.h"
16#include "BKE_paint.hh"
17#include "BKE_report.hh"
18#include "BKE_screen.hh"
19
20#include "BLI_array_utils.hh"
21#include "BLI_assert.h"
22#include "BLI_color.hh"
23#include "BLI_index_mask.hh"
24#include "BLI_kdopbvh.h"
25#include "BLI_kdtree.h"
26#include "BLI_math_matrix.hh"
27#include "BLI_math_vector.hh"
28#include "BLI_offset_indices.hh"
29#include "BLI_rect.h"
30
31#include "DNA_brush_enums.h"
32#include "DNA_brush_types.h"
33#include "DNA_scene_types.h"
34#include "DNA_view3d_types.h"
36
38
40#include "GEO_smooth_curves.hh"
41
42#include "ED_grease_pencil.hh"
43#include "ED_image.hh"
44#include "ED_object.hh"
45#include "ED_screen.hh"
46#include "ED_space_api.hh"
47#include "ED_view3d.hh"
48
49#include "MEM_guardedalloc.h"
50
51#include "RNA_access.hh"
52#include "RNA_define.hh"
53
54#include "UI_interface.hh"
55
56#include "BLT_translation.hh"
57
58#include "WM_api.hh"
59#include "WM_toolsystem.hh"
60#include "WM_types.hh"
61
63#include "paint_intern.hh"
64#include "wm_event_types.hh"
65
66#include <algorithm>
67#include <fmt/format.h>
68#include <optional>
69
71
72/* -------------------------------------------------------------------- */
76static bool stroke_get_location(bContext * /*C*/,
77 float out[3],
78 const float mouse[2],
79 bool /*force_original*/)
80{
81 out[0] = mouse[0];
82 out[1] = mouse[1];
83 out[2] = 0;
84 return true;
85}
86
87static std::unique_ptr<GreasePencilStrokeOperation> get_stroke_operation(bContext &C,
88 wmOperator *op)
89{
91 const Brush &brush = *BKE_paint_brush_for_read(paint);
93 const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
94
95 if (mode == PaintMode::GPencil) {
97 stroke_mode == BRUSH_STROKE_ERASE)
98 {
99 /* Special case: We're using the draw tool but with the eraser mode, so create an erase
100 * operation. */
102 }
103 /* FIXME: Somehow store the unique_ptr in the PaintStroke. */
104 switch (eBrushGPaintType(brush.gpencil_brush_type)) {
110 /* Fill tool keymap uses the paint operator as alternative mode. */
114 }
115 }
116 else if (mode == PaintMode::SculptGreasePencil) {
117 if (stroke_mode == BRUSH_STROKE_SMOOTH) {
118 return greasepencil::new_smooth_operation(stroke_mode);
119 }
122 return greasepencil::new_smooth_operation(stroke_mode);
124 return greasepencil::new_thickness_operation(stroke_mode);
126 return greasepencil::new_strength_operation(stroke_mode);
128 return greasepencil::new_grab_operation(stroke_mode);
130 return greasepencil::new_push_operation(stroke_mode);
132 return greasepencil::new_twist_operation(stroke_mode);
134 return greasepencil::new_pinch_operation(stroke_mode);
136 return greasepencil::new_randomize_operation(stroke_mode);
138 return greasepencil::new_clone_operation(stroke_mode);
139 }
140 }
141 else if (mode == PaintMode::WeightGPencil) {
151 }
152 }
153 else if (mode == PaintMode::VertexGPencil) {
166 /* Unused. */
168 return nullptr;
169 }
170 }
171 return nullptr;
172}
173
174static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
175{
176 UNUSED_VARS(C, op, mouse);
177 return true;
178}
179
181 wmOperator *op,
182 PaintStroke *stroke,
183 PointerRNA *stroke_element)
184{
186 paint_stroke_mode_data(stroke));
187
189 RNA_float_get_array(stroke_element, "mouse", sample.mouse_position);
190 sample.pressure = RNA_float_get(stroke_element, "pressure");
191
192 if (!operation) {
193 std::unique_ptr<GreasePencilStrokeOperation> new_operation = get_stroke_operation(*C, op);
194 BLI_assert(new_operation != nullptr);
195 new_operation->on_stroke_begin(*C, sample);
196 paint_stroke_set_mode_data(stroke, std::move(new_operation));
197 }
198 else {
199 operation->on_stroke_extended(*C, sample);
200 }
201}
202
203static void stroke_redraw(const bContext *C, PaintStroke * /*stroke*/, bool /*final*/)
204{
206}
207
208static void stroke_done(const bContext *C, PaintStroke *stroke)
209{
211 paint_stroke_mode_data(stroke));
212 if (operation != nullptr) {
213 operation->on_stroke_done(*C);
214 }
215}
216
219/* -------------------------------------------------------------------- */
224{
226 return false;
227 }
229 return false;
230 }
231 return true;
232}
233
235{
236 if (event->tablet.active == EVT_TABLET_ERASER) {
237 RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_ERASE);
238 }
239
240 const bool use_duplicate_previous_key = [&]() -> bool {
242 const Brush &brush = *BKE_paint_brush_for_read(paint);
244 const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
245
246 if (mode == PaintMode::GPencil) {
247 /* For the eraser and tint tool, we don't want auto-key to create an empty keyframe, so we
248 * duplicate the previous frame. */
252 {
253 return true;
254 }
255 /* Same for the temporary eraser when using the draw tool. */
257 stroke_mode == BRUSH_STROKE_ERASE)
258 {
259 return true;
260 }
261 }
262 return false;
263 }();
265 C, op, use_duplicate_previous_key);
266 if (return_value != OPERATOR_RUNNING_MODAL) {
267 return return_value;
268 }
269
271 op,
277 event->type);
278
279 return_value = op->type->modal(C, op, event);
280 if (return_value == OPERATOR_FINISHED) {
281 return OPERATOR_FINISHED;
282 }
283
286}
287
289{
290 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
291}
292
294{
295 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
296}
297
299{
300 ot->name = "Grease Pencil Draw";
301 ot->idname = "GREASE_PENCIL_OT_brush_stroke";
302 ot->description = "Draw a new stroke in the active Grease Pencil object";
303
308
310
312}
313
316/* -------------------------------------------------------------------- */
321{
323 return false;
324 }
326 return false;
327 }
328 return true;
329}
330
332{
333 const Scene *scene = CTX_data_scene(C);
334 const Object *object = CTX_data_active_object(C);
335 if (!object || object->type != OB_GREASE_PENCIL) {
336 return OPERATOR_CANCELLED;
337 }
338
339 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
340 if (!grease_pencil.has_active_layer()) {
341 BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
342 return OPERATOR_CANCELLED;
343 }
344
346 const Brush *brush = BKE_paint_brush_for_read(paint);
347 if (brush == nullptr) {
348 return OPERATOR_CANCELLED;
349 }
350
351 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
352 if (!active_layer.is_editable()) {
353 BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
354 return OPERATOR_CANCELLED;
355 }
356
357 /* Ensure a drawing at the current keyframe. */
358 bool inserted_keyframe = false;
359 /* For the sculpt tools, we don't want the auto-key to create an empty keyframe, so we duplicate
360 * the previous key. */
361 const bool use_duplicate_previous_key = true;
362 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
364 *scene, grease_pencil, *layer, use_duplicate_previous_key, inserted_keyframe))
365 {
366 inserted_keyframe = true;
367 }
368 }
369 if (!inserted_keyframe) {
370 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
371 return OPERATOR_CANCELLED;
372 }
374
376 op,
382 event->type);
383
384 const int return_value = op->type->modal(C, op, event);
385 if (return_value == OPERATOR_FINISHED) {
386 return OPERATOR_FINISHED;
387 }
388
391}
392
394{
395 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
396}
397
399{
400 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
401}
402
404{
405 ot->name = "Grease Pencil Sculpt";
406 ot->idname = "GREASE_PENCIL_OT_sculpt_paint";
407 ot->description = "Sculpt strokes in the active Grease Pencil object";
408
413
415
417}
418
421/* -------------------------------------------------------------------- */
426{
428 return false;
429 }
431 return false;
432 }
433 return true;
434}
435
437 wmOperator *op,
438 const wmEvent *event)
439{
440 const Scene *scene = CTX_data_scene(C);
441 const Object *object = CTX_data_active_object(C);
442 if (!object || object->type != OB_GREASE_PENCIL) {
443 return OPERATOR_CANCELLED;
444 }
445
446 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
448 const Brush *brush = BKE_paint_brush_for_read(paint);
449 if (brush == nullptr) {
450 return OPERATOR_CANCELLED;
451 }
452
455 if (drawings.is_empty()) {
456 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw weight on");
457 return OPERATOR_CANCELLED;
458 }
459
460 const int active_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
461 if (active_defgroup_nr >= 0 && BKE_object_defgroup_active_is_locked(object)) {
462 BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
463 return OPERATOR_CANCELLED;
464 }
465
467 op,
473 event->type);
474
475 const int return_value = op->type->modal(C, op, event);
476 if (return_value == OPERATOR_FINISHED) {
477 return OPERATOR_FINISHED;
478 }
479
482}
483
485 wmOperator *op,
486 const wmEvent *event)
487{
488 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
489}
490
492{
493 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
494}
495
497{
498 ot->name = "Grease Pencil Paint Weight";
499 ot->idname = "GREASE_PENCIL_OT_weight_brush_stroke";
500 ot->description = "Draw weight on stroke points in the active Grease Pencil object";
501
506
508
510}
511
514/* -------------------------------------------------------------------- */
519{
521 return false;
522 }
524 return false;
525 }
526 return true;
527}
528
530 wmOperator *op,
531 const wmEvent *event)
532{
533 const Scene *scene = CTX_data_scene(C);
534 const Object *object = CTX_data_active_object(C);
535 if (!object || object->type != OB_GREASE_PENCIL) {
536 return OPERATOR_CANCELLED;
537 }
538
539 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
540 if (!grease_pencil.has_active_layer()) {
541 BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
542 return OPERATOR_CANCELLED;
543 }
544
545 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
546 if (!active_layer.is_editable()) {
547 BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
548 return OPERATOR_CANCELLED;
549 }
550
552 const Brush *brush = BKE_paint_brush_for_read(paint);
553 if (brush == nullptr) {
554 return OPERATOR_CANCELLED;
555 }
556
557 /* Ensure a drawing at the current keyframe. */
558 bool inserted_keyframe = false;
559 /* For the vertex paint tools, we don't want the auto-key to create an empty keyframe, so we
560 * duplicate the previous key. */
561 const bool use_duplicate_previous_key = true;
563 *scene, grease_pencil, active_layer, use_duplicate_previous_key, inserted_keyframe))
564 {
565 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
566 return OPERATOR_CANCELLED;
567 }
568 if (inserted_keyframe) {
570 }
571
573 op,
579 event->type);
580
581 const int return_value = op->type->modal(C, op, event);
582 if (return_value == OPERATOR_FINISHED) {
583 return OPERATOR_FINISHED;
584 }
585
588}
589
591 wmOperator *op,
592 const wmEvent *event)
593{
594 return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
595}
596
598{
599 paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
600}
601
603{
604 ot->name = "Grease Pencil Paint Vertex";
605 ot->idname = "GREASE_PENCIL_OT_vertex_brush_stroke";
606 ot->description = "Draw on vertex colors in the active Grease Pencil object";
607
612
614
616}
617
620/* -------------------------------------------------------------------- */
626
627 /* Material of the generated stroke. */
629 /* Toggle inverse filling. */
630 bool invert = false;
631 /* Toggle precision mode. */
632 bool precision = false;
633
634 /* Methods for gap filling. */
636 /* Length of extension lines. */
638 /* Cut off extension lines at first intersection. */
640
641 /* Draw boundaries stroke overlay. */
643 /* Draw extension lines overlay. */
645
646 /* Mouse position where fill was initialized */
648 /* Extension lines drag mode is enabled (middle mouse button). */
650 /* Mouse position where the extension mode was enabled. */
652
653 /* Overlay draw callback for helper lines, etc. */
655
658 const int material_index,
659 const bool invert,
660 const bool precision)
661 {
663
664 const ToolSettings &ts = *CTX_data_tool_settings(&C);
665 const Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
673 const bool brush_invert = brush.gpencil_settings->fill_direction == BRUSH_DIR_IN;
674 /* Both operator properties and brush properties can invert. Actual invert is XOR of both. */
675 const bool combined_invert = (invert != brush_invert);
676
677 return {layer,
679 combined_invert,
680 precision,
686 }
687};
688
689/* Find and cut extension lines at intersections with other lines and strokes. */
691 ed::greasepencil::ExtensionData &extension_data,
692 Span<int> origin_drawings,
693 Span<int> origin_points)
694{
695 const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
696 const Scene &scene = *CTX_data_scene(&C);
697 const Object &object = *CTX_data_active_object(&C);
698 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
699
700 const float4x4 view_matrix = float4x4(rv3d.viewmat);
701
703 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
704
705 const IndexRange bvh_extension_range = extension_data.lines.starts.index_range();
706 Array<int> bvh_curve_offsets_data(drawings.size() + 1);
707 for (const int i : drawings.index_range()) {
708 bvh_curve_offsets_data[i] = drawings[i].drawing.strokes().points_num();
709 }
711 bvh_curve_offsets_data, bvh_extension_range.size());
712
713 /* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
714 * necessary. Not all entries are added to the BVH tree. */
715 const int max_bvh_lines = bvh_curve_offsets.data().last();
716 /* Cached view positions for lines. */
717 Array<float2> view_starts(max_bvh_lines);
718 Array<float2> view_ends(max_bvh_lines);
719
720 BVHTree *tree = BLI_bvhtree_new(max_bvh_lines, 0.0f, 4, 6);
722
723 /* Insert extension lines for intersection.
724 * Note: These are added first, so that the extension index matches its index in BVH data. */
725 for (const int i_line : bvh_extension_range.index_range()) {
726 const float2 start =
727 math::transform_point(view_matrix, extension_data.lines.starts[i_line]).xy();
728 const float2 end = math::transform_point(view_matrix, extension_data.lines.ends[i_line]).xy();
729
730 const int bvh_index = bvh_extension_range[i_line];
731 view_starts[bvh_index] = start;
732 view_ends[bvh_index] = end;
733
734 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
735 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
736 }
737
738 /* Insert segments for cutting extensions on stroke intersection. */
739 for (const int i_drawing : drawings.index_range()) {
740 const ed::greasepencil::DrawingInfo &info = drawings[i_drawing];
741 const bke::CurvesGeometry &curves = info.drawing.strokes();
742 const OffsetIndices points_by_curve = curves.points_by_curve();
743 const Span<float3> positions = curves.positions();
744 const VArray<bool> cyclic = curves.cyclic();
745 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
746 const float4x4 layer_to_view = view_matrix * layer.to_world_space(object);
747
748 for (const int i_curve : curves.curves_range()) {
749 const bool is_cyclic = cyclic[i_curve];
750 const IndexRange points = points_by_curve[i_curve];
751
752 for (const int i_point : points.drop_back(1)) {
753 const float2 start = math::transform_point(layer_to_view, positions[i_point]).xy();
754 const float2 end = math::transform_point(layer_to_view, positions[i_point + 1]).xy();
755
756 const int bvh_index = bvh_curve_offsets[i_drawing][i_point];
757 view_starts[bvh_index] = start;
758 view_ends[bvh_index] = end;
759
760 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
761 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
762 }
763 /* Last->first point segment only used for cyclic curves. */
764 if (is_cyclic) {
765 const float2 start = math::transform_point(layer_to_view, positions[points.last()]).xy();
766 const float2 end = math::transform_point(layer_to_view, positions[points.first()]).xy();
767
768 const int bvh_index = bvh_curve_offsets[i_drawing][points.last()];
769 view_starts[bvh_index] = start;
770 view_ends[bvh_index] = end;
771
772 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
773 BLI_bvhtree_insert(tree, bvh_index, bb, 2);
774 }
775 }
776 }
777
779
780 struct RaycastArgs {
781 Span<float2> starts;
782 Span<float2> ends;
783 /* Indices that may need to be ignored to avoid self-intersection. */
784 int ignore_index1;
785 int ignore_index2;
786 };
788 [](void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) {
789 using Result = math::isect_result<float2>;
790
791 const RaycastArgs &args = *static_cast<const RaycastArgs *>(userdata);
792 if (ELEM(index, args.ignore_index1, args.ignore_index2)) {
793 return;
794 }
795
796 const float2 ray_start = float2(ray->origin);
797 const float2 ray_end = ray_start + float2(ray->direction) * ray->radius;
798 const float2 &line_start = args.starts[index];
799 const float2 &line_end = args.ends[index];
800 Result result = math::isect_seg_seg(ray_start, ray_end, line_start, line_end);
801 if (result.kind <= 0) {
802 return;
803 }
804 const float dist = result.lambda * math::distance(ray_start, ray_end);
805 if (dist >= hit->dist) {
806 return;
807 }
808 /* These always need to be calculated for the BVH traversal function. */
809 hit->index = index;
810 hit->dist = result.lambda * math::distance(ray_start, ray_end);
811 /* Don't need the hit point, only the lambda. */
812 hit->no[0] = result.lambda;
813 };
814
815 /* Store intersections first before applying to the data, so that subsequent ray-casts use
816 * original end points until all intersections are found. */
817 Vector<float3> new_extension_ends(extension_data.lines.ends.size());
818 for (const int i_line : extension_data.lines.starts.index_range()) {
819 const float2 start =
820 math::transform_point(view_matrix, extension_data.lines.starts[i_line]).xy();
821 const float2 end = math::transform_point(view_matrix, extension_data.lines.ends[i_line]).xy();
822 float length;
823 const float2 dir = math::normalize_and_get_length(end - start, length);
824
825 const int bvh_index = i_line;
826 const int origin_drawing = origin_drawings[i_line];
827 const int origin_point = origin_points[i_line];
828 const int bvh_origin_index = bvh_curve_offsets[origin_drawing][origin_point];
829
830 RaycastArgs args = {view_starts, view_ends, bvh_index, bvh_origin_index};
831 BVHTreeRayHit hit;
832 hit.index = -1;
833 hit.dist = FLT_MAX;
835 tree, float3(start, 0.0f), float3(dir, 0.0f), length, &hit, callback, &args);
836
837 if (hit.index >= 0) {
838 const float lambda = hit.no[0];
839 new_extension_ends[i_line] = math::interpolate(
840 extension_data.lines.starts[i_line], extension_data.lines.ends[i_line], lambda);
841 }
842 else {
843 new_extension_ends[i_line] = extension_data.lines.ends[i_line];
844 }
845 }
846
847 extension_data.lines.ends = std::move(new_extension_ends);
848}
849
850/* Find closest point in each circle and generate extension lines between such pairs. */
852 const bContext &C,
853 ed::greasepencil::ExtensionData &extension_data,
854 Span<int> /*origin_drawings*/,
855 Span<int> /*origin_points*/)
856{
857 const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
858 const Scene &scene = *CTX_data_scene(&C);
859 const Object &object = *CTX_data_active_object(&C);
860 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
861
862 const float4x4 view_matrix = float4x4(rv3d.viewmat);
863
865 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
866
867 const IndexRange circles_range = extension_data.circles.centers.index_range();
868 /* TODO Include high-curvature feature points. */
869 const IndexRange feature_points_range = circles_range.after(0);
870 const IndexRange kd_points_range = IndexRange(circles_range.size() +
871 feature_points_range.size());
872
873 /* Upper bound for segment count. Arrays are sized for easy index mapping, exact count isn't
874 * necessary. Not all entries are added to the BVH tree. */
875 const int max_kd_entries = kd_points_range.size();
876 /* Cached view positions for lines. */
877 Array<float2> view_centers(max_kd_entries);
878 Array<float> view_radii(max_kd_entries);
879
880 KDTree_2d *kdtree = BLI_kdtree_2d_new(max_kd_entries);
881
882 /* Insert points for overlap tests. */
883 for (const int point_i : circles_range.index_range()) {
884 const float2 center =
885 math::transform_point(view_matrix, extension_data.circles.centers[point_i]).xy();
886 const float radius = math::average(math::to_scale(view_matrix)) *
887 extension_data.circles.radii[point_i];
888
889 const int kd_index = circles_range[point_i];
890 view_centers[kd_index] = center;
891 view_radii[kd_index] = radius;
892
893 BLI_kdtree_2d_insert(kdtree, kd_index, center);
894 }
895 for (const int i_point : feature_points_range.index_range()) {
896 /* TODO Insert feature points into the KDTree. */
897 UNUSED_VARS(i_point);
898 }
899 BLI_kdtree_2d_balance(kdtree);
900
901 struct {
902 Vector<float3> starts;
903 Vector<float3> ends;
904 } connection_lines;
905 /* Circles which can be kept because they generate no extension lines. */
906 Vector<int> keep_circle_indices;
907 keep_circle_indices.reserve(circles_range.size());
908
909 for (const int point_i : circles_range.index_range()) {
910 const int kd_index = circles_range[point_i];
911 const float2 center = view_centers[kd_index];
912 const float radius = view_radii[kd_index];
913
914 bool found = false;
915 BLI_kdtree_2d_range_search_cb_cpp(
916 kdtree,
917 center,
918 radius,
919 [&](const int other_point_i, const float * /*co*/, float /*dist_sq*/) {
920 if (other_point_i == kd_index) {
921 return true;
922 }
923
924 found = true;
925 connection_lines.starts.append(extension_data.circles.centers[point_i]);
926 if (circles_range.contains(other_point_i)) {
927 connection_lines.ends.append(extension_data.circles.centers[other_point_i]);
928 }
929 else if (feature_points_range.contains(other_point_i)) {
930 /* TODO copy feature point to connection_lines (beware of start index!). */
931 connection_lines.ends.append(float3(0));
932 }
933 else {
935 }
936 return true;
937 });
938 /* Keep the circle if no extension line was found. */
939 if (!found) {
940 keep_circle_indices.append(point_i);
941 }
942 }
943
944 BLI_kdtree_2d_free(kdtree);
945
946 /* Add new extension lines. */
947 extension_data.lines.starts.extend(connection_lines.starts);
948 extension_data.lines.ends.extend(connection_lines.ends);
949 /* Remove circles that formed extension lines. */
950 Vector<float3> old_centers = std::move(extension_data.circles.centers);
951 Vector<float> old_radii = std::move(extension_data.circles.radii);
952 extension_data.circles.centers.resize(keep_circle_indices.size());
953 extension_data.circles.radii.resize(keep_circle_indices.size());
954 array_utils::gather(old_centers.as_span(),
955 keep_circle_indices.as_span(),
956 extension_data.circles.centers.as_mutable_span());
957 array_utils::gather(old_radii.as_span(),
958 keep_circle_indices.as_span(),
959 extension_data.circles.radii.as_mutable_span());
960}
961
963 const bContext &C, const GreasePencilFillOpData &op_data)
964{
965 const Scene &scene = *CTX_data_scene(&C);
966 const Object &object = *CTX_data_active_object(&C);
967 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
968
970 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
971
972 ed::greasepencil::ExtensionData extension_data;
973 Vector<int> origin_points;
974 Vector<int> origin_drawings;
975 for (const int i_drawing : drawings.index_range()) {
976 const ed::greasepencil::DrawingInfo &info = drawings[i_drawing];
977 const bke::CurvesGeometry &curves = info.drawing.strokes();
978 const OffsetIndices points_by_curve = curves.points_by_curve();
979 const Span<float3> positions = curves.positions();
980 const VArray<bool> cyclic = curves.cyclic();
981 const float4x4 layer_to_world = grease_pencil.layer(info.layer_index).to_world_space(object);
982
983 for (const int i_curve : curves.curves_range()) {
984 const IndexRange points = points_by_curve[i_curve];
985 /* No extension lines on cyclic curves. */
986 if (cyclic[i_curve]) {
987 continue;
988 }
989 /* Can't compute directions for single-point curves. */
990 if (points.size() < 2) {
991 continue;
992 }
993
994 const float3 pos_head = math::transform_point(layer_to_world, positions[points[0]]);
995 const float3 pos_tail = math::transform_point(layer_to_world, positions[points.last()]);
996 const float3 pos_head_next = math::transform_point(layer_to_world, positions[points[1]]);
997 const float3 pos_tail_prev = math::transform_point(layer_to_world,
998 positions[points.last(1)]);
999 const float3 dir_head = math::normalize(pos_head - pos_head_next);
1000 const float3 dir_tail = math::normalize(pos_tail - pos_tail_prev);
1001 /* Initial length before intersection tests. */
1002 const float length = op_data.extension_length;
1003
1004 switch (op_data.extension_mode) {
1006 extension_data.lines.starts.append(pos_head);
1007 extension_data.lines.ends.append(pos_head + dir_head * length);
1008 origin_drawings.append(i_drawing);
1009 origin_points.append(points.first());
1010
1011 extension_data.lines.starts.append(pos_tail);
1012 extension_data.lines.ends.append(pos_tail + dir_tail * length);
1013 origin_drawings.append(i_drawing);
1014 /* Segment index is the start point. */
1015 origin_points.append(points.last() - 1);
1016 break;
1018 extension_data.circles.centers.append(pos_head);
1019 extension_data.circles.radii.append(length);
1020 origin_drawings.append(i_drawing);
1021 origin_points.append(points.first());
1022
1023 extension_data.circles.centers.append(pos_tail);
1024 extension_data.circles.radii.append(length);
1025 origin_drawings.append(i_drawing);
1026 /* Segment index is the start point. */
1027 origin_points.append(points.last() - 1);
1028 break;
1029 }
1030 }
1031 }
1032
1033 switch (op_data.extension_mode) {
1035 /* Intersection test against strokes and other extension lines. */
1036 if (op_data.extension_cut) {
1037 grease_pencil_fill_extension_cut(C, extension_data, origin_drawings, origin_points);
1038 }
1039 break;
1042 C, extension_data, origin_drawings, origin_points);
1043 break;
1044 }
1045
1046 return extension_data;
1047}
1048
1050 const GreasePencilFillOpData &op_data)
1051{
1052 const bool is_extend = (op_data.extension_mode == GP_FILL_EMODE_EXTEND);
1053
1054 const std::string status_str = fmt::format(
1055 IFACE_("Fill: ESC/RMB cancel, LMB Fill, MMB Adjust Extension, S: "
1056 "Switch Mode, D: Stroke Collision | Mode: {}, Collision {}, Length: {:.3f}"),
1057 (is_extend) ? IFACE_("Extend") : IFACE_("Radius"),
1058 (is_extend && op_data.extension_cut) ? IFACE_("ON") : IFACE_("OFF"),
1059 op_data.extension_length);
1060
1061 ED_workspace_status_text(&C, status_str.c_str());
1062}
1063
1064/* Draw callback for fill tool overlay. */
1065static void grease_pencil_fill_overlay_cb(const bContext *C, ARegion * /*region*/, void *arg)
1066{
1067 const ARegion &region = *CTX_wm_region(C);
1068 const RegionView3D &rv3d = *CTX_wm_region_view3d(C);
1069 const Scene &scene = *CTX_data_scene(C);
1070 const Object &object = *CTX_data_active_object(C);
1071 const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
1072 auto &op_data = *static_cast<GreasePencilFillOpData *>(arg);
1073
1074 const float4x4 world_to_view = float4x4(rv3d.viewmat);
1075 /* Note; the initial view matrix is already set, clear to draw in view space. */
1077
1078 const ColorGeometry4f stroke_curves_color = ColorGeometry4f(1, 0, 0, 1);
1079 const ColorGeometry4f extension_lines_color = ColorGeometry4f(0, 1, 1, 1);
1080 const ColorGeometry4f extension_circles_color = ColorGeometry4f(1, 0.5, 0, 1);
1081
1083 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
1084
1085 if (op_data.show_boundaries) {
1087 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
1088
1089 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1090 const IndexMask curve_mask = info.drawing.strokes().curves_range();
1092 stroke_curves_color, info.drawing.strokes().points_num());
1093 const float4x4 layer_to_world = grease_pencil.layer(info.layer_index).to_world_space(object);
1094 const bool use_xray = false;
1095 const float radius_scale = 1.0f;
1096
1098 int2(region.winx, region.winy),
1099 object,
1100 info.drawing,
1101 layer_to_world,
1102 curve_mask,
1103 colors,
1104 use_xray,
1105 radius_scale);
1106 }
1107 }
1108
1109 if (op_data.show_extension) {
1111 *C, op_data);
1112
1113 const float line_width = 2.0f;
1114
1115 const IndexRange lines_range = extensions.lines.starts.index_range();
1116 if (!lines_range.is_empty()) {
1118 extension_lines_color, lines_range.size());
1119
1121 lines_range,
1122 extensions.lines.starts,
1123 extensions.lines.ends,
1124 line_colors,
1125 line_width);
1126 }
1127 const IndexRange circles_range = extensions.circles.centers.index_range();
1128 if (!circles_range.is_empty()) {
1130 extension_circles_color, circles_range.size());
1131
1133 world_to_view,
1134 circles_range,
1135 extensions.circles.centers,
1137 circle_colors,
1138 float2(region.winx, region.winy),
1139 line_width,
1140 false);
1141 }
1142 }
1143}
1144
1146 GreasePencilFillOpData &op_data)
1147{
1148 const bool needs_overlay = op_data.show_boundaries || op_data.show_extension;
1149
1150 if (needs_overlay) {
1151 if (op_data.overlay_cb_handle == nullptr) {
1154 }
1155 }
1156 else {
1157 if (op_data.overlay_cb_handle) {
1158 ED_region_draw_cb_exit(region.type, op_data.overlay_cb_handle);
1159 op_data.overlay_cb_handle = nullptr;
1160 }
1161 }
1162}
1163
1170
1171/* Layer mode defines layers where only marked boundary strokes are used. */
1173 eGP_FillLayerModes fill_layer_mode)
1174{
1175 BLI_assert(grease_pencil.has_active_layer());
1176 const IndexRange all_layers = grease_pencil.layers().index_range();
1177 const int active_layer_index = *grease_pencil.get_layer_index(*grease_pencil.get_active_layer());
1178
1179 switch (fill_layer_mode) {
1181 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1182 return index != active_layer_index;
1183 });
1185 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1186 return index != active_layer_index + 1;
1187 });
1189 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1190 return index != active_layer_index - 1;
1191 });
1193 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1194 return index <= active_layer_index;
1195 });
1197 return VArray<bool>::ForFunc(all_layers.size(), [active_layer_index](const int index) {
1198 return index >= active_layer_index;
1199 });
1201 return VArray<bool>::ForFunc(all_layers.size(), [grease_pencil](const int index) {
1202 return !grease_pencil.layers()[index]->is_visible();
1203 });
1204 }
1205 return {};
1206}
1207
1208/* Array of visible drawings to use as borders for generating a stroke in the editable drawing on
1209 * the active layer. This is provided for every frame in the multi-frame edit range. */
1214
1216 GreasePencil &grease_pencil,
1217 bke::greasepencil::Layer &target_layer)
1218{
1219 using namespace bke::greasepencil;
1222
1223 const ToolSettings *toolsettings = scene.toolsettings;
1224 const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
1226 const bool use_autokey = blender::animrig::is_autokey_on(&scene);
1227 const bool use_duplicate_frame = (scene.toolsettings->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST);
1228 const int target_layer_index = *grease_pencil.get_layer_index(target_layer);
1229
1230 VectorSet<int> target_frames;
1231 /* Add drawing on the current frame. */
1232 target_frames.add(scene.r.cfra);
1233 /* Multi-frame edit: Add drawing on frames that are selected in any layer. */
1234 if (use_multi_frame_editing) {
1235 for (const Layer *layer : grease_pencil.layers()) {
1236 for (const auto [frame_number, frame] : layer->frames().items()) {
1237 if (frame.is_selected()) {
1238 target_frames.add(frame_number);
1239 }
1240 }
1241 }
1242 }
1243
1244 /* Create new drawings when autokey is enabled. */
1245 if (use_autokey) {
1246 for (const int frame_number : target_frames) {
1247 if (!target_layer.frames().contains(frame_number)) {
1248 if (use_duplicate_frame) {
1249 grease_pencil.insert_duplicate_frame(
1250 target_layer, *target_layer.start_frame_at(frame_number), frame_number, false);
1251 }
1252 else {
1253 grease_pencil.insert_frame(target_layer, frame_number);
1254 }
1255 }
1256 }
1257 }
1258
1260 for (const int frame_number : target_frames) {
1261 if (Drawing *target_drawing = grease_pencil.get_editable_drawing_at(target_layer,
1262 frame_number))
1263 {
1264 MutableDrawingInfo target = {*target_drawing, target_layer_index, frame_number, 1.0f};
1265
1266 Vector<DrawingInfo> sources;
1267 for (const Layer *source_layer : grease_pencil.layers()) {
1268 if (const Drawing *source_drawing = grease_pencil.get_drawing_at(*source_layer,
1269 frame_number))
1270 {
1271 const int source_layer_index = *grease_pencil.get_layer_index(*source_layer);
1272 sources.append({*source_drawing, source_layer_index, frame_number, 0});
1273 }
1274 }
1275
1276 drawings.append({std::move(target), std::move(sources)});
1277 }
1278 }
1279
1280 return drawings;
1281}
1282
1283static void smooth_fill_strokes(bke::CurvesGeometry &curves, const IndexMask &stroke_mask)
1284{
1285 const int iterations = 20;
1286 if (curves.points_num() == 0) {
1287 return;
1288 }
1289 if (stroke_mask.is_empty()) {
1290 return;
1291 }
1292
1293 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1294 const OffsetIndices points_by_curve = curves.points_by_curve();
1295 const VArray<bool> cyclic = curves.cyclic();
1296 const VArray<bool> point_selection = VArray<bool>::ForSingle(true, curves.points_num());
1297
1298 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
1300 points_by_curve,
1301 point_selection,
1302 cyclic,
1303 iterations,
1304 1.0f,
1305 false,
1306 true,
1307 positions.span);
1308 positions.finish();
1309 curves.tag_positions_changed();
1310}
1311
1313{
1314 const OffsetIndices points_by_curve = curves.points_by_curve();
1315 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1316
1317 IndexMaskMemory memory;
1318 IndexMask points_to_keep = IndexMask::from_predicate(
1319 curves.points_range(), GrainSize(2048), memory, [&](const int64_t i) {
1320 const int curve_i = point_to_curve_map[i];
1321 const IndexRange points = points_by_curve[curve_i];
1322 if (points.size() <= 2) {
1323 return true;
1324 }
1325 const int local_i = i - points.start();
1326 return (local_i % int(math::pow(2.0f, float(step))) == 0) || points.last() == i;
1327 });
1328
1329 return bke::curves_copy_point_selection(curves, points_to_keep, {});
1330}
1331
1332static bool grease_pencil_apply_fill(bContext &C, wmOperator &op, const wmEvent &event)
1333{
1337
1338 constexpr const ed::greasepencil::FillToolFitMethod fit_method =
1340 /* Debug setting: keep image data blocks for inspection. */
1341 constexpr const bool keep_images = false;
1342
1343 ARegion &region = *CTX_wm_region(&C);
1344 /* Perform bounds check. */
1345 const bool in_bounds = BLI_rcti_isect_pt_v(&region.winrct, event.xy);
1346 if (!in_bounds) {
1347 return false;
1348 }
1349
1350 wmWindow &win = *CTX_wm_window(&C);
1352 const Scene &scene = *CTX_data_scene(&C);
1353 Object &object = *CTX_data_active_object(&C);
1354 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1355 auto &op_data = *static_cast<GreasePencilFillOpData *>(op.customdata);
1356 const ToolSettings &ts = *CTX_data_tool_settings(&C);
1357 Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
1358 const float2 mouse_position = float2(event.mval);
1359 const int simplify_levels = brush.gpencil_settings->fill_simplylvl;
1360 const std::optional<float> alpha_threshold =
1362 std::nullopt :
1363 std::make_optional(brush.gpencil_settings->fill_threshold);
1364 const bool on_back = (ts.gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK);
1365
1366 if (!grease_pencil.has_active_layer()) {
1367 return false;
1368 }
1369 /* Add drawings in the active layer if autokey is enabled. */
1371 scene, grease_pencil, *grease_pencil.get_active_layer());
1372
1373 const VArray<bool> boundary_layers = get_fill_boundary_layers(
1374 grease_pencil, eGP_FillLayerModes(brush.gpencil_settings->fill_layer_mode));
1375
1376 for (const FillToolTargetInfo &info : target_drawings) {
1377 const Layer &layer = *grease_pencil.layers()[info.target.layer_index];
1378
1380 C, op_data);
1381
1382 bke::CurvesGeometry fill_curves = fill_strokes(view_context,
1383 brush,
1384 scene,
1385 layer,
1386 boundary_layers,
1387 info.sources,
1388 op_data.invert,
1389 alpha_threshold,
1390 mouse_position,
1391 extensions,
1392 fit_method,
1393 op_data.material_index,
1394 keep_images);
1395
1396 smooth_fill_strokes(fill_curves, fill_curves.curves_range());
1397
1398 if (simplify_levels > 0) {
1399 fill_curves = simplify_fixed(fill_curves, brush.gpencil_settings->fill_simplylvl);
1400 }
1401
1402 bke::CurvesGeometry &dst_curves = info.target.drawing.strokes_for_write();
1403 /* If the `fill_strokes` function creates the "fill_opacity" attribute, make sure that we
1404 * initialize this to full opacity on the target geometry. */
1405 if (fill_curves.attributes().contains("fill_opacity") &&
1406 !dst_curves.attributes().contains("fill_opacity"))
1407 {
1408 bke::SpanAttributeWriter<float> fill_opacities =
1410 "fill_opacity",
1413 fill_opacities.finish();
1414 }
1415
1416 Curves *dst_curves_id = curves_new_nomain(dst_curves);
1417 Curves *fill_curves_id = curves_new_nomain(fill_curves);
1418 const Array<bke::GeometrySet> geometry_sets = {
1419 bke::GeometrySet::from_curves(on_back ? fill_curves_id : dst_curves_id),
1420 bke::GeometrySet::from_curves(on_back ? dst_curves_id : fill_curves_id)};
1421 const int num_new_curves = fill_curves.curves_num();
1422 const IndexRange new_curves_range = (on_back ?
1423 IndexRange(num_new_curves) :
1424 dst_curves.curves_range().after(num_new_curves));
1425
1426 bke::GeometrySet joined_geometry_set = geometry::join_geometries(geometry_sets, {});
1427 if (joined_geometry_set.has_curves()) {
1428 dst_curves = joined_geometry_set.get_curves_for_write()->geometry.wrap();
1429 info.target.drawing.tag_topology_changed();
1430
1431 /* Compute texture matrix for the new curves. */
1432 const ed::greasepencil::DrawingPlacement placement(
1433 scene, region, *view_context.v3d, object, &layer);
1435 &scene, &region, mouse_position, placement);
1436 Array<float4x2> texture_matrices(num_new_curves, texture_space);
1437 info.target.drawing.set_texture_matrices(texture_matrices, new_curves_range);
1438 }
1439 }
1440
1442
1443 /* Save extend value for next operation. */
1444 brush.gpencil_settings->fill_extend_fac = op_data.extension_length /
1447
1448 return true;
1449}
1450
1452{
1454
1455 Main &bmain = *CTX_data_main(&C);
1456 Scene &scene = *CTX_data_scene(&C);
1457 Object &ob = *CTX_data_active_object(&C);
1458 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1459 Paint &paint = scene.toolsettings->gp_paint->paint;
1460 Brush &brush = *BKE_paint_brush(&paint);
1461
1462 Layer *layer = grease_pencil.get_active_layer();
1463 /* Cannot paint in locked layer. */
1464 if (layer && layer->is_locked()) {
1465 return false;
1466 }
1467 if (layer == nullptr) {
1468 layer = &grease_pencil.add_layer("GP_Layer");
1469 }
1470
1471 if (brush.gpencil_settings == nullptr) {
1473 }
1483
1485 &bmain, &ob, &brush);
1486 const int material_index = BKE_object_material_index_get(&ob, material);
1487
1488 const bool invert = RNA_boolean_get(op.ptr, "invert");
1489 const bool precision = RNA_boolean_get(op.ptr, "precision");
1490
1491 op.customdata = MEM_new<GreasePencilFillOpData>(
1492 __func__,
1493 GreasePencilFillOpData::from_context(C, *layer, material_index, invert, precision));
1494 return true;
1495}
1496
1498{
1499 const ARegion &region = *CTX_wm_region(&C);
1500 Object &ob = *CTX_data_active_object(&C);
1501 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1502
1504
1505 if (op.customdata) {
1506 auto &op_data = *static_cast<GreasePencilFillOpData *>(op.customdata);
1507
1508 if (op_data.overlay_cb_handle) {
1509 ED_region_draw_cb_exit(region.type, op_data.overlay_cb_handle);
1510 op_data.overlay_cb_handle = nullptr;
1511 }
1512
1513 MEM_delete(static_cast<GreasePencilFillOpData *>(op.customdata));
1514 op.customdata = nullptr;
1515 }
1516
1517 /* Clear status message area. */
1518 ED_workspace_status_text(&C, nullptr);
1519
1521
1524}
1525
1526static int grease_pencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1527{
1528 const ARegion &region = *CTX_wm_region(C);
1530 Brush &brush = *BKE_paint_brush(&ts.gp_paint->paint);
1532 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
1533
1534 /* Fill tool needs a material (cannot use default material). */
1536 brush.gpencil_settings->material == nullptr)
1537 {
1538 BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material");
1539 return OPERATOR_CANCELLED;
1540 }
1541 if (BKE_object_material_get(&ob, ob.actcol) == nullptr) {
1542 BKE_report(op->reports, RPT_ERROR, "Fill tool needs active material");
1543 return OPERATOR_CANCELLED;
1544 }
1545 if (!grease_pencil_fill_init(*C, *op)) {
1546 grease_pencil_fill_exit(*C, *op);
1547 return OPERATOR_CANCELLED;
1548 }
1549 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1550
1553 grease_pencil_fill_update_overlay(region, op_data);
1554
1557
1558 /* Add a modal handler for this operator. */
1560
1562}
1563
1565 Cancel = 1,
1566 Confirm,
1572 Invert,
1573 Precision,
1574};
1575
1577{
1578 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1579 /* Extension line length increment, for normal and precise mode respectively. */
1580 const float extension_delta = (op_data.precision ? 0.002f : 0.02f);
1581
1582 switch (event->val) {
1584 return OPERATOR_CANCELLED;
1585
1587 /* Ignore in extension mode. */
1588 if (op_data.is_extension_drag_active) {
1589 break;
1590 }
1591
1592 op_data.fill_mouse_pos = float2(event->mval);
1594 }
1595
1597 if (op_data.show_extension) {
1598 /* Toggle mode. */
1599 if (op_data.extension_mode == GP_FILL_EMODE_EXTEND) {
1600 op_data.extension_mode = GP_FILL_EMODE_RADIUS;
1601 }
1602 else {
1603 op_data.extension_mode = GP_FILL_EMODE_EXTEND;
1604 }
1605 grease_pencil_update_extend(*C, op_data);
1606 }
1607 break;
1608
1610 op_data.extension_length = std::max(op_data.extension_length - extension_delta, 0.0f);
1611 grease_pencil_update_extend(*C, op_data);
1612 break;
1613
1615 op_data.extension_length = std::min(op_data.extension_length + extension_delta, 10.0f);
1616 grease_pencil_update_extend(*C, op_data);
1617 break;
1618
1620 if (event->val == KM_PRESS) {
1621 /* Consider initial offset as zero position. */
1622 op_data.is_extension_drag_active = true;
1623 /* TODO This is the GPv2 logic and it's weird. Should be reconsidered, for now use the
1624 * same method. */
1625 const float2 base_pos = float2(event->mval);
1626 constexpr const float gap = 300.0f;
1627 op_data.extension_mouse_pos = (math::distance(base_pos, op_data.fill_mouse_pos) >= gap ?
1628 base_pos :
1629 base_pos - float2(gap, 0));
1631 }
1632 if (event->val == KM_RELEASE) {
1634 op_data.is_extension_drag_active = false;
1635 }
1636 /* Update cursor line. */
1639 break;
1640 }
1641
1643 if (op_data.show_extension) {
1644 op_data.extension_cut = !op_data.extension_cut;
1645 grease_pencil_update_extend(*C, op_data);
1646 }
1647 break;
1648
1650 op_data.invert = !op_data.invert;
1651 break;
1652
1654 op_data.precision = !op_data.precision;
1655 break;
1656
1657 default:
1659 break;
1660 }
1662}
1663
1664static int grease_pencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
1665{
1666 const RegionView3D &rv3d = *CTX_wm_region_view3d(C);
1667
1668 auto &op_data = *static_cast<GreasePencilFillOpData *>(op->customdata);
1669
1670 int estate = OPERATOR_RUNNING_MODAL;
1671 switch (event->type) {
1672 case EVT_MODAL_MAP:
1673 estate = grease_pencil_fill_event_modal_map(C, op, event);
1674 break;
1675 case MOUSEMOVE: {
1676 if (!op_data.is_extension_drag_active) {
1677 break;
1678 }
1679
1680 const Object &ob = *CTX_data_active_object(C);
1681 const float pixel_size = ED_view3d_pixel_size(&rv3d, ob.loc);
1682 const float2 mouse_pos = float2(event->mval);
1683 const float initial_dist = math::distance(op_data.extension_mouse_pos,
1684 op_data.fill_mouse_pos);
1685 const float current_dist = math::distance(mouse_pos, op_data.fill_mouse_pos);
1686
1687 float delta = (current_dist - initial_dist) * pixel_size * 0.5f;
1688 op_data.extension_length = std::clamp(op_data.extension_length + delta, 0.0f, 10.0f);
1689
1690 /* Update cursor line and extend lines. */
1693
1694 grease_pencil_update_extend(*C, op_data);
1695 break;
1696 }
1697 default:
1698 break;
1699 }
1700 /* Process last operations before exiting. */
1701 switch (estate) {
1702 case OPERATOR_FINISHED:
1703 grease_pencil_fill_exit(*C, *op);
1705 break;
1706
1707 case OPERATOR_CANCELLED:
1708 grease_pencil_fill_exit(*C, *op);
1709 break;
1710
1711 default:
1712 break;
1713 }
1714
1715 return estate;
1716}
1717
1719{
1720 grease_pencil_fill_exit(*C, *op);
1721}
1722
1724{
1725 PropertyRNA *prop;
1726
1727 ot->name = "Grease Pencil Fill";
1728 ot->idname = "GREASE_PENCIL_OT_fill";
1729 ot->description = "Fill with color the shape formed by strokes";
1730
1735
1737
1738 prop = RNA_def_boolean(
1739 ot->srna, "invert", false, "Invert", "Find boundary of unfilled instead of filled regions");
1741
1742 prop = RNA_def_boolean(
1743 ot->srna, "precision", false, "Precision", "Use precision movement for extension lines");
1745}
1746
1749} // namespace blender::ed::sculpt_paint
1750
1751/* -------------------------------------------------------------------- */
1756{
1757 using namespace blender::ed::sculpt_paint;
1758 WM_operatortype_append(GREASE_PENCIL_OT_brush_stroke);
1759 WM_operatortype_append(GREASE_PENCIL_OT_sculpt_paint);
1760 WM_operatortype_append(GREASE_PENCIL_OT_weight_brush_stroke);
1761 WM_operatortype_append(GREASE_PENCIL_OT_vertex_brush_stroke);
1762 WM_operatortype_append(GREASE_PENCIL_OT_fill);
1763}
1764
1766{
1767 using namespace blender::ed::greasepencil;
1769
1770 static const EnumPropertyItem modal_items[] = {
1771 {int(FillToolModalKey::Cancel), "CANCEL", 0, "Cancel", ""},
1772 {int(FillToolModalKey::Confirm), "CONFIRM", 0, "Confirm", ""},
1773 {int(FillToolModalKey::ExtensionModeToggle),
1774 "EXTENSION_MODE_TOGGLE",
1775 0,
1776 "Toggle Extension Mode",
1777 ""},
1778 {int(FillToolModalKey::ExtensionLengthen),
1779 "EXTENSION_LENGTHEN",
1780 0,
1781 "Lengthen Extensions",
1782 ""},
1783 {int(FillToolModalKey::ExtensionShorten), "EXTENSION_SHORTEN", 0, "Shorten Extensions", ""},
1784 {int(FillToolModalKey::ExtensionDrag), "EXTENSION_DRAG", 0, "Drag Extensions", ""},
1785 {int(FillToolModalKey::ExtensionCollide), "EXTENSION_COLLIDE", 0, "Collide Extensions", ""},
1786 {int(FillToolModalKey::Invert), "INVERT", 0, "Invert", ""},
1787 {int(FillToolModalKey::Precision), "PRECISION", 0, "Precision", ""},
1788 {0, nullptr, 0, nullptr, nullptr},
1789 };
1790
1791 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Fill Tool Modal Map");
1792
1793 /* This function is called for each space-type, only needs to add map once. */
1794 if (keymap && keymap->modal_items) {
1795 return;
1796 }
1797
1798 keymap = WM_modalkeymap_ensure(keyconf, "Fill Tool Modal Map", modal_items);
1799
1800 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_fill");
1801}
1802
Functions to insert, delete or modify keyframes.
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:563
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:621
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:601
Low-level operations for grease pencil.
Material * BKE_grease_pencil_object_material_ensure_from_active_input_brush(Main *bmain, Object *ob, Brush *brush)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct 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:99
@ SculptGreasePencil
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:506
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void(* BVHTree_RayCastCallback)(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
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.
#define BLI_SCOPED_DEFER(function_to_defer)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
#define UNUSED_VARS(...)
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
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_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_RUNNING_MODAL
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
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:245
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr IndexRange after(int64_t n) const
constexpr bool contains(int64_t value) const
constexpr IndexRange index_range() const
bool contains(const Key &key) const
Definition BLI_map.hh:329
static VArray ForSingle(T value, const int64_t size)
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 resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void reserve(const int64_t min_capacity)
Span< T > as_span() const
bool contains(const StringRef attribute_id) const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
const bke::CurvesGeometry & strokes() const
const Map< FramesMapKeyT, GreasePencilFrame > & frames() 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 IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
DEGForeachIDComponentCallback callback
static bool is_cyclic(const Nurb *nu)
KDTree_3d * tree
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void ED_filltool_modal_keymap(wmKeyConfig *keyconf)
void ED_operatortypes_grease_pencil_draw()
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
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)
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)
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op, const bool use_duplicate_previous_key)
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 grease_pencil_sculpting_poll(bContext *C)
bool grease_pencil_weight_painting_poll(bContext *C)
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_vertex_paint_operation(BrushStrokeMode stroke_mode)
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_paint_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_vertex_blur_operation()
std::unique_ptr< GreasePencilStrokeOperation > new_tint_operation()
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_weight_paint_draw_operation(const BrushStrokeMode &brush_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)
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 stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
static bool grease_pencil_weight_brush_stroke_poll(bContext *C)
static void grease_pencil_sculpt_paint_cancel(bContext *C, wmOperator *op)
static int grease_pencil_vertex_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void grease_pencil_fill_exit(bContext &C, wmOperator &op)
static int grease_pencil_weight_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void stroke_update_step(bContext *C, wmOperator *op, PaintStroke *, PointerRNA *stroke_element)
static bool grease_pencil_sculpt_paint_poll(bContext *C)
static int grease_pencil_weight_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void grease_pencil_fill_cancel(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_weight_brush_stroke(wmOperatorType *ot)
static int grease_pencil_brush_stroke_invoke(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 int grease_pencil_vertex_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void GREASE_PENCIL_OT_vertex_brush_stroke(wmOperatorType *ot)
static int grease_pencil_sculpt_paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 bool grease_pencil_vertex_brush_stroke_poll(bContext *C)
static void grease_pencil_vertex_brush_stroke_cancel(bContext *C, wmOperator *op)
static bke::CurvesGeometry simplify_fixed(bke::CurvesGeometry &curves, const int step)
static int grease_pencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int grease_pencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void stroke_redraw(const bContext *C, PaintStroke *, bool)
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
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 int grease_pencil_fill_event_modal_map(bContext *C, wmOperator *op, const wmEvent *event)
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 bool grease_pencil_apply_fill(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 int grease_pencil_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
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)
static int grease_pencil_sculpt_paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr< PaintModeData > mode_data)
static bool grease_pencil_fill_init(bContext &C, wmOperator &op)
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)
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
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
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
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_saturation
struct CurveMapping * curve_rand_hue
struct CurveMapping * curve_rand_uv
struct Material * material
struct CurveMapping * curve_rand_value
char gpencil_sculpt_brush_type
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]
View3D * v3d
Definition ED_view3d.hh:74
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::@346 circles
struct blender::ed::greasepencil::ExtensionData::@345 lines
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)
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
wmTabletData tablet
Definition WM_types.hh:751
short type
Definition WM_types.hh:722
const void * modal_items
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
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:33
@ WM_CURSOR_EW_ARROW
Definition wm_cursors.hh:45
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_MODAL_MAP
@ MOUSEMOVE
@ EVT_TABLET_ERASER
wmOperatorType * ot
Definition wm_files.cc:4125
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
bool WM_toolsystem_active_tool_is_brush(const bContext *C)