37 bke::greasepencil::Drawing *from_drawing;
41enum class ActionOnNextRange { Nothing, ReverseExisting, ReverseAddition, ReverseBoth };
43enum class ActiveLayerBehavior { JoinAndCopySelection, JoinSelection };
52 const Span<MutableDrawingInfo> drawings,
53 int64_t &r_total_points_selected,
54 IndexMaskMemory &memory)
57 r_total_points_selected = 0;
61 object, info.drawing, info.layer_index, memory);
65 r_total_points_selected += points_selection.
size();
75 const Array<int> points_map = info.drawing.strokes().point_to_curve_map();
76 for (
const IndexRange initial_range : initial_ranges) {
77 if (points_map[initial_range.first()] == points_map[initial_range.last()]) {
78 selected_ranges.
append({&info.drawing, initial_range});
82 IndexRange range = {initial_range.start(), 1};
83 int previous_curve = points_map[range.
start()];
84 for (
const int64_t index : initial_range.drop_front(1)) {
85 const int current_curve = points_map[index];
86 if (previous_curve != current_curve) {
87 selected_ranges.
append({&info.drawing, range});
89 previous_curve = current_curve;
92 range = {range.
start(), range.
size() + 1};
96 selected_ranges.
append({&info.drawing, range});
100 return selected_ranges;
103template<
typename T>
void reverse_point_data(
const IndexRange point_range, MutableSpan<T>
data)
105 data.slice(point_range.
first(), point_range.
size()).reverse();
109void swap_handle_attributes(MutableSpan<T> handles_left, MutableSpan<T> handles_right)
113 for (const int point : range) {
114 std::swap(handles_left[point], handles_right[point]);
123void reverse_points_of(bke::CurvesGeometry &dst_curves,
const IndexRange points_to_reverse)
125 bke::MutableAttributeAccessor attributes = dst_curves.attributes_for_write();
127 attributes.foreach_attribute([&](
const bke::AttributeIter &iter) {
135 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
137 using T = decltype(dummy);
138 reverse_point_data<T>(points_to_reverse, attribute.span.typed<T>());
144 if (attributes.contains(
"handle_left") && attributes.contains(
"handle_right")) {
145 MutableSpan<float3> handles_left = dst_curves.handle_positions_left_for_write().slice(
147 MutableSpan<float3> handles_right = dst_curves.handle_positions_right_for_write().slice(
149 swap_handle_attributes<float3>(handles_left, handles_right);
151 if (attributes.contains(
".selection_handle_left") &&
152 attributes.contains(
".selection_handle_right"))
154 bke::SpanAttributeWriter<bool> writer_left = attributes.lookup_for_write_span<
bool>(
155 ".selection_handle_left");
156 bke::SpanAttributeWriter<bool> writer_right = attributes.lookup_for_write_span<
bool>(
157 ".selection_handle_right");
158 const MutableSpan<bool> selection_left = writer_left.span.slice(points_to_reverse);
159 const MutableSpan<bool> selection_right = writer_right.span.slice(points_to_reverse);
160 swap_handle_attributes<bool>(selection_left, selection_right);
161 writer_left.finish();
162 writer_right.finish();
164 if (attributes.contains(
"handle_type_left") && attributes.contains(
"handle_type_right")) {
165 MutableSpan<int8_t> types_left = dst_curves.handle_types_left_for_write().slice(
167 MutableSpan<int8_t> types_right = dst_curves.handle_types_right_for_write().slice(
169 swap_handle_attributes<int8_t>(types_left, types_right);
173void apply_action(ActionOnNextRange action,
174 const IndexRange working_range,
175 const IndexRange adding_range,
176 bke::CurvesGeometry &dst_curves)
191 case ActionOnNextRange::Nothing:
193 case ActionOnNextRange::ReverseExisting: {
194 reverse_points_of(dst_curves, working_range);
197 case ActionOnNextRange::ReverseAddition: {
198 const IndexRange src_range_on_dst = {working_range.
last() + 1, adding_range.
size()};
199 reverse_points_of(dst_curves, src_range_on_dst);
202 case ActionOnNextRange::ReverseBoth: {
203 apply_action(ActionOnNextRange::ReverseExisting, working_range, adding_range, dst_curves);
204 apply_action(ActionOnNextRange::ReverseAddition, working_range, adding_range, dst_curves);
217int64_t compute_closest_range_to(PointsRange &range,
218 const Span<PointsRange> &ranges,
220 ActionOnNextRange &r_action)
222 auto get_range_begin_end = [](
const PointsRange &points_range) -> std::pair<float3, float3> {
223 const Span<float3> current_range_positions = points_range.from_drawing->strokes().positions();
224 const float3 range_begin = current_range_positions[points_range.range.first()];
225 const float3 range_end = current_range_positions[points_range.range.last()];
227 return {range_begin, range_end};
230 const auto [cur_range_begin, cur_range_end] = get_range_begin_end(range);
233 int64_t ret_value = starting_from;
234 ActionOnNextRange action = ActionOnNextRange::Nothing;
236 const int64_t iterations = ranges.
size() - starting_from;
237 for (
const int64_t i : IndexRange(starting_from, iterations)) {
238 const auto [range_begin, range_end] = get_range_begin_end(ranges[
i]);
241 if (dist < min_dist) {
242 action = ActionOnNextRange::Nothing;
248 if (dist < min_dist) {
249 action = ActionOnNextRange::ReverseExisting;
255 if (dist < min_dist) {
256 action = ActionOnNextRange::ReverseAddition;
262 if (dist < min_dist) {
263 action = ActionOnNextRange::ReverseBoth;
273void copy_range_to_dst(
const PointsRange &points_range,
274 int &dst_starting_point,
275 bke::CurvesGeometry &dst_curves)
277 Array<int> src_raw_offsets(2);
278 Array<int> dst_raw_offsets(2);
280 const int64_t selection_size = points_range.range.size();
281 src_raw_offsets[0] = points_range.range.first();
282 src_raw_offsets[1] = points_range.range.last() + 1;
284 dst_raw_offsets[0] = dst_starting_point;
285 dst_starting_point += selection_size;
286 dst_raw_offsets[1] = dst_starting_point;
288 OffsetIndices<int> src_offsets{src_raw_offsets};
289 OffsetIndices<int> dst_offsets{dst_raw_offsets};
298 dst_curves.attributes_for_write());
301PointsRange copy_point_attributes(MutableSpan<PointsRange> selected_ranges,
302 bke::CurvesGeometry &dst_curves,
303 bke::greasepencil::Drawing &dst_drawing)
317 const PointsRange &first_range = selected_ranges.
first();
318 PointsRange working_range = {&dst_drawing, {0, first_range.range.size()}};
320 int next_point_index = 0;
321 copy_range_to_dst(first_range, next_point_index, dst_curves);
324 for (
const int64_t i : IndexRange(1, ranges)) {
325 ActionOnNextRange action;
326 const int64_t closest_range = compute_closest_range_to(
327 working_range, selected_ranges,
i, action);
328 std::swap(selected_ranges[
i], selected_ranges[closest_range]);
329 PointsRange &next_range = selected_ranges[
i];
330 copy_range_to_dst(next_range, next_point_index, dst_curves);
331 apply_action(action, working_range.range, next_range.range, dst_curves);
332 working_range.range = {0, next_point_index};
335 return working_range;
338void copy_curve_attributes(Span<PointsRange> ranges_selected,
339 bke::CurvesGeometry &dst_curves,
340 bke::greasepencil::Drawing &dst_drawing)
353 auto src_range = [&]() ->
const PointsRange & {
354 const auto *it = std::find_if(
355 ranges_selected.
begin(), ranges_selected.
end(), [dst_drawing](
const PointsRange &range) {
356 return range.from_drawing == &dst_drawing;
359 return it != ranges_selected.
end() ? *it : ranges_selected.
first();
362 const bke::CurvesGeometry &src_curves = src_range.from_drawing->strokes();
363 const Array<int> points_map = src_curves.point_to_curve_map();
364 const int first_selected_curve = points_map[src_range.range.first()];
366 const int final_curve_index = dst_curves.curves_num() - 1;
367 const Array<int> dst_curves_raw_offsets = {final_curve_index, dst_curves.curves_num()};
368 const OffsetIndices<int> dst_curve_offsets{dst_curves_raw_offsets};
375 IndexMask({first_selected_curve, 1}),
376 dst_curves.attributes_for_write());
377 dst_curves.cyclic_for_write().first() =
false;
384void clear_selection_attribute(Span<PointsRange> ranges_selected)
386 for (
const PointsRange &range : ranges_selected) {
387 bke::CurvesGeometry &curves = range.from_drawing->strokes_for_write();
388 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
389 if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(
".selection")) {
396void remove_selected_points(Span<PointsRange> ranges_selected)
400 using RangesMap = Map<bke::greasepencil::Drawing *, Vector<IndexMask>>;
401 RangesMap ranges_by_drawing;
402 for (
const PointsRange &points_range : ranges_selected) {
403 BLI_assert(points_range.from_drawing !=
nullptr);
404 Vector<IndexMask> &ranges = ranges_by_drawing.lookup_or_add(points_range.from_drawing, {});
405 ranges.
append(points_range.range);
408 for (
const RangesMap::Item &item : ranges_by_drawing.items()) {
409 bke::CurvesGeometry &dst_curves = item.key->strokes_for_write();
410 IndexMaskMemory memory;
412 dst_curves.remove_points(combined_mask, {});
416void append_strokes_from(bke::CurvesGeometry &&other, bke::CurvesGeometry &dst)
418 const int initial_points_num = dst.points_num();
419 const int initial_curves_num = dst.curves_num();
420 const int other_points_num = other.points_num();
421 const int other_curves_num = other.curves_num();
423 dst.resize(initial_points_num + other_points_num, initial_curves_num + other_curves_num);
425 Array<int> other_raw_offsets{0, other_points_num};
426 Array<int> dst_raw_offsets{initial_points_num, initial_points_num + other_points_num};
428 OffsetIndices<int> other_point_offsets{other_raw_offsets};
429 OffsetIndices<int> dst_point_offsets{dst_raw_offsets};
438 dst.attributes_for_write());
440 other_raw_offsets = {0, other_curves_num};
441 dst_raw_offsets = {initial_curves_num, initial_curves_num + other_curves_num};
443 OffsetIndices<int> other_curve_offsets{other_raw_offsets};
444 OffsetIndices<int> dst_curve_offsets{dst_raw_offsets};
453 dst.attributes_for_write());
466 using namespace bke::greasepencil;
473 if (!grease_pencil.has_active_layer()) {
478 const ActiveLayerBehavior active_layer_behavior =
static_cast<ActiveLayerBehavior
>(
480 const Layer &active_layer = *grease_pencil.get_active_layer();
482 Drawing *dst_drawing = grease_pencil.get_editable_drawing_at(active_layer, scene->
r.
cfra);
483 if (dst_drawing ==
nullptr) {
487 IndexMaskMemory memory;
492 *
object, editable_drawings, selected_points_count, memory);
493 if (ranges_selected.
size() <= 1) {
500 bke::CurvesGeometry tmp_curves(selected_points_count, 1);
502 const PointsRange working_range = copy_point_attributes(
503 ranges_selected, tmp_curves, *dst_drawing);
504 copy_curve_attributes(ranges_selected, tmp_curves, *dst_drawing);
506 clear_selection_attribute(ranges_selected);
508 Array<PointsRange> working_range_collection = {working_range};
509 clear_selection_attribute(working_range_collection);
512 if (active_layer_behavior == ActiveLayerBehavior::JoinSelection) {
513 remove_selected_points(ranges_selected);
516 append_strokes_from(std::move(tmp_curves), dst_curves);
529 dst_curves.update_curve_types();
530 dst_curves.tag_topology_changed();
539void GREASE_PENCIL_OT_join_selection(wmOperatorType *
ot)
541 static const EnumPropertyItem active_layer_behavior[] = {
542 {int(ActiveLayerBehavior::JoinAndCopySelection),
546 "Copy the selection in the new stroke"},
547 {int(ActiveLayerBehavior::JoinSelection),
551 "Move the selection to the new stroke"},
552 {0,
nullptr, 0,
nullptr,
nullptr},
556 ot->
name =
"Join Selection";
557 ot->
idname =
"GREASE_PENCIL_OT_join_selection";
562 ot->
exec = grease_pencil_join_selection_exec;
570 active_layer_behavior,
571 int(ActiveLayerBehavior::JoinSelection),
573 "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 grease pencil.
void BKE_report(ReportList *reports, eReportType type, const char *message)
void DEG_id_tag_update(ID *id, unsigned int flags)
struct GreasePencil GreasePencil
struct wmOperator wmOperator
BMesh const char void * data
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 IndexRange index_range() 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)
static IndexMask from_union(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
void ED_operatortypes_grease_pencil_join()
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)
void fill_selection_false(GMutableSpan selection)
void fill_selection_true(GMutableSpan selection)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
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)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< float, 3 > float3
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
wmOperatorStatus(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
wmOperatorStatus(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
struct ReportList * reports
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)