36 bke::CurvesGeometry *owning_curves;
38 bool belongs_to_active_layer;
41enum class ActionOnNextRange { Nothing, ReverseExisting, ReverseAddition, ReverseBoth };
43enum class ActiveLayerBehavior { JoinAndCopySelection, JoinSelection };
51Vector<PointsRange> retrieve_selection_ranges(
Object &
object,
52 const Span<MutableDrawingInfo> drawings,
53 const int active_layer_index,
54 int64_t &r_total_points_selected,
55 IndexMaskMemory &memory)
57 Vector<PointsRange> selected_ranges{};
58 r_total_points_selected = 0;
62 object, info.drawing, info.layer_index, memory);
66 r_total_points_selected += points_selection.
size();
68 const Vector<IndexRange> initial_ranges = points_selection.
to_ranges();
69 const bool is_active_layer = info.layer_index == active_layer_index;
76 Vector<IndexRange> ranges{};
77 const Array<int> points_map = info.drawing.strokes().point_to_curve_map();
78 for (
const IndexRange initial_range : initial_ranges) {
79 if (points_map[initial_range.first()] == points_map[initial_range.last()]) {
81 {&info.drawing.strokes_for_write(), initial_range, is_active_layer});
85 IndexRange range = {initial_range.start(), 1};
86 int previous_curve = points_map[range.
start()];
87 for (
const int64_t index : initial_range.drop_front(1)) {
88 const int current_curve = points_map[
index];
89 if (previous_curve != current_curve) {
90 selected_ranges.
append({&info.drawing.strokes_for_write(), range, is_active_layer});
92 previous_curve = current_curve;
95 range = {range.
start(), range.
size() + 1};
99 selected_ranges.
append({&info.drawing.strokes_for_write(), range, is_active_layer});
103 return selected_ranges;
106template<
typename T>
void reverse_point_data(
const IndexRange point_range, MutableSpan<T>
data)
108 data.slice(point_range.
first(), point_range.
size()).reverse();
115void reverse_points_of(bke::CurvesGeometry &dst_curves,
const IndexRange points_to_reverse)
117 bke::MutableAttributeAccessor attributes = dst_curves.attributes_for_write();
119 attributes.foreach_attribute([&](
const bke::AttributeIter &iter) {
127 bke::GSpanAttributeWriter
attribute = attributes.lookup_for_write_span(iter.name);
129 using T = decltype(dummy);
130 reverse_point_data<T>(points_to_reverse, attribute.span.typed<T>());
136void apply_action(ActionOnNextRange action,
137 const IndexRange working_range,
138 const IndexRange adding_range,
139 bke::CurvesGeometry &dst_curves)
154 case ActionOnNextRange::Nothing:
156 case ActionOnNextRange::ReverseExisting: {
157 reverse_points_of(dst_curves, working_range);
160 case ActionOnNextRange::ReverseAddition: {
161 const IndexRange src_range_on_dst = {working_range.
last() + 1, adding_range.
size()};
162 reverse_points_of(dst_curves, src_range_on_dst);
165 case ActionOnNextRange::ReverseBoth: {
166 apply_action(ActionOnNextRange::ReverseExisting, working_range, adding_range, dst_curves);
167 apply_action(ActionOnNextRange::ReverseAddition, working_range, adding_range, dst_curves);
180int64_t compute_closest_range_to(PointsRange &range,
181 const Span<PointsRange> &ranges,
183 ActionOnNextRange &r_action)
185 auto get_range_begin_end = [](
const PointsRange &points_range) -> std::pair<float3, float3> {
186 const Span<float3> current_range_positions = points_range.owning_curves->positions();
187 const float3 range_begin = current_range_positions[points_range.range.first()];
188 const float3 range_end = current_range_positions[points_range.range.last()];
190 return {range_begin, range_end};
193 const auto [cur_range_begin, cur_range_end] = get_range_begin_end(range);
196 int64_t ret_value = starting_from;
197 ActionOnNextRange action = ActionOnNextRange::Nothing;
199 const int64_t iterations = ranges.
size() - starting_from;
200 for (
const int64_t i : IndexRange(starting_from, iterations)) {
201 const auto [range_begin, range_end] = get_range_begin_end(ranges[i]);
204 if (dist < min_dist) {
205 action = ActionOnNextRange::Nothing;
211 if (dist < min_dist) {
212 action = ActionOnNextRange::ReverseExisting;
218 if (dist < min_dist) {
219 action = ActionOnNextRange::ReverseAddition;
225 if (dist < min_dist) {
226 action = ActionOnNextRange::ReverseBoth;
236void copy_range_to_dst(
const PointsRange &points_range,
237 int &dst_starting_point,
238 bke::CurvesGeometry &dst_curves)
240 Array<int> src_raw_offsets(2);
241 Array<int> dst_raw_offsets(2);
243 const int64_t selection_size = points_range.range.size();
244 src_raw_offsets[0] = points_range.range.first();
245 src_raw_offsets[1] = points_range.range.last() + 1;
247 dst_raw_offsets[0] = dst_starting_point;
248 dst_starting_point += selection_size;
249 dst_raw_offsets[1] = dst_starting_point;
251 OffsetIndices<int> src_offsets{src_raw_offsets};
252 OffsetIndices<int> dst_offsets{dst_raw_offsets};
261 dst_curves.attributes_for_write());
264PointsRange copy_point_attributes(MutableSpan<PointsRange> selected_ranges,
265 bke::CurvesGeometry &dst_curves)
279 const PointsRange &first_range = selected_ranges.
first();
280 PointsRange working_range = {&dst_curves, {0, first_range.range.size()},
true};
282 int next_point_index = 0;
283 copy_range_to_dst(first_range, next_point_index, dst_curves);
286 for (
const int64_t i : IndexRange(1, ranges)) {
287 ActionOnNextRange action;
288 const int64_t closest_range = compute_closest_range_to(
289 working_range, selected_ranges, i, action);
290 std::swap(selected_ranges[i], selected_ranges[closest_range]);
291 PointsRange &next_range = selected_ranges[i];
292 copy_range_to_dst(next_range, next_point_index, dst_curves);
293 apply_action(action, working_range.range, next_range.range, dst_curves);
294 working_range.range = {0, next_point_index};
297 return working_range;
300void copy_curve_attributes(Span<PointsRange> ranges_selected, bke::CurvesGeometry &dst_curves)
313 auto src_range = [&]() ->
const PointsRange & {
314 auto it = std::find_if(ranges_selected.
begin(),
315 ranges_selected.
end(),
316 [](
const PointsRange &range) { return range.belongs_to_active_layer; });
318 return it != ranges_selected.
end() ? *it : ranges_selected.
first();
321 const bke::CurvesGeometry &src_curves = *src_range.owning_curves;
322 const Array<int> points_map = src_curves.point_to_curve_map();
323 const int first_selected_curve = points_map[src_range.range.first()];
325 const int final_curve_index = dst_curves.curves_num() - 1;
326 const Array<int> dst_curves_raw_offsets = {final_curve_index, dst_curves.curves_num()};
327 const OffsetIndices<int> dst_curve_offsets{dst_curves_raw_offsets};
334 IndexMask({first_selected_curve, 1}),
335 dst_curves.attributes_for_write());
336 dst_curves.cyclic_for_write().first() =
false;
343void clear_selection_attribute(Span<PointsRange> ranges_selected,
346 for (
const PointsRange &range : ranges_selected) {
347 bke::CurvesGeometry &curves = *range.owning_curves;
348 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
349 bke::SpanAttributeWriter<bool>
selection = attributes.lookup_or_add_for_write_span<
bool>(
350 ".selection", selection_domain);
353 IndexMask{curves.points_num()} :
354 IndexMask{curves.curves_num()};
361void remove_selected_points_in_active_layer(Span<PointsRange> ranges_selected,
362 bke::CurvesGeometry &dst_curves)
364 IndexMaskMemory memory;
365 Vector<int64_t> mask_content;
366 for (
const PointsRange &points_range : ranges_selected) {
367 if (!points_range.belongs_to_active_layer) {
371 Array<int64_t> range_content(points_range.range.size());
372 IndexMask(points_range.range).to_indices(range_content.as_mutable_span());
373 mask_content.
extend(range_content);
377 std::sort(mask_content.
begin(), mask_content.
end());
380 dst_curves.remove_points(
mask, {});
383void append_strokes_from(bke::CurvesGeometry &&other, bke::CurvesGeometry &dst)
385 const int initial_points_num = dst.points_num();
386 const int initial_curves_num = dst.curves_num();
387 const int other_points_num = other.points_num();
388 const int other_curves_num = other.curves_num();
390 dst.resize(initial_points_num + other_points_num, initial_curves_num + other_curves_num);
392 Array<int> other_raw_offsets{0, other_points_num};
393 Array<int> dst_raw_offsets{initial_points_num, initial_points_num + other_points_num};
395 OffsetIndices<int> other_point_offsets{other_raw_offsets};
396 OffsetIndices<int> dst_point_offsets{dst_raw_offsets};
405 dst.attributes_for_write());
407 other_raw_offsets = {0, other_curves_num};
408 dst_raw_offsets = {initial_curves_num, initial_curves_num + other_curves_num};
410 OffsetIndices<int> other_curve_offsets{other_raw_offsets};
411 OffsetIndices<int> dst_curve_offsets{dst_raw_offsets};
420 dst.attributes_for_write());
431int grease_pencil_join_selection_exec(bContext *
C,
wmOperator *op)
433 using namespace bke::greasepencil;
440 if (!grease_pencil.has_active_layer()) {
444 const ActiveLayerBehavior active_layer_behavior =
static_cast<ActiveLayerBehavior
>(
446 const Layer &active_layer = *grease_pencil.get_active_layer();
448 const std::optional<int> opt_layer_index = grease_pencil.get_layer_index(active_layer);
450 const int active_layer_index = *opt_layer_index;
452 Drawing *dst_drawing = grease_pencil.get_editable_drawing_at(*grease_pencil.get_active_layer(),
454 if (dst_drawing ==
nullptr) {
458 IndexMaskMemory memory;
462 Vector<PointsRange> ranges_selected = retrieve_selection_ranges(
463 *
object, editable_drawings, active_layer_index, selected_points_count, memory);
464 if (ranges_selected.
size() <= 1) {
471 bke::CurvesGeometry tmp_curves(selected_points_count, 1);
473 const PointsRange working_range = copy_point_attributes(ranges_selected, tmp_curves);
474 copy_curve_attributes(ranges_selected, tmp_curves);
476 clear_selection_attribute(ranges_selected, selection_domain);
478 Array<PointsRange> working_range_collection = {working_range};
479 clear_selection_attribute(working_range_collection, selection_domain);
482 if (active_layer_behavior == ActiveLayerBehavior::JoinSelection) {
483 remove_selected_points_in_active_layer(ranges_selected, dst_curves);
486 append_strokes_from(std::move(tmp_curves), dst_curves);
488 dst_curves.update_curve_types();
489 dst_curves.tag_topology_changed();
498void GREASE_PENCIL_OT_join_selection(wmOperatorType *
ot)
500 static const EnumPropertyItem active_layer_behavior[] = {
501 {
int(ActiveLayerBehavior::JoinAndCopySelection),
505 "Copy the selection in the new stroke"},
506 {
int(ActiveLayerBehavior::JoinSelection),
510 "Move the selection to the new stroke"},
511 {0,
nullptr, 0,
nullptr,
nullptr},
515 ot->
name =
"Join Selection";
516 ot->
idname =
"GREASE_PENCIL_OT_join_selection";
521 ot->
exec = grease_pencil_join_selection_exec;
529 active_layer_behavior,
530 int(ActiveLayerBehavior::JoinSelection),
532 "Defines how the operator will behave on the selection in the active layer");
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
void DEG_id_tag_update(ID *id, unsigned int flags)
struct GreasePencil GreasePencil
struct wmOperator wmOperator
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
bke::CurvesGeometry & strokes_for_write()
void tag_topology_changed()
Vector< IndexRange > to_ranges() const
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr int64_t size() const
constexpr T & first() const
constexpr const T & first() const
constexpr int64_t size() const
constexpr const T * end() const
constexpr const T * begin() const
void append(const T &value)
void extend(Span< T > array)
Span< T > as_span() const
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
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_operatortypes_grease_pencil_join()
blender::bke::AttrDomain ED_grease_pencil_selection_domain_get(const ToolSettings *tool_settings, const Object *object)
ccl_device_inline float4 mask(const int4 mask, const float4 a)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void gather_attributes_to_groups(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > dst_offsets, const IndexMask &src_selection, MutableAttributeAccessor dst_attributes)
void copy_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
bool editable_grease_pencil_poll(bContext *C)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< float, 3 > float3
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
struct ToolSettings * toolsettings
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)