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;
60 for (
const MutableDrawingInfo &info : drawings) {
62 object, info.drawing, info.layer_index, memory);
63 if (points_selection.is_empty()) {
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()]) {
80 selected_ranges.append(
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);
285 const int64_t ranges = selected_ranges.size() - 1;
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());
433 using namespace bke::greasepencil;
438 scene->toolsettings,
object);
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);
481 bke::CurvesGeometry &dst_curves = dst_drawing->strokes_for_write();
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();
490 dst_drawing->tag_topology_changed();
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)
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()
bool belongs_to_active_layer
bke::CurvesGeometry * owning_curves
blender::bke::AttrDomain ED_grease_pencil_selection_domain_get(const ToolSettings *tool_settings, const Object *object)
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)
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)
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 *)