Blender V5.0
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
8
9#include "BLI_array.hh"
10#include "BLI_lasso_2d.hh"
11#include "BLI_rect.h"
12#include "BLI_task.hh"
13
14#include "DNA_brush_types.h"
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
23
24#include "ED_grease_pencil.hh"
25#include "ED_view3d.hh"
26
27#include "RNA_access.hh"
28
29#include "WM_api.hh"
30
32
33static constexpr int BBOX_PADDING = 2;
34
38static bool execute_trim_on_drawing(const int layer_index,
39 const Object &ob_eval,
40 Object &obact,
41 const ARegion &region,
42 const float4x4 &projection,
43 const Span<int2> mcoords,
44 const bool keep_caps,
46{
47 const bke::CurvesGeometry &src = drawing.strokes();
48 const OffsetIndices<int> src_points_by_curve = src.points_by_curve();
49
50 /* Get evaluated geometry. */
53
54 /* Compute screen space positions. */
55 Array<float2> screen_space_positions(src.points_num());
56 threading::parallel_for(src.points_range(), 4096, [&](const IndexRange src_points) {
57 for (const int src_point : src_points) {
58 screen_space_positions[src_point] = ED_view3d_project_float_v2_m4(
59 &region, deformation.positions[src_point], projection);
60 }
61 });
62
63 /* Compute bounding boxes of curves in screen space. The bounding boxes are used to speed
64 * up the search for intersecting curves. */
65 Array<rcti> screen_space_bbox(src.curves_num());
66 threading::parallel_for(src.curves_range(), 512, [&](const IndexRange src_curves) {
67 for (const int src_curve : src_curves) {
68 rcti *bbox = &screen_space_bbox[src_curve];
69 BLI_rcti_init_minmax(bbox);
70
71 const IndexRange src_points = src_points_by_curve[src_curve];
72 for (const int src_point : src_points) {
73 BLI_rcti_do_minmax_v(bbox, int2(screen_space_positions[src_point]));
74 }
75
76 /* Add some padding, otherwise we could just miss intersections. */
77 BLI_rcti_pad(bbox, BBOX_PADDING, BBOX_PADDING);
78 }
79 });
80
81 rcti bbox_lasso;
82 BLI_lasso_boundbox(&bbox_lasso, mcoords);
83
84 /* Collect curves and curve points inside the lasso area. */
85 Vector<int> selected_curves;
86 Vector<Vector<int>> selected_points_in_curves;
87
88 IndexMaskMemory memory;
90 obact, drawing, layer_index, memory);
91 editable_strokes.foreach_index([&](const int src_curve) {
92 /* To speed things up: do a bounding box check on the curve and the lasso area. */
93 if (!BLI_rcti_isect(&bbox_lasso, &screen_space_bbox[src_curve], nullptr)) {
94 return;
95 }
96
97 /* Look for curve points inside the lasso area. */
98 Vector<int> selected_points;
99 for (const int src_point : src_points_by_curve[src_curve]) {
100 /* Check if point is inside the lasso area. */
101 if (BLI_rcti_isect_pt_v(&bbox_lasso, int2(screen_space_positions[src_point])) &&
103 int(screen_space_positions[src_point].x),
104 int(screen_space_positions[src_point].y),
105 IS_CLIPPED))
106 {
107 if (selected_points.is_empty()) {
108 selected_curves.append(src_curve);
109 }
110 selected_points.append(src_point);
111 }
112 }
113 if (!selected_points.is_empty()) {
114 selected_points_in_curves.append(std::move(selected_points));
115 }
116 });
117
118 const IndexMask curve_selection = IndexMask::from_indices(selected_curves.as_span(), memory);
119 /* Abort when the lasso area is empty. */
120 if (curve_selection.is_empty()) {
121 return false;
122 }
123
124 /* Apply trim. */
125 bke::CurvesGeometry cut_strokes = ed::greasepencil::trim::trim_curve_segments(
126 src,
127 screen_space_positions,
128 screen_space_bbox,
129 curve_selection,
130 selected_points_in_curves,
131 keep_caps);
132
133 /* Set the new geometry. */
134 drawing.strokes_for_write() = std::move(cut_strokes);
135 drawing.tag_topology_changed();
136
137 return true;
138}
139
144{
145 const Scene *scene = CTX_data_scene(C);
146 const ARegion *region = CTX_wm_region(C);
147 const RegionView3D *rv3d = CTX_wm_region_view3d(C);
148 const Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
150 Object *ob_eval = DEG_get_evaluated(depsgraph, obact);
151
152 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(obact->data);
153
155 Brush *brush = BKE_paint_brush(paint);
156 if (brush->gpencil_settings == nullptr) {
158 }
159 const bool keep_caps = (brush->gpencil_settings->flag & GP_BRUSH_ERASER_KEEP_CAPS) != 0;
160 const bool active_layer_only = (brush->gpencil_settings->flag & GP_BRUSH_ACTIVE_LAYER_ONLY) != 0;
161 std::atomic<bool> changed = false;
162
163 bool inserted_keyframe = false;
164 if (active_layer_only) {
165 /* Apply trim on drawings of active layer. */
166 if (!grease_pencil.has_active_layer()) {
167 return OPERATOR_CANCELLED;
168 }
169
170 bke::greasepencil::Layer &layer = *grease_pencil.get_active_layer();
171 if (!layer.is_editable()) {
172 return OPERATOR_CANCELLED;
173 }
174
175 ensure_active_keyframe(*scene, grease_pencil, layer, true, inserted_keyframe);
176 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
177 const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(rv3d, layer_to_world);
182 *ob_eval,
183 *obact,
184 *region,
185 projection,
186 mcoords,
187 keep_caps,
188 info.drawing))
189 {
190 changed = true;
191 }
192 });
193 }
194 else {
195 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
196 if (layer->is_editable()) {
198 *scene, grease_pencil, *layer, true, inserted_keyframe);
199 }
200 }
201
202 /* Apply trim on every editable drawing. */
206 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
207 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
208 const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(rv3d, layer_to_world);
210 *ob_eval,
211 *obact,
212 *region,
213 projection,
214 mcoords,
215 keep_caps,
216 info.drawing))
217 {
218 changed = true;
219 }
220 });
221 }
222
223 if (changed) {
224 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
225 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
226 if (inserted_keyframe) {
228 }
229 }
230
231 return OPERATOR_FINISHED;
232}
233
235{
236 const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
237
238 if (mcoords.is_empty()) {
240 }
241
242 return stroke_trim_execute(C, mcoords);
243}
244
245} // namespace blender::ed::greasepencil
246
248{
249 using namespace blender::ed::greasepencil;
250
251 ot->name = "Grease Pencil Trim";
252 ot->idname = "GREASE_PENCIL_OT_stroke_trim";
253 ot->description = "Delete stroke points in between intersecting strokes";
254
255 ot->invoke = WM_gesture_lasso_invoke;
256 ot->modal = WM_gesture_lasso_modal;
259 ot->cancel = WM_gesture_lasso_cancel;
260
262
264}
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:648
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:476
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
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)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ GP_BRUSH_ERASER_KEEP_CAPS
@ GP_BRUSH_ACTIVE_LAYER_ONLY
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
#define IS_CLIPPED
Definition ED_view3d.hh:252
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
BPy_StructRNA * depsgraph
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
bool is_empty() const
void append(const T &value)
bool is_empty() const
bool is_empty() const
Definition BLI_array.hh:264
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
float4x4 to_world_space(const Object &object) const
void foreach_index(Fn &&fn) const
VecBase< int, 2 > int2
void GREASE_PENCIL_OT_stroke_trim(wmOperatorType *ot)
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval, const Object &ob_orig, const bke::greasepencil::Drawing &drawing_orig)
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const bool duplicate_previous_key, bool &r_inserted_keyframe)
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
static wmOperatorStatus grease_pencil_stroke_trim_exec(bContext *C, wmOperator *op)
static constexpr int BBOX_PADDING
static bool execute_trim_on_drawing(const int layer_index, const Object &ob_eval, Object &obact, const ARegion &region, const float4x4 &projection, const Span< int2 > mcoords, const bool keep_caps, bke::greasepencil::Drawing &drawing)
static wmOperatorStatus stroke_trim_execute(const bContext *C, const Span< int2 > mcoords)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
bool grease_pencil_painting_poll(bContext *C)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
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
struct BrushGpencilSettings * gpencil_settings
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(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)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)