Blender V5.0
grease_pencil_sculpt_clone.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
5#include "BKE_context.hh"
6#include "BKE_curves.hh"
8#include "BKE_paint.hh"
9
10#include "BLI_bounds.hh"
11
12#include "ED_curves.hh"
13#include "ED_grease_pencil.hh"
14#include "ED_view3d.hh"
15
16#include "WM_api.hh"
17#include "WM_types.hh"
18
20#include "paint_intern.hh"
21
22#include <numeric>
23
25
27 public:
29
30 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
31 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
32 void on_stroke_done(const bContext & /*C*/) override {}
33};
34
35void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
36{
37 Main &bmain = *CTX_data_main(&C);
38 Object &object = *CTX_data_active_object(&C);
39 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
40
41 this->init_stroke(C, start_sample);
42
43 /* NOTE: Only one copy is created at the beginning of each stroke.
44 * GPv2 supposedly has 2 modes:
45 * - Stamp: Clone on stroke start and then transform (the transform part doesn't work)
46 * - Continuous: Create multiple copies during the stroke (disabled)
47 *
48 * Here we only have the GPv2 behavior that actually works for now. */
50 C, [&](const GreasePencilStrokeParams &params, const DeltaProjectionFunc &projection_fn) {
51 /* Only insert on the active layer. */
52 if (&params.layer != grease_pencil.get_active_layer()) {
53 return false;
54 }
55
56 /* TODO: Could become a tool setting. */
57 const bool keep_world_transform = false;
58 const float4x4 object_to_layer = math::invert(params.layer.to_object_space(object));
60 bmain, object, object_to_layer, keep_world_transform, false, params.drawing);
61 if (pasted_curves.is_empty()) {
62 return false;
63 }
64
65 bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
66 const OffsetIndices<int> pasted_points_by_curve = curves.points_by_curve().slice(
67 pasted_curves);
68 const IndexRange pasted_points = IndexRange::from_begin_size(
69 pasted_points_by_curve[0].start(), pasted_points_by_curve.total_size());
70 if (pasted_points.is_empty()) {
71 return false;
72 }
73
74 const Bounds<float3> bounds = *bounds::min_max(curves.positions().slice(pasted_points));
75 const float4x4 transform = params.layer.to_world_space(params.ob_eval);
76 /* FIXME: Projecting the center of the bounds to the view can sometimes fail. This might
77 * result in unexpected behavior on the user end. Figure out a way to not rely on view
78 * space here and compute the translation offset in layer space instead. */
79 float2 view_center(0.0f);
82 view_center,
84 {
85 return false;
86 }
87
88 const float2 &mouse_delta = start_sample.mouse_position - view_center;
90
91 MutableSpan<float3> positions = curves.positions_for_write();
92 threading::parallel_for(pasted_points, 4096, [&](const IndexRange range) {
93 for (const int point_i : range) {
94 positions[point_i] += compute_orig_delta(
95 projection_fn, deformation, point_i, mouse_delta);
96 }
97 });
98 params.drawing.tag_positions_changed();
99
100 return true;
101 });
102}
103
105 const InputSample &extension_sample)
106{
107 this->stroke_extended(extension_sample);
108}
109
110std::unique_ptr<GreasePencilStrokeOperation> new_clone_operation(const BrushStrokeMode stroke_mode)
111{
112 return std::make_unique<CloneOperation>(stroke_mode);
113}
114
115} // namespace blender::ed::sculpt_paint::greasepencil
Object * CTX_data_active_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
#define C
Definition RandGen.cpp:29
constexpr bool is_empty() const
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
void foreach_editable_drawing(const bContext &C, FunctionRef< bool(const GreasePencilStrokeParams &params, const DeltaProjectionFunc &projection_fn)> fn) const
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
IndexRange paste_all_strokes_from_clipboard(Main &bmain, Object &object, const float4x4 &object_to_paste_layer, const bool keep_world_transform, const bool paste_back, bke::greasepencil::Drawing &drawing)
std::unique_ptr< GreasePencilStrokeOperation > new_clone_operation(BrushStrokeMode stroke_mode)
bke::crazyspace::GeometryDeformation get_drawing_deformation(const GreasePencilStrokeParams &params)
float3 compute_orig_delta(const DeltaProjectionFunc &projection_fn, const bke::crazyspace::GeometryDeformation &deformation, int index, const float2 &screen_delta)
std::function< float3(const float3 position, const float2 &screen_delta)> DeltaProjectionFunc
CartesianBasis invert(const CartesianBasis &basis)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
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< float, 2 > float2
BrushStrokeMode