Blender V5.0
MOD_grease_pencil_length.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_hash.h"
10#include "BLI_rand.h"
11
12#include "BLT_translation.hh"
13
14#include "BLO_read_write.hh"
15
16#include "DNA_defaults.h"
18#include "DNA_node_types.h" /* For `GeometryNodeCurveSampleMode` */
19#include "DNA_object_types.h"
20#include "DNA_screen_types.h"
21
22#include "BKE_curves.hh"
23#include "BKE_geometry_set.hh"
24#include "BKE_grease_pencil.hh"
25#include "BKE_modifier.hh"
26
28#include "UI_resources.hh"
29
31#include "MOD_modifiertypes.hh"
32#include "MOD_ui_common.hh"
33
34#include "RNA_access.hh"
35#include "RNA_prototypes.hh"
36
37#include "GEO_extend_curves.hh"
38#include "GEO_trim_curves.hh"
39
40namespace blender {
41
51
52static void copy_data(const ModifierData *md, ModifierData *target, int flags)
53{
54 const auto *omd = reinterpret_cast<const GreasePencilLengthModifierData *>(md);
55 auto *tomd = reinterpret_cast<GreasePencilLengthModifierData *>(target);
56
58
59 BKE_modifier_copydata_generic(md, target, flags);
60 modifier::greasepencil::copy_influence_data(&omd->influence, &tomd->influence, flags);
61}
62
63static void free_data(ModifierData *md)
64{
65 auto *omd = reinterpret_cast<GreasePencilLengthModifierData *>(md);
67}
68
69static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
70{
71 auto *omd = reinterpret_cast<GreasePencilLengthModifierData *>(md);
72 modifier::greasepencil::foreach_influence_ID_link(&omd->influence, ob, walk, user_data);
73}
74
75static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
76{
78 reinterpret_cast<const GreasePencilLengthModifierData *>(md);
79
82}
83
90
91static Array<float> noise_table(int len, int offset, int seed)
92{
93 Array<float> table(len);
94 for (const int i : table.index_range()) {
95 table[i] = BLI_hash_int_01(BLI_hash_int_2d(seed, i + offset + 1));
96 }
97 return table;
98}
99
100static float table_sample(MutableSpan<float> table, float x)
101{
102 return math::interpolate(table[int(math::ceil(x))], table[int(math::floor(x))], math::fract(x));
103}
104
105static void deform_drawing(const ModifierData &md,
106 const Object &ob,
108 const int current_time)
109{
111 reinterpret_cast<const GreasePencilLengthModifierData &>(md);
113 bke::CurvesGeometry &curves = drawing.strokes_for_write();
114
115 if (curves.is_empty()) {
116 return;
117 }
118
119 IndexMaskMemory memory;
121 &ob, curves, mmd.influence, memory);
122
123 const int curves_num = curves.curves_num();
124
125 /* Variable for tagging shrinking when values are adjusted after random. */
126 std::atomic<bool> needs_additional_shrinking = false;
127
128 VArray<float> use_starts = VArray<float>::from_single(mmd.start_fac, curves_num);
129 VArray<float> use_ends = VArray<float>::from_single(mmd.end_fac, curves_num);
130
131 Array<float> modified_starts;
132 Array<float> modified_ends;
133 if (mmd.rand_start_fac != 0.0 || mmd.rand_end_fac != 0.0) {
134 modified_starts = Array<float>(curves.curves_num(), mmd.start_fac);
135 modified_ends = Array<float>(curves.curves_num(), mmd.end_fac);
136
137 /* Use random to modify start/end factors. Put the modified values outside the
138 * branch so it could be accessed in later stretching/shrinking stages. */
139 use_starts = VArray<float>::from_span(modified_starts.as_mutable_span());
140 use_ends = VArray<float>::from_span(modified_ends.as_mutable_span());
141
142 int seed = mmd.seed;
143
144 /* Make sure different modifiers get different seeds. */
145 seed += BLI_hash_string(ob.id.name + 2);
147
148 if (mmd.flag & GP_LENGTH_USE_RANDOM) {
149 seed += current_time / mmd.step;
150 }
151
152 float rand_offset = BLI_hash_int_01(seed);
153
154 Array<float> noise_table_length = noise_table(
155 4 + curves_num, int(math::floor(mmd.rand_offset)), seed + 2);
156
157 threading::parallel_for(IndexRange(curves_num), 512, [&](const IndexRange parallel_range) {
158 for (const int i : parallel_range) {
159 /* To ensure a nice distribution, we use halton sequence and offset using the seed. */
160 double r[2];
161 const uint primes[2] = {2, 3};
162 double offset[2] = {0.0f, 0.0f};
163 BLI_halton_2d(primes, offset, i, r);
164
165 float rand[2] = {0.0f, 0.0f};
166 for (int j = 0; j < 2; j++) {
167 float noise = table_sample(noise_table_length, i + j * 2 + math::fract(mmd.rand_offset));
168
169 rand[j] = math::mod(float(r[j] + rand_offset), 1.0f);
170 rand[j] = math::abs(
171 math::mod(sin(rand[j] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f) + noise);
172 }
173
174 modified_starts[i] = modified_starts[i] + rand[0] * mmd.rand_start_fac;
175 modified_ends[i] = modified_ends[i] + rand[1] * mmd.rand_end_fac;
176
177 if (modified_starts[i] <= 0.0f || modified_ends[i] <= 0.0f) {
178 needs_additional_shrinking.store(true, std::memory_order_relaxed);
179 }
180 }
181 });
182 }
183
184 curves = geometry::extend_curves(curves,
185 selection,
186 use_starts,
187 use_ends,
188 mmd.overshoot_fac,
189 (mmd.flag & GP_LENGTH_USE_CURVATURE) != 0,
190 mmd.point_density,
192 mmd.max_angle,
193 (mmd.flag & GP_LENGTH_INVERT_CURVATURE) != 0,
194 ((mmd.mode & GP_LENGTH_ABSOLUTE) != 0) ?
197 {});
198
199 /* Always do the stretching first since it might depend on points which could be deleted by the
200 * shrink. */
201 if (mmd.start_fac < 0.0f || mmd.end_fac < 0.0f || needs_additional_shrinking) {
202 /* #trim_curves() accepts the `end` values if it's sampling from the beginning of the
203 * curve, so we need to get the lengths of the curves and subtract it from the back when the
204 * modifier is in Absolute mode. For convenience, we always call #trim_curves() in LENGTH
205 * mode since the function itself will need length to be sampled anyway. */
206 Array<float> starts(curves.curves_num());
207 Array<float> ends(curves.curves_num());
208 Array<bool> needs_removal(curves.curves_num());
209 needs_removal.fill(false);
210
212 const VArray<bool> &cyclic = curves.cyclic();
213
214 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange parallel_range) {
215 for (const int curve : parallel_range) {
216 float length = curves.evaluated_length_total_for_curve(curve, cyclic[curve]);
217 if (mmd.mode & GP_LENGTH_ABSOLUTE) {
218 starts[curve] = -math::min(use_starts[curve], 0.0f);
219 ends[curve] = length - -math::min(use_ends[curve], 0.0f);
220 }
221 else {
222 starts[curve] = -math::min(use_starts[curve], 0.0f) * length;
223 ends[curve] = (1 + math::min(use_ends[curve], 0.0f)) * length;
224 }
225 if (starts[curve] > ends[curve]) {
226 needs_removal[curve] = true;
227 }
228 }
229 });
230 curves = geometry::trim_curves(curves,
231 selection,
232 VArray<float>::from_span(starts.as_span()),
233 VArray<float>::from_span(ends.as_span()),
235 {});
236
237 /* #trim_curves() will leave the last segment there when trimmed length is greater than
238 * curve original length, thus we need to remove those curves afterwards. */
239 IndexMaskMemory memory_remove;
240 const IndexMask to_remove = IndexMask::from_bools(needs_removal.as_span(), memory_remove);
241 if (!to_remove.is_empty()) {
242 curves.remove_curves(to_remove, {});
243 }
244 }
245
246 drawing.tag_topology_changed();
247}
248
250 const ModifierEvalContext *ctx,
251 blender::bke::GeometrySet *geometry_set)
252{
254
255 if (!geometry_set->has_grease_pencil()) {
256 return;
257 }
258
259 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
260
261 IndexMaskMemory mask_memory;
263 grease_pencil, mmd->influence, mask_memory);
266 grease_pencil, layer_mask, grease_pencil.runtime->eval_frame);
267
269 deform_drawing(*md, *ctx->object, *drawing, grease_pencil.runtime->eval_frame);
270 });
271}
272
273static void panel_draw(const bContext *C, Panel *panel)
274{
275 uiLayout *layout = panel->layout;
276
278
279 layout->use_property_split_set(true);
280 layout->prop(ptr, "mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
281
282 uiLayout *col = &layout->column(true);
283
284 if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) {
285 col->prop(ptr, "start_factor", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
286 col->prop(ptr, "end_factor", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
287 }
288 else {
289 col->prop(ptr, "start_length", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
290 col->prop(ptr, "end_length", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
291 }
292
293 layout->prop(ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Used Length"), ICON_NONE);
294 PanelLayout random_panel_layout = layout->panel_prop_with_bool_header(
295 C, ptr, "open_random_panel", ptr, "use_random", IFACE_("Randomize"));
296 if (uiLayout *random_layout = random_panel_layout.body) {
297 uiLayout *subcol = &random_layout->column(false);
298 subcol->use_property_split_set(true);
299 subcol->active_set(RNA_boolean_get(ptr, "use_random"));
300
301 subcol->prop(ptr, "step", UI_ITEM_NONE, std::nullopt, ICON_NONE);
302
303 subcol->prop(ptr, "random_start_factor", UI_ITEM_NONE, IFACE_("Offset Start"), ICON_NONE);
304 subcol->prop(ptr, "random_end_factor", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
305 subcol->prop(ptr, "random_offset", UI_ITEM_NONE, IFACE_("Noise Offset"), ICON_NONE);
306 subcol->prop(ptr, "seed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
307 }
308 PanelLayout curvature_panel_layout = layout->panel_prop_with_bool_header(
309 C, ptr, "open_curvature_panel", ptr, "use_curvature", IFACE_("Curvature"));
310 if (uiLayout *curvature_layout = curvature_panel_layout.body) {
311 uiLayout *subcol = &curvature_layout->column(false);
312 subcol->use_property_split_set(true);
313 subcol->active_set(RNA_boolean_get(ptr, "use_curvature"));
314
315 subcol->prop(ptr, "point_density", UI_ITEM_NONE, std::nullopt, ICON_NONE);
316 subcol->prop(ptr, "segment_influence", UI_ITEM_NONE, std::nullopt, ICON_NONE);
317 subcol->prop(ptr, "max_angle", UI_ITEM_NONE, std::nullopt, ICON_NONE);
318 subcol->prop(ptr, "invert_curvature", UI_ITEM_NONE, IFACE_("Invert"), ICON_NONE);
319 }
320
321 if (uiLayout *influence_panel = layout->panel_prop(
322 C, ptr, "open_influence_panel", IFACE_("Influence")))
323 {
326 }
327
329}
330
335
336} // namespace blender
337
339 /*idname*/ "GreasePencilLengthModifier",
340 /*name*/ N_("Length"),
341 /*struct_name*/ "GreasePencilLengthModifierData",
342 /*struct_size*/ sizeof(GreasePencilLengthModifierData),
343 /*srna*/ &RNA_GreasePencilLengthModifier,
345 /*flags*/
348 /*icon*/ ICON_MOD_LENGTH,
349
350 /*copy_data*/ blender::copy_data,
351
352 /*deform_verts*/ nullptr,
353 /*deform_matrices*/ nullptr,
354 /*deform_verts_EM*/ nullptr,
355 /*deform_matrices_EM*/ nullptr,
356 /*modify_mesh*/ nullptr,
357 /*modify_geometry_set*/ blender::modify_geometry_set,
358
359 /*init_data*/ blender::init_data,
360 /*required_data_mask*/ nullptr,
361 /*free_data*/ blender::free_data,
362 /*is_disabled*/ nullptr,
363 /*update_depsgraph*/ nullptr,
364 /*depends_on_time*/ nullptr,
365 /*depends_on_normals*/ nullptr,
366 /*foreach_ID_link*/ blender::foreach_ID_link,
367 /*foreach_tex_link*/ nullptr,
368 /*free_runtime_data*/ nullptr,
369 /*panel_register*/ blender::panel_register,
370 /*blend_write*/ blender::blend_write,
371 /*blend_read*/ blender::blend_read,
372};
Low-level operations for curves.
Low-level operations for grease pencil.
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_AcceptsGreasePencil
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
#define BLI_assert(a)
Definition BLI_assert.h:46
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition BLI_hash.h:92
BLI_INLINE unsigned int BLI_hash_string(const char *str)
Definition BLI_hash.h:67
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition BLI_hash.h:51
Random number functions.
void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r)
Definition rand.cc:237
unsigned int uint
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define IFACE_(msgid)
#define DNA_struct_default_get(struct_name)
@ GP_LENGTH_INVERT_CURVATURE
@ GP_LENGTH_USE_RANDOM
@ GP_LENGTH_USE_CURVATURE
@ GP_LENGTH_ABSOLUTE
@ GP_LENGTH_RELATIVE
@ eModifierType_GreasePencilLength
@ GEO_NODE_CURVE_SAMPLE_FACTOR
@ GEO_NODE_CURVE_SAMPLE_LENGTH
Object is a sort of wrapper for general info.
static void panel_register(ARegionType *region_type)
static void panel_draw(const bContext *, Panel *panel)
ModifierTypeInfo modifierType_GreasePencilLength
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, blender::bke::GeometrySet *geometry_set)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
#define C
Definition RandGen.cpp:29
@ UI_ITEM_R_SLIDER
#define UI_ITEM_NONE
static unsigned long seed
Definition btSoftBody.h:39
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
IndexRange index_range() const
Definition BLI_array.hh:360
void fill(const T &value) const
Definition BLI_array.hh:272
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
static VArray from_single(T value, const int64_t size)
static VArray from_span(Span< T > values)
IndexRange curves_range() const
void remove_curves(const IndexMask &curves_to_delete, const AttributeFilter &attribute_filter)
VArray< bool > cyclic() const
bke::CurvesGeometry & strokes_for_write()
uint col
#define sin
bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &starts, const VArray< float > &ends, GeometryNodeCurveSampleMode mode, const bke::AttributeFilter &attribute_filter)
bke::CurvesGeometry extend_curves(bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &start_lengths, const VArray< float > &end_lengths, float overshoot_fac, bool follow_curvature, float point_density, float segment_influence, float max_angle, bool invert_curvature, GeometryNodeCurveSampleMode sample_mode, const bke::AttributeFilter &attribute_filter)
T floor(const T &a)
T interpolate(const T &a, const T &b, const FactorT &t)
T fract(const T &a)
T ceil(const T &a)
T mod(const T &a, const T &b)
T abs(const T &a)
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
static IndexMask get_filtered_stroke_mask(const Object *ob, const bke::CurvesGeometry &curves, const Material *material_filter, const std::optional< int > material_pass_filter, const bool material_filter_invert, const bool material_pass_filter_invert, IndexMaskMemory &memory)
void write_influence_data(BlendWriter *writer, const GreasePencilModifierInfluenceData *influence_data)
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, const std::optional< StringRef > tree_node_name_filter, const std::optional< int > layer_pass_filter, const bool layer_filter_invert, const bool layer_pass_filter_invert, IndexMaskMemory &memory)
Vector< bke::greasepencil::Drawing * > get_drawings_for_write(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
void draw_material_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_layer_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, Object *ob, IDWalkFunc walk, void *user_data)
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, GreasePencilModifierInfluenceData *influence_data_dst, const int)
void ensure_no_bezier_curves(Drawing &drawing)
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
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static float table_sample(MutableSpan< float > table, float x)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void panel_draw(const bContext *C, Panel *panel)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static void deform_drawing(const ModifierData &md, const Object &ob, bke::greasepencil::Drawing &drawing)
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void blend_read(BlendDataReader *reader, ModifierData *md)
static Array< float > noise_table(int len, int offset, int seed)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
GreasePencilModifierInfluenceData influence
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
struct uiLayout * layout
GreasePencil * get_grease_pencil_for_write()
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
PanelLayout panel_prop_with_bool_header(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name, PointerRNA *bool_prop_owner, blender::StringRefNull bool_prop_name, std::optional< blender::StringRef > label)
uiLayout & column(bool align)
void active_set(bool active)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
uint len
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238