WIP: GEO: Copy curve selection #117869

Draft
Iliya Katushenock wants to merge 4 commits from mod_moder/blender:copy_selected_curve_points into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 132 additions and 92 deletions

View File

@ -38,6 +38,7 @@
#include "ED_object.hh"
#include "ED_screen.hh"
#include "GEO_curve_copy_selection.hh"
#include "GEO_join_geometries.hh"
#include "GEO_reorder.hh"
#include "GEO_smooth_curves.hh"
@ -347,96 +348,6 @@ static void GREASE_PENCIL_OT_stroke_simplify(wmOperatorType *ot)
/** \name Delete Operator
* \{ */
static bke::CurvesGeometry remove_points_and_split(const bke::CurvesGeometry &curves,
const IndexMask &mask)
{
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> src_cyclic = curves.cyclic();
Array<bool> points_to_delete(curves.points_num());
mask.to_bools(points_to_delete.as_mutable_span());
const int total_points = points_to_delete.as_span().count(false);
/* Return if deleting everything. */
if (total_points == 0) {
return {};
}
int curr_dst_point_id = 0;
Array<int> dst_to_src_point(total_points);
Vector<int> dst_curve_counts;
Vector<int> dst_to_src_curve;
Vector<bool> dst_cyclic;
for (const int curve_i : curves.curves_range()) {
const IndexRange points = points_by_curve[curve_i];
const Span<bool> curve_points_to_delete = points_to_delete.as_span().slice(points);
const bool curve_cyclic = src_cyclic[curve_i];
/* Note, these ranges start at zero and needed to be shifted by `points.first()` */
const Vector<IndexRange> ranges_to_keep = array_utils::find_all_ranges(curve_points_to_delete,
false);
if (ranges_to_keep.is_empty()) {
continue;
}
const bool is_last_segment_selected = curve_cyclic && ranges_to_keep.first().first() == 0 &&
ranges_to_keep.last().last() == points.size() - 1;
const bool is_curve_self_joined = is_last_segment_selected && ranges_to_keep.size() != 1;
const bool is_cyclic = ranges_to_keep.size() == 1 && is_last_segment_selected;
IndexRange range_ids = ranges_to_keep.index_range();
/* Skip the first range because it is joined to the end of the last range. */
for (const int range_i : ranges_to_keep.index_range().drop_front(is_curve_self_joined)) {
const IndexRange range = ranges_to_keep[range_i];
int count = range.size();
for (const int src_point : range.shift(points.first())) {
dst_to_src_point[curr_dst_point_id++] = src_point;
}
/* Join the first range to the end of the last range. */
if (is_curve_self_joined && range_i == range_ids.last()) {
const IndexRange first_range = ranges_to_keep[range_ids.first()];
for (const int src_point : first_range.shift(points.first())) {
dst_to_src_point[curr_dst_point_id++] = src_point;
}
count += first_range.size();
}
dst_curve_counts.append(count);
dst_to_src_curve.append(curve_i);
dst_cyclic.append(is_cyclic);
}
}
const int total_curves = dst_to_src_curve.size();
bke::CurvesGeometry dst_curves(total_points, total_curves);
MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
array_utils::copy(dst_curve_counts.as_span(), new_curve_offsets.drop_back(1));
offset_indices::accumulate_counts_to_offsets(new_curve_offsets);
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
const bke::AttributeAccessor src_attributes = curves.attributes();
/* Transfer curve attributes. */
gather_attributes(
src_attributes, bke::AttrDomain::Curve, {}, {"cyclic"}, dst_to_src_curve, dst_attributes);
array_utils::copy(dst_cyclic.as_span(), dst_curves.cyclic_for_write());
/* Transfer point attributes. */
gather_attributes(
src_attributes, bke::AttrDomain::Point, {}, {}, dst_to_src_point, dst_attributes);
dst_curves.update_curve_types();
dst_curves.remove_attributes_based_on_types();
return dst_curves;
}
static int grease_pencil_delete_exec(bContext *C, wmOperator * /*op*/)
{
const Scene *scene = CTX_data_scene(C);
@ -461,7 +372,8 @@ static int grease_pencil_delete_exec(bContext *C, wmOperator * /*op*/)
curves.remove_curves(elements, {});
}
else if (selection_domain == bke::AttrDomain::Point) {
curves = remove_points_and_split(curves, elements);
const IndexMask inverted = elements.complement(IndexRange(curves.points_num()), memory);
curves = geometry::copy_curve_points(curves, inverted, {});
}
info.drawing.tag_topology_changed();
changed = true;
@ -1926,7 +1838,10 @@ static bool grease_pencil_separate_selected(bContext &C,
info.frame_number);
drawing_dst.strokes_for_write() = bke::curves_copy_point_selection(
curves_src, selected_points, {});
curves_src = remove_points_and_split(curves_src, selected_points);
const IndexMask inverted = selected_points.complement(IndexRange(curves_src.points_num()),
memory);
curves_src = geometry::copy_curve_points(curves_src, inverted, {});
info.drawing.tag_topology_changed();
drawing_dst.tag_topology_changed();

View File

@ -17,6 +17,7 @@ set(INC_SYS
set(SRC
intern/add_curves_on_mesh.cc
intern/curve_constraints.cc
intern/curve_copy_selection.cc
intern/fillet_curves.cc
intern/join_geometries.cc
intern/mesh_copy_selection.cc
@ -49,6 +50,7 @@ set(SRC
GEO_add_curves_on_mesh.hh
GEO_curve_constraints.hh
GEO_curve_copy_selection.hh
GEO_fillet_curves.hh
GEO_join_geometries.hh
GEO_mesh_copy_selection.hh

View File

@ -0,0 +1,20 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_anonymous_attribute_id.hh"
#include "BLI_index_mask.hh"
#include "BKE_curves.hh"
namespace blender::geometry {
bke::CurvesGeometry copy_curve_points(
const bke::CurvesGeometry &src_curves,
const IndexMask &points_mask,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -0,0 +1,103 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <iostream>
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_attribute.hh"
#include "BLI_index_mask.hh"
#include "BKE_curves.hh"
namespace blender::geometry {
static void split_mask_for_ranges(const OffsetIndices<int> groups,
const IndexMask mask,
MutableSpan<IndexMask> masks)
{
threading::parallel_for(groups.index_range(), 2048, [&](const IndexRange range) {
const IndexMask local_mask = mask.slice_content(groups[range]);
if (local_mask.is_empty()) {
return;
}
for (const int64_t i : range) {
masks[i] = local_mask.slice_content(groups[i]);
}
});
}
static void count_ranges(const Span<IndexMask> masks, MutableSpan<int> ranges)
{
threading::parallel_for(masks.index_range(), 2048, [&](const IndexRange range) {
for (const int64_t i : range) {
masks[i].foreach_range([&](const IndexRange /*range*/) { ranges[i]++; });
}
});
}
static void fill_groups_by_indices(const OffsetIndices<int> groups, MutableSpan<int> indices)
{
threading::parallel_for(groups.index_range(), 2048, [&](const IndexRange range) {
for (const int64_t i : range) {
indices.slice(groups[i]).fill(i);
}
});
}
static void ranges_to_sizes(const IndexMask &mask, MutableSpan<int> r_offsets)
{
int offset_i = 0;
mask.foreach_range([&](const IndexRange range) {
r_offsets[offset_i] = range.size();
offset_i++;
});
}
bke::CurvesGeometry copy_curve_points(
const bke::CurvesGeometry &src_curves,
const IndexMask &points_mask,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const OffsetIndices<int> points_by_curve = src_curves.points_by_curve();
IndexMaskMemory memory;
Array<IndexMask> curves_masks(points_by_curve.size());
split_mask_for_ranges(points_by_curve, points_mask, curves_masks);
Array<int> curves_subdivision(points_by_curve.size() + 1, 0);
count_ranges(curves_masks, curves_subdivision);
const OffsetIndices<int> curves_subdivision_offset =
offset_indices::accumulate_counts_to_offsets(curves_subdivision);
const int64_t total_curves = curves_subdivision_offset.total_size();
bke::CurvesGeometry dst_curves(points_mask.size(), total_curves);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
threading::parallel_for(points_by_curve.index_range(), 2048, [&](const IndexRange range) {
for (const int64_t i : range) {
ranges_to_sizes(curves_masks[i], dst_offsets.slice(curves_subdivision_offset[i]));
}
});
offset_indices::accumulate_counts_to_offsets(dst_offsets);
Array<int> curve_indices(total_curves);
fill_groups_by_indices(curves_subdivision_offset, curve_indices);
const bke::AttributeAccessor src_attributes = src_curves.attributes();
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
bke::gather_attributes(
src_attributes, bke::AttrDomain::Point, propagation_info, {}, points_mask, dst_attributes);
bke::gather_attributes(src_attributes,
bke::AttrDomain::Curve,
propagation_info,
{},
curve_indices.as_span(),
dst_attributes);
dst_curves.update_curve_types();
return dst_curves;
}
} // namespace blender::geometry