diff options
author | Hans Goudey <h.goudey@me.com> | 2022-06-09 14:09:04 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-06-09 14:09:04 +0300 |
commit | 8a6cbcf386a1833d2b76aa0afc8abf830493f190 (patch) | |
tree | f018cf71877160301d2b3c4604719af2fa3fbac1 /source/blender/blenkernel | |
parent | 0fddff027ed025c27c818ff68315b8285e5c1a69 (diff) |
Curves: Port delete geometry node to the new curves type
Add a method to remove points from the new curves type, just like
the existing curve removal function. No functional changes are expected.
The code is simpler because all data is just stored as attributes, but
also different because the point data for all curves is stored in the same
arrays.
Similar performance improvements as other commits in T95443 are
expected, expecially for cases where there are many small curves.
Differential Revision: https://developer.blender.org/D15130
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_curves.hh | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curves_geometry.cc | 191 |
2 files changed, 176 insertions, 16 deletions
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 28d03f96db1..2bebd3ff97d 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -387,6 +387,7 @@ class CurvesGeometry : public ::CurvesGeometry { void update_customdata_pointers(); + void remove_points(IndexMask points_to_delete); void remove_curves(IndexMask curves_to_delete); /** diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 3c060bd1b8f..1c3715aaf69 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1100,6 +1100,165 @@ static void *ensure_customdata_layer(CustomData &custom_data, &custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str()); } +static void copy_between_buffers(const CPPType &type, + const void *src_buffer, + void *dst_buffer, + const IndexRange src_range, + const IndexRange dst_range) +{ + BLI_assert(src_range.size() == dst_range.size()); + type.copy_construct_n(POINTER_OFFSET(src_buffer, type.size() * src_range.start()), + POINTER_OFFSET(dst_buffer, type.size() * dst_range.start()), + src_range.size()); +} + +template<typename T> +static void copy_with_map(const Span<T> src, const Span<int> map, MutableSpan<T> dst) +{ + threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + dst[i] = src[map[i]]; + } + }); +} + +static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_with_map(src.typed<T>(), map, dst.typed<T>()); + }); +} + +/** + * Builds an array that for every point, contains the corresponding curve index. + */ +static Array<int> build_point_to_curve_map(const CurvesGeometry &curves) +{ + Array<int> point_to_curve_map(curves.points_num()); + threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange curves_range) { + for (const int i_curve : curves_range) { + point_to_curve_map.as_mutable_span().slice(curves.points_for_curve(i_curve)).fill(i_curve); + } + }); + return point_to_curve_map; +} + +static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, + const IndexMask points_to_delete) +{ + /* Use a map from points to curves to facilitate using an #IndexMask input. */ + const Array<int> point_to_curve_map = build_point_to_curve_map(curves); + + const Vector<IndexRange> copy_point_ranges = points_to_delete.extract_ranges_invert( + curves.points_range()); + + /* For every range of points to copy, find the offset in the result curves point layers. */ + int new_point_count = 0; + Array<int> copy_point_range_dst_offsets(copy_point_ranges.size()); + for (const int i : copy_point_ranges.index_range()) { + copy_point_range_dst_offsets[i] = new_point_count; + new_point_count += copy_point_ranges[i].size(); + } + BLI_assert(new_point_count == (curves.points_num() - points_to_delete.size())); + + /* Find out how many non-deleted points there are in every curve. */ + Array<int> curve_point_counts(curves.curves_num(), 0); + for (const IndexRange range : copy_point_ranges) { + for (const int point_i : range) { + curve_point_counts[point_to_curve_map[point_i]]++; + } + } + + /* Build the offsets for the new curve points, skipping curves that had all points deleted. + * Also store the original indices of the corresponding input curves, to facilitate parallel + * copying of curve domain data. */ + int new_curve_count = 0; + int curve_point_offset = 0; + Vector<int> new_curve_offsets; + Vector<int> new_curve_orig_indices; + new_curve_offsets.append(0); + for (const int i : curve_point_counts.index_range()) { + if (curve_point_counts[i] > 0) { + curve_point_offset += curve_point_counts[i]; + new_curve_offsets.append(curve_point_offset); + + new_curve_count++; + new_curve_orig_indices.append(i); + } + } + + CurvesGeometry new_curves{new_point_count, new_curve_count}; + + threading::parallel_invoke( + /* Initialize curve offsets. */ + [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); }, + /* Copy over point attributes. */ + [&]() { + const CustomData &old_point_data = curves.point_data; + CustomData &new_point_data = new_curves.point_data; + for (const int layer_i : IndexRange(old_point_data.totlayer)) { + const CustomDataLayer &old_layer = old_point_data.layers[layer_i]; + const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + void *dst_buffer = ensure_customdata_layer( + new_point_data, old_layer.name, data_type, new_point_count); + + threading::parallel_for( + copy_point_ranges.index_range(), 128, [&](const IndexRange ranges_range) { + for (const int range_i : ranges_range) { + const IndexRange src_range = copy_point_ranges[range_i]; + copy_between_buffers(type, + old_layer.data, + dst_buffer, + src_range, + {copy_point_range_dst_offsets[range_i], src_range.size()}); + } + }); + } + }, + /* Copy over curve attributes. + * In some cases points are just dissolved, so the the number of + * curves will be the same. That could be optimized in the future. */ + [&]() { + const CustomData &old_curve_data = curves.curve_data; + CustomData &new_curve_data = new_curves.curve_data; + for (const int layer_i : IndexRange(old_curve_data.totlayer)) { + const CustomDataLayer &old_layer = old_curve_data.layers[layer_i]; + const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + void *dst_buffer = ensure_customdata_layer( + new_curve_data, old_layer.name, data_type, new_curve_count); + + if (new_curves.curves_num() == curves.curves_num()) { + type.copy_construct_n(old_layer.data, dst_buffer, new_curves.curves_num()); + } + else { + copy_with_map({type, old_layer.data, curves.curves_num()}, + new_curve_orig_indices, + {type, dst_buffer, new_curves.curves_num()}); + } + } + }); + + new_curves.update_curve_types(); + + return new_curves; +} + +void CurvesGeometry::remove_points(const IndexMask points_to_delete) +{ + if (points_to_delete.is_empty()) { + return; + } + if (points_to_delete.size() == this->points_num()) { + *this = {}; + } + *this = copy_with_removed_points(*this, points_to_delete); +} + static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, const IndexMask curves_to_delete) { @@ -1159,20 +1318,17 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - const void *src_buffer = old_layer.data; void *dst_buffer = ensure_customdata_layer( new_point_data, old_layer.name, data_type, new_tot_points); threading::parallel_for( old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { for (const int range_i : ranges_range) { - const IndexRange old_point_range = old_point_ranges[range_i]; - const IndexRange new_point_range = new_point_ranges[range_i]; - - type.copy_construct_n( - POINTER_OFFSET(src_buffer, type.size() * old_point_range.start()), - POINTER_OFFSET(dst_buffer, type.size() * new_point_range.start()), - old_point_range.size()); + copy_between_buffers(type, + old_layer.data, + dst_buffer, + old_point_ranges[range_i], + new_point_ranges[range_i]); } }); } @@ -1186,20 +1342,17 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type); const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - const void *src_buffer = old_layer.data; void *dst_buffer = ensure_customdata_layer( new_curve_data, old_layer.name, data_type, new_tot_curves); threading::parallel_for( old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { for (const int range_i : ranges_range) { - const IndexRange old_curve_range = old_curve_ranges[range_i]; - const IndexRange new_curve_range = new_curve_ranges[range_i]; - - type.copy_construct_n( - POINTER_OFFSET(src_buffer, type.size() * old_curve_range.start()), - POINTER_OFFSET(dst_buffer, type.size() * new_curve_range.start()), - old_curve_range.size()); + copy_between_buffers(type, + old_layer.data, + dst_buffer, + old_curve_ranges[range_i], + new_curve_ranges[range_i]); } }); } @@ -1212,6 +1365,12 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, void CurvesGeometry::remove_curves(const IndexMask curves_to_delete) { + if (curves_to_delete.is_empty()) { + return; + } + if (curves_to_delete.size() == this->curves_num()) { + *this = {}; + } *this = copy_with_removed_curves(*this, curves_to_delete); } |