Blender V4.3
grease_pencil_trim.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_array.hh"
11#include "BLI_lasso_2d.hh"
12#include "BLI_math_geom.h"
13#include "BLI_rect.h"
14#include "BLI_task.hh"
15
16#include "BKE_brush.hh"
17#include "BKE_context.hh"
18#include "BKE_crazyspace.hh"
19#include "BKE_curves.hh"
20#include "BKE_paint.hh"
21#include "BKE_report.hh"
22
24
25#include "ED_grease_pencil.hh"
26#include "ED_view3d.hh"
27
28#include "RNA_access.hh"
29#include "RNA_define.hh"
30
31#include "WM_api.hh"
32
34
35static constexpr int BBOX_PADDING = 2;
36
40static bool execute_trim_on_drawing(const int layer_index,
41 const int frame_number,
42 const Object &ob_eval,
43 Object &obact,
44 const ARegion &region,
45 const float4x4 &projection,
46 const Span<int2> mcoords,
47 const bool keep_caps,
49{
50 const bke::CurvesGeometry &src = drawing.strokes();
51 const OffsetIndices<int> src_points_by_curve = src.points_by_curve();
52
53 /* Get evaluated geometry. */
56 &ob_eval, obact, layer_index, frame_number);
57
58 /* Compute screen space positions. */
59 Array<float2> screen_space_positions(src.points_num());
60 threading::parallel_for(src.points_range(), 4096, [&](const IndexRange src_points) {
61 for (const int src_point : src_points) {
62 screen_space_positions[src_point] = ED_view3d_project_float_v2_m4(
63 &region, deformation.positions[src_point], projection);
64 }
65 });
66
67 /* Compute bounding boxes of curves in screen space. The bounding boxes are used to speed
68 * up the search for intersecting curves. */
69 Array<rcti> screen_space_bbox(src.curves_num());
70 threading::parallel_for(src.curves_range(), 512, [&](const IndexRange src_curves) {
71 for (const int src_curve : src_curves) {
72 rcti *bbox = &screen_space_bbox[src_curve];
73 BLI_rcti_init_minmax(bbox);
74
75 const IndexRange src_points = src_points_by_curve[src_curve];
76 for (const int src_point : src_points) {
77 BLI_rcti_do_minmax_v(bbox, int2(screen_space_positions[src_point]));
78 }
79
80 /* Add some padding, otherwise we could just miss intersections. */
81 BLI_rcti_pad(bbox, BBOX_PADDING, BBOX_PADDING);
82 }
83 });
84
85 rcti bbox_lasso;
86 BLI_lasso_boundbox(&bbox_lasso, mcoords);
87
88 /* Collect curves and curve points inside the lasso area. */
89 Vector<int> selected_curves;
90 Vector<Vector<int>> selected_points_in_curves;
91
92 IndexMaskMemory memory;
94 obact, drawing, layer_index, memory);
95 editable_strokes.foreach_index([&](const int src_curve) {
96 /* To speed things up: do a bounding box check on the curve and the lasso area. */
97 if (!BLI_rcti_isect(&bbox_lasso, &screen_space_bbox[src_curve], nullptr)) {
98 return;
99 }
100
101 /* Look for curve points inside the lasso area. */
102 Vector<int> selected_points;
103 for (const int src_point : src_points_by_curve[src_curve]) {
104 /* Check if point is inside the lasso area. */
105 if (BLI_rcti_isect_pt_v(&bbox_lasso, int2(screen_space_positions[src_point])) &&
107 int(screen_space_positions[src_point].x),
108 int(screen_space_positions[src_point].y),
109 IS_CLIPPED))
110 {
111 if (selected_points.is_empty()) {
112 selected_curves.append(src_curve);
113 }
114 selected_points.append(src_point);
115 }
116 }
117 if (!selected_points.is_empty()) {
118 selected_points_in_curves.append(std::move(selected_points));
119 }
120 });
121
122 const IndexMask curve_selection = IndexMask::from_indices(selected_curves.as_span(), memory);
123 /* Abort when the lasso area is empty. */
124 if (curve_selection.is_empty()) {
125 return false;
126 }
127
128 /* Apply trim. */
129 bke::CurvesGeometry cut_strokes = ed::greasepencil::trim::trim_curve_segments(
130 src,
131 screen_space_positions,
132 screen_space_bbox,
133 curve_selection,
134 selected_points_in_curves,
135 keep_caps);
136
137 /* Set the new geometry. */
138 drawing.strokes_for_write() = std::move(cut_strokes);
139 drawing.tag_topology_changed();
140
141 return true;
142}
143
147static int stroke_trim_execute(const bContext *C, const Span<int2> mcoords)
148{
149 const Scene *scene = CTX_data_scene(C);
150 const ARegion *region = CTX_wm_region(C);
151 const RegionView3D *rv3d = CTX_wm_region_view3d(C);
152 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
153 Object *obact = CTX_data_active_object(C);
154 Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
155
156 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(obact->data);
157
159 Brush *brush = BKE_paint_brush(paint);
160 if (brush->gpencil_settings == nullptr) {
162 }
163 const bool keep_caps = (brush->gpencil_settings->flag & GP_BRUSH_ERASER_KEEP_CAPS) != 0;
164 const bool active_layer_only = (brush->gpencil_settings->flag & GP_BRUSH_ACTIVE_LAYER_ONLY) != 0;
165 std::atomic<bool> changed = false;
166
167 if (active_layer_only) {
168 /* Apply trim on drawings of active layer. */
169 if (!grease_pencil.has_active_layer()) {
170 return OPERATOR_CANCELLED;
171 }
172 const bke::greasepencil::Layer &layer = *grease_pencil.get_active_layer();
173 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
174 const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(rv3d, layer_to_world);
176 ed::greasepencil::retrieve_editable_drawings_from_layer(*scene, grease_pencil, layer);
177 threading::parallel_for_each(drawings, [&](const ed::greasepencil::MutableDrawingInfo &info) {
179 info.frame_number,
180 *ob_eval,
181 *obact,
182 *region,
183 projection,
184 mcoords,
185 keep_caps,
186 info.drawing))
187 {
188 changed = true;
189 }
190 });
191 }
192 else {
193 /* Apply trim on every editable drawing. */
195 ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
196 threading::parallel_for_each(drawings, [&](const ed::greasepencil::MutableDrawingInfo &info) {
197 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
198 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
199 const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(rv3d, layer_to_world);
201 info.frame_number,
202 *ob_eval,
203 *obact,
204 *region,
205 projection,
206 mcoords,
207 keep_caps,
208 info.drawing))
209 {
210 changed = true;
211 }
212 });
213 }
214
215 if (changed) {
216 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
217 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
218 }
219
220 return OPERATOR_FINISHED;
221}
222
224{
225 const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
226
227 if (mcoords.is_empty()) {
229 }
230
231 return stroke_trim_execute(C, mcoords);
232}
233
234} // namespace blender::ed::greasepencil
235
237{
238 using namespace blender::ed::greasepencil;
239
240 ot->name = "Grease Pencil Trim";
241 ot->idname = "GREASE_PENCIL_OT_stroke_trim";
242 ot->description = "Delete stroke points in between intersecting strokes";
243
246 ot->exec = grease_pencil_stroke_trim;
247 ot->poll = grease_pencil_painting_poll;
249
251
253}
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:563
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Low-level operations for curves.
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
void BLI_lasso_boundbox(rcti *rect, blender::Span< blender::int2 > mcoords)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ GP_BRUSH_ERASER_KEEP_CAPS
@ GP_BRUSH_ACTIVE_LAYER_ONLY
@ OPERATOR_PASS_THROUGH
#define IS_CLIPPED
Definition ED_view3d.hh:239
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
@ 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
bool is_empty() const
Definition BLI_array.hh:253
void append(const T &value)
Span< T > as_span() const
OffsetIndices< int > points_by_curve() const
IndexRange points_range() const
const bke::CurvesGeometry & strokes() const
void foreach_index(Fn &&fn) const
const Depsgraph * depsgraph
void GREASE_PENCIL_OT_stroke_trim(wmOperatorType *ot)
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval, const Object &ob_orig, int layer_index, int frame)
static int grease_pencil_stroke_trim(bContext *C, wmOperator *op)
static int stroke_trim_execute(const bContext *C, const Span< int2 > mcoords)
static constexpr int BBOX_PADDING
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static bool execute_trim_on_drawing(const int layer_index, const int frame_number, const Object &ob_eval, Object &obact, const ARegion &region, const float4x4 &projection, const Span< int2 > mcoords, const bool keep_caps, bke::greasepencil::Drawing &drawing)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
struct BrushGpencilSettings * gpencil_settings
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
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)