Curves: add OffsetIndices abstraction

This changes how we access the points that correspond to each curve in a `CurvesGeometry`.
Previously, `CurvesGeometry::points_for_curve(int curve_index) -> IndexRange`
was called for every curve in many loops. Now one has to call
`CurvesGeometry::points_by_curve() -> OffsetIndices` before the
loop and use the returned value inside the loop.

While this is a little bit more verbose in general, it has some benefits:
* Better standardization of how "offset indices" are used. The new data
  structure can be used independent of curves.
* Allows for better data oriented design. Generally, we want to retrieve
  all the arrays we need for a loop first and then do the processing.
  Accessing the old `CurvesGeometry::points_for_curve(...)` did not follow
  that design because it hid the underlying offset array.
* Makes it easier to pass the offsets to a function without having to
  pass the entire `CurvesGeometry`.
* Can improve performance in theory due to one less memory access
  because `this` does not have to be dereferenced every time.
  This likely doesn't have a noticable impact in practice.

Differential Revision: https://developer.blender.org/D17025
This commit is contained in:
Jacques Lucke 2023-01-18 11:52:27 +01:00
parent c1d360f7fb
commit 2c2178549b
43 changed files with 399 additions and 220 deletions

View File

@ -18,6 +18,7 @@
#include "BLI_generic_virtual_array.hh"
#include "BLI_index_mask.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.hh"
#include "BLI_shared_cache.hh"
#include "BLI_span.hh"
#include "BLI_task.hh"
@ -164,23 +165,17 @@ class CurvesGeometry : public ::CurvesGeometry {
IndexRange points_range() const;
IndexRange curves_range() const;
/**
* Number of control points in the indexed curve.
*/
int points_num_for_curve(const int index) const;
/**
* The index of the first point in every curve. The size of this span is one larger than the
* number of curves. Consider using #points_for_curve rather than using the offsets directly.
* number of curves. Consider using #points_by_curve rather than using the offsets directly.
*/
Span<int> offsets() const;
MutableSpan<int> offsets_for_write();
/**
* Access a range of indices of point data for a specific curve.
* The offsets of every curve into arrays on the points domain.
*/
IndexRange points_for_curve(int index) const;
IndexRange points_for_curves(IndexRange curves) const;
OffsetIndices<int> points_by_curve() const;
/** The type (#CurveType) of each curve, or potentially a single if all are the same type. */
VArray<int8_t> curve_types() const;
@ -852,16 +847,6 @@ inline IndexRange CurvesGeometry::curves_range() const
return IndexRange(this->curves_num());
}
inline int CurvesGeometry::points_num_for_curve(const int index) const
{
BLI_assert(this->curve_num > 0);
BLI_assert(this->curve_num > index);
BLI_assert(this->curve_offsets != nullptr);
const int offset = this->curve_offsets[index];
const int offset_next = this->curve_offsets[index + 1];
return offset_next - offset;
}
inline bool CurvesGeometry::is_single_type(const CurveType type) const
{
return this->curve_type_counts()[type] == this->curves_num();
@ -884,25 +869,9 @@ inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts
return this->runtime->type_counts;
}
inline IndexRange CurvesGeometry::points_for_curve(const int index) const
inline OffsetIndices<int> CurvesGeometry::points_by_curve() const
{
/* Offsets are not allocated when there are no curves. */
BLI_assert(this->curve_num > 0);
BLI_assert(this->curve_num > index);
BLI_assert(this->curve_offsets != nullptr);
const int offset = this->curve_offsets[index];
const int offset_next = this->curve_offsets[index + 1];
return {offset, offset_next - offset};
}
inline IndexRange CurvesGeometry::points_for_curves(const IndexRange curves) const
{
/* Offsets are not allocated when there are no curves. */
BLI_assert(this->curve_num > 0);
BLI_assert(this->curve_offsets != nullptr);
const int offset = this->curve_offsets[curves.start()];
const int offset_next = this->curve_offsets[curves.one_after_last()];
return {offset, offset_next - offset};
return OffsetIndices<int>({this->curve_offsets, this->curve_num + 1});
}
inline int CurvesGeometry::evaluated_points_num() const
@ -928,7 +897,8 @@ inline IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange c
inline Span<int> CurvesGeometry::bezier_evaluated_offsets_for_curve(const int curve_index) const
{
const IndexRange points = this->points_for_curve(curve_index);
const OffsetIndices points_by_curve = this->points_by_curve();
const IndexRange points = points_by_curve[curve_index];
return this->runtime->bezier_evaluated_offsets.as_span().slice(points);
}

View File

@ -532,11 +532,6 @@ void fill_curve_counts(const bke::CurvesGeometry &curves,
Span<IndexRange> curve_ranges,
MutableSpan<int> counts);
/**
* Turn an array of sizes into the offset at each index including all previous sizes.
*/
void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0);
IndexMask indices_for_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
const CurveType type,

View File

@ -108,6 +108,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
return curves_id;
}
const OffsetIndices points_by_curve = curves.points_by_curve();
MutableSpan<float3> positions = curves.positions_for_write();
SpanAttributeWriter<float> radius_attribute =
curves_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
@ -119,7 +120,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span<BPoint> src_points(src_curve.bp, src_curve.pntsu);
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int i : src_points.index_range()) {
const BPoint &bp = src_points[i];
@ -146,7 +147,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span<BezTriple> src_points(src_curve.bezt, src_curve.pntsu);
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
resolutions[curve_i] = src_curve.resolu;
@ -174,7 +175,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span src_points(src_curve.bp, src_curve.pntsu);
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
resolutions[curve_i] = src_curve.resolu;
nurbs_orders[curve_i] = src_curve.orderu;

View File

@ -633,10 +633,11 @@ static void write_sharp_bezier_edges(const CurvesInfo &curves_info,
sharp_edges = mesh_attributes.lookup_or_add_for_write_span<bool>("sharp_edge", ATTR_DOMAIN_EDGE);
const OffsetIndices profile_points_by_curve = profile.points_by_curve();
const VArray<int8_t> types = profile.curve_types();
foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
if (types[info.i_profile] == CURVE_TYPE_BEZIER) {
const IndexRange points = profile.points_for_curve(info.i_profile);
const IndexRange points = profile_points_by_curve[info.i_profile];
mark_bezier_vector_edges_sharp(points.size(),
info.main_segment_num,
profile.bezier_evaluated_offsets_for_curve(info.i_profile),

View File

@ -457,6 +457,7 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves,
MutableSpan<int> offsets,
MutableSpan<int> bezier_evaluated_offsets)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
VArray<int8_t> types = curves.curve_types();
VArray<int> resolution = curves.resolution();
VArray<bool> cyclic = curves.cyclic();
@ -468,7 +469,7 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves,
VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
build_offsets(offsets, [&](const int curve_index) -> int {
const IndexRange points = curves.points_for_curve(curve_index);
const IndexRange points = points_by_curve[curve_index];
switch (types[curve_index]) {
case CURVE_TYPE_CATMULL_ROM:
return curves::catmull_rom::calculate_evaluated_num(
@ -533,9 +534,10 @@ IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
Array<int> CurvesGeometry::point_to_curve_map() const
{
const OffsetIndices points_by_curve = this->points_by_curve();
Array<int> map(this->points_num());
for (const int i : this->curves_range()) {
map.as_mutable_span().slice(this->points_for_curve(i)).fill(i);
map.as_mutable_span().slice(points_by_curve[i]).fill(i);
}
return map;
}
@ -552,13 +554,14 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
this->runtime->nurbs_basis_cache.resize(this->curves_num());
MutableSpan<curves::nurbs::BasisCache> basis_caches(this->runtime->nurbs_basis_cache);
const OffsetIndices points_by_curve = this->points_by_curve();
VArray<bool> cyclic = this->cyclic();
VArray<int8_t> orders = this->nurbs_orders();
VArray<int8_t> knots_modes = this->nurbs_knots_modes();
threading::parallel_for(nurbs_mask.index_range(), 64, [&](const IndexRange range) {
for (const int curve_index : nurbs_mask.slice(range)) {
const IndexRange points = this->points_for_curve(curve_index);
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const int8_t order = orders[curve_index];
@ -597,6 +600,7 @@ Span<float3> CurvesGeometry::evaluated_positions() const
MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache;
this->runtime->evaluated_positions_span = evaluated_positions;
const OffsetIndices points_by_curve = this->points_by_curve();
VArray<int8_t> types = this->curve_types();
VArray<bool> cyclic = this->cyclic();
VArray<int> resolution = this->resolution();
@ -613,7 +617,7 @@ Span<float3> CurvesGeometry::evaluated_positions() const
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
for (const int curve_index : curves_range) {
const IndexRange points = this->points_for_curve(curve_index);
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
switch (types[curve_index]) {
@ -677,6 +681,7 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
Vector<int64_t> bezier_indices;
const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, bezier_indices);
if (!bezier_mask.is_empty()) {
const OffsetIndices points_by_curve = this->points_by_curve();
const Span<float3> positions = this->positions();
const Span<float3> handles_left = this->handle_positions_left();
const Span<float3> handles_right = this->handle_positions_right();
@ -686,7 +691,7 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
if (cyclic[curve_index]) {
continue;
}
const IndexRange points = this->points_for_curve(curve_index);
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const float epsilon = 1e-6f;
@ -753,6 +758,7 @@ static void evaluate_generic_data_for_curve(
Span<float3> CurvesGeometry::evaluated_normals() const
{
this->runtime->normal_cache_mutex.ensure([&]() {
const OffsetIndices points_by_curve = this->points_by_curve();
const VArray<int8_t> types = this->curve_types();
const VArray<bool> cyclic = this->cyclic();
const VArray<int8_t> normal_mode = this->normal_mode();
@ -792,7 +798,7 @@ Span<float3> CurvesGeometry::evaluated_normals() const
/* If the "tilt" attribute exists, rotate the normals around the tangents by the
* evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
if (use_tilt) {
const IndexRange points = this->points_for_curve(curve_index);
const IndexRange points = points_by_curve[curve_index];
if (types[curve_index] == CURVE_TYPE_POLY) {
rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
evaluated_tangents.slice(evaluated_points),
@ -828,7 +834,8 @@ void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
{
BLI_assert(this->runtime->offsets_cache_mutex.is_cached());
BLI_assert(this->runtime->nurbs_basis_cache_mutex.is_cached());
const IndexRange points = this->points_for_curve(curve_index);
const OffsetIndices points_by_curve = this->points_by_curve();
const IndexRange points = points_by_curve[curve_index];
BLI_assert(src.size() == points.size());
BLI_assert(dst.size() == this->evaluated_points_for_curve(curve_index).size());
evaluate_generic_data_for_curve(curve_index,
@ -848,6 +855,7 @@ void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst)
{
BLI_assert(this->runtime->offsets_cache_mutex.is_cached());
BLI_assert(this->runtime->nurbs_basis_cache_mutex.is_cached());
const OffsetIndices points_by_curve = this->points_by_curve();
const VArray<int8_t> types = this->curve_types();
const VArray<int> resolution = this->resolution();
const VArray<bool> cyclic = this->cyclic();
@ -856,7 +864,7 @@ void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst)
threading::parallel_for(this->curves_range(), 512, [&](IndexRange curves_range) {
for (const int curve_index : curves_range) {
const IndexRange points = this->points_for_curve(curve_index);
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
evaluate_generic_data_for_curve(curve_index,
points,
@ -973,6 +981,7 @@ void CurvesGeometry::calculate_bezier_auto_handles()
if (this->handle_positions_left().is_empty() || this->handle_positions_right().is_empty()) {
return;
}
const OffsetIndices points_by_curve = this->points_by_curve();
const VArray<int8_t> types = this->curve_types();
const VArray<bool> cyclic = this->cyclic();
const VArraySpan<int8_t> types_left{this->handle_types_left()};
@ -984,7 +993,7 @@ void CurvesGeometry::calculate_bezier_auto_handles()
threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
for (const int i_curve : range) {
if (types[i_curve] == CURVE_TYPE_BEZIER) {
const IndexRange points = this->points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
curves::bezier::calculate_auto_handles(cyclic[i_curve],
types_left.slice(points),
types_right.slice(points),
@ -1071,10 +1080,11 @@ static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst
*/
static Array<int> build_point_to_curve_map(const CurvesGeometry &curves)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
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);
point_to_curve_map.as_mutable_span().slice(points_by_curve[i_curve]).fill(i_curve);
}
});
return point_to_curve_map;
@ -1202,6 +1212,7 @@ static CurvesGeometry copy_with_removed_curves(
const IndexMask curves_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
const OffsetIndices old_points_by_curve = curves.points_by_curve();
const Span<int> old_offsets = curves.offsets();
const Vector<IndexRange> old_curve_ranges = curves_to_delete.extract_ranges_invert(
curves.curves_range(), nullptr);
@ -1214,7 +1225,7 @@ static CurvesGeometry copy_with_removed_curves(
new_curve_ranges.append(IndexRange(new_tot_curves, curve_range.size()));
new_tot_curves += curve_range.size();
const IndexRange old_point_range = curves.points_for_curves(curve_range);
const IndexRange old_point_range = old_points_by_curve[curve_range];
old_point_ranges.append(old_point_range);
new_point_ranges.append(IndexRange(new_tot_points, old_point_range.size()));
new_tot_points += old_point_range.size();
@ -1319,9 +1330,10 @@ static void reverse_curve_point_data(const CurvesGeometry &curves,
const IndexMask curve_selection,
MutableSpan<T> data)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : curve_selection.slice(range)) {
data.slice(curves.points_for_curve(curve_i)).reverse();
data.slice(points_by_curve[curve_i]).reverse();
}
});
}
@ -1332,9 +1344,10 @@ static void reverse_swap_curve_point_data(const CurvesGeometry &curves,
MutableSpan<T> data_a,
MutableSpan<T> data_b)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : curve_selection.slice(range)) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
MutableSpan<T> a = data_a.slice(points);
MutableSpan<T> b = data_b.slice(points);
for (const int i : IndexRange(points.size() / 2)) {
@ -1440,9 +1453,10 @@ static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
{
attribute_math::DefaultMixer<T> mixer(r_values);
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
for (const int i_curve : range) {
for (const int i_point : curves.points_for_curve(i_curve)) {
for (const int i_point : points_by_curve[i_curve]) {
mixer.mix_in(i_curve, old_values[i_point]);
}
}
@ -1462,9 +1476,10 @@ void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
r_values.fill(true);
for (const int i_curve : IndexRange(curves.curves_num())) {
for (const int i_point : curves.points_for_curve(i_curve)) {
for (const int i_point : points_by_curve[i_curve]) {
if (!old_values[i_point]) {
r_values[i_curve] = false;
break;
@ -1500,8 +1515,9 @@ static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i_curve : IndexRange(curves.curves_num())) {
r_values.slice(curves.points_for_curve(i_curve)).fill(old_values[i_curve]);
r_values.slice(points_by_curve[i_curve]).fill(old_values[i_curve]);
}
}

View File

@ -14,39 +14,30 @@ void fill_curve_counts(const bke::CurvesGeometry &curves,
const Span<IndexRange> curve_ranges,
MutableSpan<int> counts)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) {
for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) {
threading::parallel_for(curves_range, 4096, [&](IndexRange range) {
for (const int i : range) {
counts[i] = curves.points_for_curve(i).size();
counts[i] = points_by_curve.size(i);
}
});
}
});
}
void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset)
{
int offset = start_offset;
for (const int i : counts_to_offsets.index_range().drop_back(1)) {
const int count = counts_to_offsets[i];
BLI_assert(count > 0);
counts_to_offsets[i] = offset;
offset += count;
}
counts_to_offsets.last() = offset;
}
void copy_point_data(const CurvesGeometry &src_curves,
const CurvesGeometry &dst_curves,
const Span<IndexRange> curve_ranges,
const GSpan src,
GMutableSpan dst)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
for (const IndexRange range : curve_ranges.slice(range)) {
const IndexRange src_points = src_curves.points_for_curves(range);
const IndexRange dst_points = dst_curves.points_for_curves(range);
const IndexRange src_points = src_points_by_curve[range];
const IndexRange dst_points = dst_points_by_curve[range];
/* The arrays might be large, so a threaded copy might make sense here too. */
dst.slice(dst_points).copy_from(src.slice(src_points));
}
@ -59,10 +50,12 @@ void copy_point_data(const CurvesGeometry &src_curves,
const GSpan src,
GMutableSpan dst)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(src_curve_selection.index_range(), 512, [&](IndexRange range) {
for (const int i : src_curve_selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
/* The arrays might be large, so a threaded copy might make sense here too. */
dst.slice(dst_points).copy_from(src.slice(src_points));
}
@ -76,10 +69,11 @@ void fill_points(const CurvesGeometry &curves,
{
BLI_assert(*value.type() == dst.type());
const CPPType &type = dst.type();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
for (const int i : curve_selection.slice(range)) {
const IndexRange points = curves.points_for_curve(i);
type.fill_assign_n(value.get(), dst.slice(curves.points_for_curve(i)).data(), points.size());
const IndexRange points = points_by_curve[i];
type.fill_assign_n(value.get(), dst.slice(points).data(), points.size());
}
});
}
@ -91,9 +85,10 @@ void fill_points(const CurvesGeometry &curves,
{
BLI_assert(*value.type() == dst.type());
const CPPType &type = dst.type();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
for (const IndexRange range : curve_ranges.slice(range)) {
const IndexRange points = curves.points_for_curves(range);
const IndexRange points = points_by_curve[range];
type.fill_assign_n(value.get(), dst.slice(points).data(), points.size());
}
});

View File

@ -143,6 +143,7 @@ namespace blender::bke {
static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolutions = curves.resolution();
const VArray<bool> curves_cyclic = curves.cyclic();
@ -158,7 +159,7 @@ static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves
Vector<float3> nurbs_tangents;
for (const int i_curve : range) {
const IndexRange points = curves.points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
const IndexRange evaluated_points = curves.evaluated_points_for_curve(i_curve);
MutableSpan<float3> curve_normals = results.as_mutable_span().slice(points);

View File

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_index_range.hh"
#include "BLI_span.hh"
namespace blender::offset_indices {
/**
* References an array of ascending indices. A pair of consecutive indices encode an index range.
* Another common way to store the same kind of data is to store the start and size of every range
* separately. Using offsets instead halves the memory consumption. The downside is that the
* array has to be one element longer than the total number of ranges. The extra element is
* necessary to be able to get the last index range without requiring an extra branch for the case.
*
* This class is a thin wrapper around such an array that makes it easy to retrieve the index range
* at a specific index.
*/
template<typename T> class OffsetIndices {
private:
static_assert(std::is_integral_v<T>);
Span<T> offsets_;
public:
OffsetIndices(const Span<T> offsets) : offsets_(offsets)
{
BLI_assert(std::is_sorted(offsets_.begin(), offsets_.end()));
}
T size(const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < offsets_.size() - 1);
const int64_t begin = offsets_[index];
const int64_t end = offsets_[index + 1];
const int64_t size = end - begin;
return size;
}
IndexRange operator[](const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < offsets_.size() - 1);
const int64_t begin = offsets_[index];
const int64_t end = offsets_[index + 1];
const int64_t size = end - begin;
return IndexRange(begin, size);
}
IndexRange operator[](const IndexRange indices) const
{
const int64_t begin = offsets_[indices.start()];
const int64_t end = offsets_[indices.one_after_last()];
const int64_t size = end - begin;
return IndexRange(begin, size);
}
};
/**
* Turn an array of sizes into the offset at each index including all previous sizes.
*/
void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0);
} // namespace blender::offset_indices
namespace blender {
using offset_indices::OffsetIndices;
}

View File

@ -117,6 +117,7 @@ set(SRC
intern/mesh_intersect.cc
intern/noise.c
intern/noise.cc
intern/offset_indices.cc
intern/path_util.c
intern/polyfill_2d.c
intern/polyfill_2d_beautify.c
@ -294,6 +295,7 @@ set(SRC
BLI_multi_value_map.hh
BLI_noise.h
BLI_noise.hh
BLI_offset_indices.hh
BLI_parameter_pack_utils.hh
BLI_path_util.h
BLI_polyfill_2d.h

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_offset_indices.hh"
namespace blender::offset_indices {
void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset)
{
int offset = start_offset;
for (const int i : counts_to_offsets.index_range().drop_back(1)) {
const int count = counts_to_offsets[i];
BLI_assert(count > 0);
counts_to_offsets[i] = offset;
offset += count;
}
counts_to_offsets.last() = offset;
}
} // namespace blender::offset_indices

View File

@ -238,11 +238,12 @@ static void curves_batch_cache_fill_segments_proc_pos(
using namespace blender;
/* TODO: use hair radius layer if available. */
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
const OffsetIndices points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange range) {
for (const int i_curve : range) {
const IndexRange points = curves.points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
Span<float3> curve_positions = positions.slice(points);
MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points);
@ -334,6 +335,8 @@ static void curves_batch_cache_ensure_edit_points_data(const Curves &curves_id,
GPU_vertbuf_init_with_format(cache.edit_points_data, &format_data);
GPU_vertbuf_data_alloc(cache.edit_points_data, curves.points_num());
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> selection = curves.attributes().lookup_or_default<bool>(
".selection", eAttrDomain(curves_id.selection_domain), true);
switch (curves_id.selection_domain) {
@ -346,7 +349,7 @@ static void curves_batch_cache_ensure_edit_points_data(const Curves &curves_id,
case ATTR_DOMAIN_CURVE:
for (const int curve_i : curves.curves_range()) {
const float curve_selection = selection[curve_i] ? 1.0f : 0.0f;
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points) {
GPU_vertbuf_attr_set(cache.edit_points_data, color, point_i, &curve_selection);
}
@ -367,8 +370,10 @@ static void curves_batch_cache_ensure_edit_lines(const Curves &curves_id, Curves
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
const OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i : curves.curves_range()) {
const IndexRange points = curves.points_for_curve(i);
const IndexRange points = points_by_curve[i];
for (const int i_point : points) {
GPU_indexbuf_add_generic_vert(&elb, i_point);
}
@ -460,9 +465,10 @@ static void curves_batch_cache_fill_strands_data(const Curves &curves_id,
{
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
curves_id.geometry);
const blender::OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i : IndexRange(curves.curves_num())) {
const IndexRange points = curves.points_for_curve(i);
const IndexRange points = points_by_curve[i];
*(uint *)GPU_vertbuf_raw_step(&data_step) = points.start();
*(ushort *)GPU_vertbuf_raw_step(&seg_step) = points.size() - 1;

View File

@ -334,7 +334,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
if (curves.curves_num() >= 1) {
blender::VArray<float> radii = curves.attributes().lookup_or_default(
"radius", ATTR_DOMAIN_POINT, 0.005f);
const blender::IndexRange first_curve_points = curves.points_for_curve(0);
const blender::IndexRange first_curve_points = curves.points_by_curve()[0];
const float first_radius = radii[first_curve_points.first()];
const float last_radius = radii[first_curve_points.last()];
const float middle_radius = radii[first_curve_points.size() / 2];

View File

@ -112,8 +112,9 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
RandomNumberGenerator rng;
const OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i : curves.curves_range()) {
const IndexRange points = curves.points_for_curve(i);
const IndexRange points = points_by_curve[i];
MutableSpan<float3> curve_positions = positions.slice(points);
MutableSpan<float> curve_radii = radius.span.slice(points);

View File

@ -286,11 +286,12 @@ static void try_convert_single_object(Object &curves_ob,
const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
const MFace *mfaces = (const MFace *)CustomData_get_layer(&surface_me.fdata, CD_MFACE);
const OffsetIndices points_by_curve = curves.points_by_curve();
const Span<float3> positions = surface_me.vert_positions();
for (const int new_hair_i : IndexRange(hair_num)) {
const int curve_i = new_hair_i;
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const float3 &root_pos_cu = positions_cu[points.first()];
const float3 root_pos_su = transforms.curves_to_surface * root_pos_cu;
@ -440,6 +441,7 @@ static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &p
const float4x4 world_to_object_mat = object_to_world_mat.inverted();
MutableSpan<float3> positions = curves.positions_for_write();
const OffsetIndices points_by_curve = curves.points_by_curve();
const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache,
const Span<int> indices_to_transfer,
@ -448,7 +450,7 @@ static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &p
for (const int i : range) {
const int hair_i = indices_to_transfer[i];
const int curve_i = i + curve_index_offset;
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()};
for (const int key_i : keys.index_range()) {
const float3 key_pos_wo = keys[key_i].co;
@ -554,6 +556,7 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
.typed<float2>();
}
const OffsetIndices points_by_curve = curves.points_by_curve();
MutableSpan<float3> positions_cu = curves.positions_for_write();
MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
@ -567,7 +570,7 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];
const float3 old_first_point_pos_su = transforms.curves_to_surface *
@ -625,7 +628,7 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const int first_point_i = points.first();
const float3 old_first_point_pos_cu = positions_cu[first_point_i];

View File

@ -32,9 +32,10 @@ static IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves,
if (selection.is_single()) {
return selection.get_internal_single() ? IndexMask(curves_range) : IndexMask();
}
const OffsetIndices points_by_curve = curves.points_by_curve();
return index_mask_ops::find_indices_based_on_predicate(
curves_range, 512, r_indices, [&](const int64_t curve_i) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
/* The curve is selected if any of its points are selected. */
Array<bool, 32> point_selection(points.size());
selection.materialize_compressed(points, point_selection);

View File

@ -91,6 +91,8 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
}
};
const OffsetIndices points_by_curve = curves.points_by_curve();
BrushPositionCandidate best_candidate = threading::parallel_reduce(
curves.curves_range(),
128,
@ -99,7 +101,7 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
BrushPositionCandidate best_candidate = init;
for (const int curve_i : curves_range) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (points.size() == 1) {
const float3 &pos_cu = positions[points.first()];

View File

@ -189,6 +189,7 @@ struct CombOperationExecutor {
MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write();
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
const OffsetIndices points_by_curve = curves_orig_->points_by_curve();
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
@ -200,7 +201,7 @@ struct CombOperationExecutor {
Vector<int> &local_changed_curves = r_changed_curves.local();
for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
const IndexRange points = curves_orig_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_front(1)) {
const float3 old_pos_cu = deformation.positions[point_i];
const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
@ -295,12 +296,13 @@ struct CombOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
const OffsetIndices points_by_curve = curves_orig_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
Vector<int> &local_changed_curves = r_changed_curves.local();
for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
const IndexRange points = curves_orig_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_front(1)) {
const float3 pos_old_cu = deformation.positions[point_i];
@ -359,10 +361,11 @@ struct CombOperationExecutor {
void initialize_segment_lengths()
{
const Span<float3> positions_cu = curves_orig_->positions();
const OffsetIndices points_by_curve = curves_orig_->points_by_curve();
self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num());
threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves_orig_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[point_i];
const float3 &p2_cu = positions_cu[point_i + 1];
@ -379,12 +382,13 @@ struct CombOperationExecutor {
void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves)
{
const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
const OffsetIndices points_by_curve = curves_orig_->points_by_curve();
MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) {
threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : changed_curves.as_span().slice(range)) {
const IndexRange points = curves_orig_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int segment_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[segment_i];
float3 &p2_cu = positions_cu[segment_i + 1];

View File

@ -140,10 +140,11 @@ struct DeleteOperationExecutor {
/* Remove deleted curves from the stored deformed positions. */
const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert(
curves_->curves_range());
const OffsetIndices points_by_curve = curves_->points_by_curve();
Vector<float3> new_deformed_positions;
for (const IndexRange curves_range : ranges_to_keep) {
new_deformed_positions.extend(
self_->deformed_positions_.as_span().slice(curves_->points_for_curves(curves_range)));
self_->deformed_positions_.as_span().slice(points_by_curve[curves_range]));
}
self_->deformed_positions_ = std::move(new_deformed_positions);
@ -172,10 +173,11 @@ struct DeleteOperationExecutor {
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (points.size() == 1) {
const float3 pos_cu = brush_transform_inv * self_->deformed_positions_[points.first()];
float2 pos_re;
@ -231,10 +233,11 @@ struct DeleteOperationExecutor {
{
const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (points.size() == 1) {
const float3 &pos_cu = self_->deformed_positions_[points.first()];

View File

@ -88,13 +88,14 @@ class ShrinkCurvesEffect : public CurvesEffect {
const Span<int> curve_indices,
const Span<float> move_distances_cu) override
{
const OffsetIndices points_by_curve = curves.points_by_curve();
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
ParameterizationBuffers data;
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
this->shrink_curve(positions_cu.slice(points), move_distance_cu, data);
}
});
@ -137,13 +138,14 @@ class ExtrapolateCurvesEffect : public CurvesEffect {
const Span<int> curve_indices,
const Span<float> move_distances_cu) override
{
const OffsetIndices points_by_curve = curves.points_by_curve();
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
MoveAndResampleBuffers resample_buffer;
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (points.size() <= 1) {
continue;
}
@ -178,12 +180,13 @@ class ScaleCurvesEffect : public CurvesEffect {
const Span<int> curve_indices,
const Span<float> move_distances_cu) override
{
const OffsetIndices points_by_curve = curves.points_by_curve();
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const float old_length = this->compute_poly_curve_length(positions_cu.slice(points));
const float length_diff = scale_up_ ? move_distance_cu : -move_distance_cu;
@ -342,6 +345,7 @@ struct CurvesEffectOperationExecutor {
{
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@ -360,7 +364,7 @@ struct CurvesEffectOperationExecutor {
Influences &local_influences = influences_for_thread.local();
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const float curve_selection_factor = curve_selection_factors_[curve_i];
@ -452,12 +456,13 @@ struct CurvesEffectOperationExecutor {
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
Influences &local_influences = influences_for_thread.local();
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
float max_move_distance_cu = 0.0f;

View File

@ -370,13 +370,14 @@ static int select_random_exec(bContext *C, wmOperator *op)
if (!was_anything_selected) {
selection.fill(1.0f);
}
const OffsetIndices points_by_curve = curves.points_by_curve();
switch (curves_id->selection_domain) {
case ATTR_DOMAIN_POINT: {
if (partial) {
if (constant_per_curve) {
for (const int curve_i : curves.curves_range()) {
const float random_value = next_partial_random_value();
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points) {
selection[point_i] *= random_value;
}
@ -393,7 +394,7 @@ static int select_random_exec(bContext *C, wmOperator *op)
if (constant_per_curve) {
for (const int curve_i : curves.curves_range()) {
const bool random_value = next_bool_random_value();
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (!random_value) {
selection.slice(points).fill(0.0f);
}
@ -547,6 +548,7 @@ static int select_end_exec(bContext *C, wmOperator *op)
if (!was_anything_selected) {
curves::fill_selection_true(selection.span);
}
const OffsetIndices points_by_curve = curves.points_by_curve();
selection.span.type().to_static_type_tag<bool, float>([&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_void_v<T>) {
@ -556,7 +558,7 @@ static int select_end_exec(bContext *C, wmOperator *op)
MutableSpan<T> selection_typed = selection.span.typed<T>();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (end_points) {
selection_typed.slice(points.drop_back(amount)).fill(T(0));
}
@ -667,6 +669,7 @@ static int select_grow_update(bContext *C, wmOperator *op, const float mouse_dif
const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x;
bke::SpanAttributeWriter<float> selection = float_selection_ensure(curves_id);
const OffsetIndices points_by_curve = curves.points_by_curve();
/* Grow or shrink selection based on precomputed distances. */
switch (selection.domain) {
@ -680,7 +683,7 @@ static int select_grow_update(bContext *C, wmOperator *op, const float mouse_dif
/* Propagate grown point selection to the curve selection. */
MutableSpan<float> curves_selection = selection.span;
for (const int curve_i : curves.curves_range()) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const Span<float> points_selection = new_points_selection.as_span().slice(points);
const float max_selection = *std::max_element(points_selection.begin(),
points_selection.end());

View File

@ -160,6 +160,7 @@ struct PinchOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@ -169,7 +170,7 @@ struct PinchOperationExecutor {
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_front(1)) {
const float3 old_pos_cu = deformation.positions[point_i];
const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
@ -236,10 +237,11 @@ struct PinchOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_front(1)) {
const float3 old_pos_cu = deformation.positions[point_i];
@ -269,10 +271,11 @@ struct PinchOperationExecutor {
void initialize_segment_lengths()
{
const Span<float3> positions_cu = curves_->positions();
const OffsetIndices points_by_curve = curves_->points_by_curve();
self_->segment_lengths_cu_.reinitialize(curves_->points_num());
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[point_i];
const float3 &p2_cu = positions_cu[point_i + 1];
@ -286,6 +289,7 @@ struct PinchOperationExecutor {
void restore_segment_lengths(const Span<bool> changed_curves)
{
const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
const OffsetIndices points_by_curve = curves_->points_by_curve();
MutableSpan<float3> positions_cu = curves_->positions_for_write();
threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
@ -293,7 +297,7 @@ struct PinchOperationExecutor {
if (!changed_curves[curve_i]) {
continue;
}
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int segment_i : IndexRange(points.size() - 1)) {
const float3 &p1_cu = positions_cu[points[segment_i]];
float3 &p2_cu = positions_cu[points[segment_i] + 1];

View File

@ -182,11 +182,12 @@ struct PuffOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_selection_i : range) {
const int curve_i = curve_selection_[curve_selection_i];
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]];
float2 prev_pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values);
@ -242,11 +243,12 @@ struct PuffOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_selection_i : range) {
const int curve_i = curve_selection_[curve_selection_i];
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_front(1)) {
const float3 &prev_pos_cu = deformation.positions[point_i - 1];
const float3 &pos_cu = deformation.positions[point_i];
@ -269,13 +271,14 @@ struct PuffOperationExecutor {
void puff(const Span<float> curve_weights)
{
BLI_assert(curve_weights.size() == curve_selection_.size());
const OffsetIndices points_by_curve = curves_->points_by_curve();
MutableSpan<float3> positions_cu = curves_->positions_for_write();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
Vector<float> accumulated_lengths_cu;
for (const int curve_selection_i : range) {
const int curve_i = curve_selection_[curve_selection_i];
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const int first_point_i = points[0];
const float3 first_pos_cu = positions_cu[first_point_i];
const float3 first_pos_su = transforms_.curves_to_surface * first_pos_cu;
@ -336,11 +339,12 @@ struct PuffOperationExecutor {
void initialize_segment_lengths()
{
const OffsetIndices points_by_curve = curves_->points_by_curve();
const Span<float3> positions_cu = curves_->positions();
self_->segment_lengths_cu_.reinitialize(curves_->points_num());
threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[point_i];
const float3 &p2_cu = positions_cu[point_i + 1];
@ -354,11 +358,12 @@ struct PuffOperationExecutor {
void restore_segment_lengths()
{
const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
const OffsetIndices points_by_curve = curves_->points_by_curve();
MutableSpan<float3> positions_cu = curves_->positions_for_write();
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int segment_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[segment_i];
float3 &p2_cu = positions_cu[segment_i + 1];

View File

@ -263,6 +263,7 @@ struct SelectionPaintOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@ -273,7 +274,7 @@ struct SelectionPaintOperationExecutor {
threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const float max_weight = threading::parallel_reduce(
curves_->points_for_curve(curve_i).drop_back(1),
points_by_curve[curve_i].drop_back(1),
1024,
0.0f,
[&](const IndexRange segment_range, const float init) {
@ -330,6 +331,7 @@ struct SelectionPaintOperationExecutor {
{
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
const float brush_radius_cu = self_->brush_3d_.radius_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
@ -337,7 +339,7 @@ struct SelectionPaintOperationExecutor {
threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) {
for (const int curve_i : curves_range) {
const float max_weight = threading::parallel_reduce(
curves_->points_for_curve(curve_i).drop_back(1),
points_by_curve[curve_i].drop_back(1),
1024,
0.0f,
[&](const IndexRange segment_range, const float init) {

View File

@ -317,6 +317,7 @@ struct SlideOperationExecutor {
const Span<float3> positions_orig_su = surface_orig_->vert_positions();
const Span<MLoop> loops_orig = surface_orig_->loops();
const OffsetIndices points_by_curve = curves_orig_->points_by_curve();
MutableSpan<float3> positions_orig_cu = curves_orig_->positions_for_write();
MutableSpan<float2> surface_uv_coords = curves_orig_->surface_uv_coords_for_write();
@ -334,7 +335,7 @@ struct SlideOperationExecutor {
threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) {
for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) {
const int curve_i = slide_curve_info.curve_i;
const IndexRange points = curves_orig_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const int first_point_i = points[0];
const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i];

View File

@ -138,10 +138,11 @@ struct SmoothOperationExecutor {
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points) {
const float3 &pos_cu = brush_transform_inv * deformation.positions[point_i];
float2 pos_re;
@ -194,10 +195,11 @@ struct SmoothOperationExecutor {
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points) {
const float3 &pos_cu = deformation.positions[point_i];
const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
@ -221,11 +223,12 @@ struct SmoothOperationExecutor {
void smooth(const Span<float> point_smooth_factors)
{
const OffsetIndices points_by_curve = curves_->points_by_curve();
MutableSpan<float3> positions = curves_->positions_for_write();
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
Vector<float3> old_positions;
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
old_positions.clear();
old_positions.extend(positions.slice(points));
for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) {

View File

@ -179,6 +179,7 @@ struct SnakeHookOperatorExecutor {
const float4x4 brush_transform_inv = brush_transform.inverted();
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
MutableSpan<float3> positions_cu = curves_->positions_for_write();
@ -191,7 +192,7 @@ struct SnakeHookOperatorExecutor {
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
MoveAndResampleBuffers resample_buffer;
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const int last_point_i = points.last();
const float3 old_pos_cu = deformation.positions[last_point_i];
const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
@ -264,6 +265,7 @@ struct SnakeHookOperatorExecutor {
{
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const OffsetIndices points_by_curve = curves_->points_by_curve();
MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
@ -272,7 +274,7 @@ struct SnakeHookOperatorExecutor {
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
MoveAndResampleBuffers resample_buffer;
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const int last_point_i = points.last();
const float3 old_pos_cu = deformation.positions[last_point_i];

View File

@ -117,11 +117,12 @@ static void interpolate_position_without_interpolation(
const float4x4 &surface_to_curves_normal_mat)
{
const int added_curves_num = root_positions_cu.size();
const OffsetIndices points_by_curve = curves.points_by_curve();
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
for (const int i : range) {
const int curve_i = old_curves_num + i;
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const float3 &root_cu = root_positions_cu[i];
const float length = new_lengths_cu[i];
const float3 &normal_su = new_normals_su[i];
@ -147,13 +148,14 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves,
MutableSpan<float3> positions_cu = curves.positions_for_write();
const int added_curves_num = root_positions_cu.size();
const OffsetIndices points_by_curve = curves.points_by_curve();
const Span<float2> uv_coords = curves.surface_uv_coords();
threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
for (const int added_curve_i : range) {
const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i];
const int curve_i = old_curves_num + added_curve_i;
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
const float length_cu = new_lengths_cu[added_curve_i];
const float3 &normal_su = new_normals_su[added_curve_i];
@ -188,7 +190,7 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves,
float normal_rotation_cu[3][3];
rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu);
const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i);
const IndexRange neighbor_points = points_by_curve[neighbor_curve_i];
const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]];
/* Sample the positions on neighbors and mix them into the final positions of the curve.
@ -289,7 +291,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
interpolate_from_neighbors<int>(
neighbors_per_curve,
inputs.fallback_point_count,
[&](const int curve_i) { return curves.points_for_curve(curve_i).size(); },
[&](const int curve_i) { return curve_offsets[curve_i + 1] - curve_offsets[curve_i]; },
new_point_counts_per_curve);
}
else {
@ -314,11 +316,12 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
/* Determine length of new curves. */
Array<float> new_lengths_cu(added_curves_num);
if (inputs.interpolate_length) {
const OffsetIndices points_by_curve = curves.points_by_curve();
interpolate_from_neighbors<float>(
neighbors_per_curve,
inputs.fallback_curve_length,
[&](const int curve_i) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
float length = 0.0f;
for (const int segment_i : points.drop_back(1)) {
const float3 &p1 = positions_cu[segment_i];

View File

@ -41,10 +41,12 @@ static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
const Span<T> src,
MutableSpan<T> dst)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
for (const int curve_i : curve_selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange dst_points = dst_points_by_curve[curve_i];
const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i));
threaded_slice_fill(src.slice(src_points), offsets, dst.slice(dst_points));
}
@ -76,9 +78,10 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
{
/* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets);
const OffsetIndices points_by_curve = src_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = points_by_curve[curve_i];
const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i);
MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range);
@ -103,12 +106,12 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
}
});
bke::curves::accumulate_counts_to_offsets(point_offsets);
offset_indices::accumulate_counts_to_offsets(point_offsets);
dst_curve_offsets[curve_i] = point_offsets.last();
}
});
bke::curves::accumulate_counts_to_offsets(dst_curve_offsets);
offset_indices::accumulate_counts_to_offsets(dst_curve_offsets);
}
static void calculate_directions(const Span<float3> positions, MutableSpan<float3> directions)
@ -450,6 +453,8 @@ static bke::CurvesGeometry fillet_curves(
dst_handles_r = dst_curves.handle_positions_right_for_write();
}
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
Array<float3> directions;
Array<float> angles;
@ -457,9 +462,9 @@ static bke::CurvesGeometry fillet_curves(
Array<float> input_radii_buffer;
for (const int curve_i : curve_selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i));
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_points_by_curve[curve_i];
const Span<float3> src_positions = positions.slice(src_points);
directions.reinitialize(src_points.size());

View File

@ -229,8 +229,9 @@ static void normalize_curve_point_data(const CurvesGeometry &curves,
const IndexMask curve_selection,
MutableSpan<float3> data)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i_curve : curve_selection) {
normalize_span(data.slice(curves.points_for_curve(i_curve)));
normalize_span(data.slice(points_by_curve[i_curve]));
}
}
@ -262,7 +263,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
/* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
bke::curves::accumulate_counts_to_offsets(dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
/* All resampled curves are poly curves. */
@ -286,6 +287,9 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
Array<int> sample_indices(dst_curves.points_num());
Array<float> sample_factors(dst_curves.points_num());
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
/* Use a "for each group of curves: for each attribute: for each curve" pattern to work on
* smaller sections of data that ideally fit into CPU cache better than simply one attribute at a
* time or one curve at a time. */
@ -297,7 +301,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
/* Gather uniform samples based on the accumulated lengths of the original curve. */
for (const int i_curve : sliced_selection) {
const bool cyclic = curves_cyclic[i_curve];
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_points_by_curve[i_curve];
const Span<float> lengths = src_curves.evaluated_lengths_for_curve(i_curve, cyclic);
if (lengths.is_empty()) {
/* Handle curves with only one evaluated point. */
@ -321,8 +325,8 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
for (const int i_curve : sliced_selection) {
const IndexRange src_points = src_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange src_points = src_points_by_curve[i_curve];
const IndexRange dst_points = dst_points_by_curve[i_curve];
if (curve_types[i_curve] == CURVE_TYPE_POLY) {
length_parameterize::interpolate(src.slice(src_points),
@ -349,7 +353,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
auto interpolate_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
for (const int i_curve : sliced_selection) {
const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_points_by_curve[i_curve];
length_parameterize::interpolate(src.slice(src_points),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
@ -372,7 +376,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
/* Fill the default value for non-interpolating attributes that still must be copied. */
for (GMutableSpan dst : attributes.dst_no_interpolation) {
for (const int i_curve : sliced_selection) {
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_points_by_curve[i_curve];
dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
}
}
@ -438,7 +442,7 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
}
});
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
bke::curves::accumulate_counts_to_offsets(dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
@ -449,6 +453,8 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
AttributesForInterpolation attributes;
gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
const IndexMask sliced_selection = selection.slice(selection_range);
@ -460,8 +466,8 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
for (const int i_curve : sliced_selection) {
const IndexRange src_points = src_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange src_points = src_points_by_curve[i_curve];
const IndexRange dst_points = dst_points_by_curve[i_curve];
src_curves.interpolate_to_evaluated(
i_curve, src.slice(src_points), dst.slice(dst_points));
}
@ -471,7 +477,7 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
auto copy_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
for (const int i_curve : sliced_selection) {
const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_points_by_curve[i_curve];
dst.slice(dst_points).copy_from(src.slice(src_points));
}
};
@ -491,7 +497,7 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
/* Fill the default value for non-interpolating attributes that still must be copied. */
for (GMutableSpan dst : attributes.dst_no_interpolation) {
for (const int i_curve : sliced_selection) {
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
const IndexRange dst_points = dst_points_by_curve[i_curve];
dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
}
}

View File

@ -279,9 +279,10 @@ static int to_nurbs_size(const CurveType src_type, const int src_size)
static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<int> sizes)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
sizes[i] = curves.points_for_curve(i).size();
sizes[i] = points_by_curve[i].size();
}
});
}
@ -307,7 +308,7 @@ static bke::CurvesGeometry convert_curves_to_bezier(
CurveType(src_types[i]), src_cyclic[i], KnotsMode(src_knot_modes[i]), dst_offsets[i]);
}
});
bke::curves::accumulate_counts_to_offsets(dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
const bke::AttributeAccessor src_attributes = src_curves.attributes();
@ -332,6 +333,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(
MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
MutableSpan<float> dst_weights = dst_curves.nurbs_weights_for_write();
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
auto catmull_rom_to_bezier = [&](IndexMask selection) {
bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
@ -339,8 +343,8 @@ static bke::CurvesGeometry convert_curves_to_bezier(
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
catmull_rom_to_bezier_handles(src_positions.slice(src_points),
src_cyclic[i],
dst_handles_l.slice(dst_points),
@ -392,8 +396,8 @@ static bke::CurvesGeometry convert_curves_to_bezier(
threading::parallel_for(selection.index_range(), 64, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
const Span<float3> src_curve_positions = src_positions.slice(src_points);
if (dst_points.size() == 1) {
const float3 &position = src_positions[src_points.first()];
@ -430,8 +434,8 @@ static bke::CurvesGeometry convert_curves_to_bezier(
for (bke::AttributeTransferData &attribute : generic_attributes) {
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
nurbs_to_bezier_assign(attribute.src.slice(src_points),
KnotsMode(src_knot_modes[i]),
attribute.dst.span.slice(dst_points));
@ -482,7 +486,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), dst_offsets[i]);
}
});
bke::curves::accumulate_counts_to_offsets(dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
const bke::AttributeAccessor src_attributes = src_curves.attributes();
@ -508,6 +512,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
}
};
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
auto catmull_rom_to_nurbs = [&](IndexMask selection) {
dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
@ -515,8 +522,8 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
catmull_rom_to_nurbs_positions(
src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
}
@ -525,8 +532,8 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
for (bke::AttributeTransferData &attribute : generic_attributes) {
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
bezier_generic_to_nurbs(attribute.src.slice(src_points),
attribute.dst.span.slice(dst_points));
}
@ -572,8 +579,8 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
bezier_positions_to_nurbs(src_positions.slice(src_points),
src_handles_l.slice(src_points),
src_handles_r.slice(src_points),
@ -584,8 +591,8 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
for (bke::AttributeTransferData &attribute : generic_attributes) {
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
const IndexRange src_points = src_points_by_curve[i];
const IndexRange dst_points = dst_points_by_curve[i];
bezier_generic_to_nurbs(attribute.src.slice(src_points),
attribute.dst.span.slice(dst_points));
}

View File

@ -31,9 +31,10 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
{
/* Fill the array with each curve's point count, then accumulate them to the offsets. */
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets);
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
MutableSpan<int> point_offsets = dst_point_offsets.slice(src_segments);
@ -54,11 +55,11 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
}
}
bke::curves::accumulate_counts_to_offsets(point_offsets);
offset_indices::accumulate_counts_to_offsets(point_offsets);
dst_curve_offsets[curve_i] = point_offsets.last();
}
});
bke::curves::accumulate_counts_to_offsets(dst_curve_offsets);
offset_indices::accumulate_counts_to_offsets(dst_curve_offsets);
}
template<typename T>
@ -79,13 +80,15 @@ static void subdivide_attribute_linear(const bke::CurvesGeometry &src_curves,
const Span<T> src,
MutableSpan<T> dst)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
for (const int curve_i : selection.slice(selection_range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
const Span<int> offsets = point_offsets.slice(src_segments);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_points_by_curve[curve_i];
const Span<T> curve_src = src.slice(src_points);
MutableSpan<T> curve_dst = dst.slice(dst_points);
@ -126,11 +129,13 @@ static void subdivide_attribute_catmull_rom(const bke::CurvesGeometry &src_curve
const Span<T> src,
MutableSpan<T> dst)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
for (const int curve_i : selection.slice(selection_range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_points_by_curve[curve_i];
bke::curves::catmull_rom::interpolate_to_evaluated(src.slice(src_points),
cyclic[curve_i],
@ -367,19 +372,21 @@ bke::CurvesGeometry subdivide_curves(
const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()};
const Span<float3> src_handles_l = src_curves.handle_positions_left();
const Span<float3> src_handles_r = src_curves.handle_positions_right();
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_points_by_curve[curve_i];
subdivide_bezier_positions(src_positions.slice(src_points),
src_types_l.slice(src_points),
src_types_r.slice(src_points),

View File

@ -191,6 +191,7 @@ static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask se
if (!dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
return;
}
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write();
MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write();
MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write();
@ -198,7 +199,7 @@ static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask se
threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange points = dst_curves.points_for_curve(curve_i);
const IndexRange points = dst_points_by_curve[curve_i];
handle_types_right.slice(points).fill(int8_t(BEZIER_HANDLE_FREE));
handle_types_left.slice(points).fill(int8_t(BEZIER_HANDLE_FREE));
handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f});
@ -587,18 +588,20 @@ static void trim_attribute_linear(const bke::CurvesGeometry &src_curves,
const Span<bke::curves::IndexRangeCyclic> src_ranges,
MutableSpan<bke::AttributeTransferData> transfer_attributes)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
for (bke::AttributeTransferData &attribute : transfer_attributes) {
attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points),
attribute.dst.span.typed<T>(),
src_ranges[curve_i],
dst_curves.points_for_curve(curve_i),
dst_points_by_curve[curve_i],
start_points[curve_i],
end_points[curve_i]);
}
@ -615,13 +618,15 @@ static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves,
const Span<bke::curves::IndexRangeCyclic> src_ranges,
MutableSpan<bke::AttributeTransferData> transfer_attributes)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
const Span<float3> src_positions = src_curves.positions();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange dst_points = dst_points_by_curve[curve_i];
sample_interval_linear<float3>(src_positions.slice(src_points),
dst_positions,
@ -650,14 +655,16 @@ static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves,
const Span<bke::curves::IndexRangeCyclic> src_ranges,
MutableSpan<bke::AttributeTransferData> transfer_attributes)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
const Span<float3> src_positions = src_curves.positions();
const VArray<bool> src_cyclic = src_curves.cyclic();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange dst_points = dst_points_by_curve[curve_i];
sample_interval_catmull_rom<float3>(src_positions.slice(src_points),
dst_positions,
@ -677,8 +684,8 @@ static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves,
threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange dst_points = dst_points_by_curve[curve_i];
sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points),
attribute.dst.span.typed<T>(),
@ -701,12 +708,14 @@ static void trim_bezier_curves(const bke::CurvesGeometry &src_curves,
const Span<bke::curves::IndexRangeCyclic> src_ranges,
MutableSpan<bke::AttributeTransferData> transfer_attributes)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const Span<float3> src_positions = src_curves.positions();
const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()};
const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()};
const Span<float3> src_handles_l = src_curves.handle_positions_left();
const Span<float3> src_handles_r = src_curves.handle_positions_right();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
@ -715,8 +724,8 @@ static void trim_bezier_curves(const bke::CurvesGeometry &src_curves,
threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange dst_points = dst_points_by_curve[curve_i];
sample_interval_bezier(src_positions.slice(src_points),
src_handles_l.slice(src_points),
@ -752,12 +761,14 @@ static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves,
const Span<bke::curves::IndexRangeCyclic> src_ranges,
MutableSpan<bke::AttributeTransferData> transfer_attributes)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
const Span<float3> src_eval_positions = src_curves.evaluated_positions();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
for (const int64_t curve_i : selection.slice(range)) {
const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
const IndexRange dst_points = dst_points_by_curve[curve_i];
const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i);
sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points),
@ -782,11 +793,11 @@ static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves,
GArray<> evaluated_data(CPPType::get<T>(), src_evaluated_points.size());
GMutableSpan evaluated_span = evaluated_data.as_mutable_span();
src_curves.interpolate_to_evaluated(
curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span);
curve_i, attribute.src.slice(src_points_by_curve[curve_i]), evaluated_span);
sample_interval_linear<T>(evaluated_span.typed<T>(),
attribute.dst.span.typed<T>(),
src_ranges[curve_i],
dst_curves.points_for_curve(curve_i),
dst_points_by_curve[curve_i],
start_points[curve_i],
end_points[curve_i]);
}
@ -824,6 +835,7 @@ static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves,
MutableSpan<bke::curves::CurvePoint> end_points,
MutableSpan<bke::curves::IndexRangeCyclic> src_ranges)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> src_cyclic = curves.cyclic();
const VArray<int> resolution = curves.resolution();
const VArray<int8_t> curve_types = curves.curve_types();
@ -840,7 +852,7 @@ static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves,
}
else {
dst_curve_types[curve_i] = curve_type;
point_count = curves.points_num_for_curve(curve_i);
point_count = points_by_curve.size(curve_i);
}
if (point_count == 1) {
/* Single point. */
@ -976,15 +988,16 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
/* Transfer copied curves parameters. */
const VArray<int8_t> src_curve_types = src_curves.curve_types();
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
threading::parallel_for(
inverse_selection.index_range(), 4096, [&](const IndexRange selection_range) {
for (const int64_t curve_i : inverse_selection.slice(selection_range)) {
dst_curve_offsets[curve_i] = src_curves.points_num_for_curve(curve_i);
dst_curve_offsets[curve_i] = src_points_by_curve.size(curve_i);
dst_curve_types[curve_i] = src_curve_types[curve_i];
}
});
/* Finalize and update the geometry container. */
bke::curves::accumulate_counts_to_offsets(dst_curve_offsets);
offset_indices::accumulate_counts_to_offsets(dst_curve_offsets);
dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
dst_curves.update_curve_types();

View File

@ -304,13 +304,14 @@ static void blur_on_curve_exec(const bke::CurvesGeometry &curves,
MutableSpan<T> src = main_buffer;
MutableSpan<T> dst = tmp_buffer;
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
for ([[maybe_unused]] const int iteration : IndexRange(iterations)) {
attribute_math::DefaultMixer<T> mixer{dst, IndexMask(0)};
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
if (points.size() == 1) {
/* No mixing possible. */
const int point_i = points[0];
@ -347,7 +348,7 @@ static void blur_on_curve_exec(const bke::CurvesGeometry &curves,
mixer.mix_in(last_i, src[last_i - 1], last_neighbor_weight);
}
}
mixer.finalize(curves.points_for_curves(range));
mixer.finalize(points_by_curve[range]);
});
std::swap(src, dst);
}

View File

@ -61,10 +61,11 @@ class EndpointFieldInput final : public bke::CurvesFieldInput {
Array<bool> selection(curves.points_num(), false);
MutableSpan<bool> selection_span = selection.as_mutable_span();
const OffsetIndices points_by_curve = curves.points_by_curve();
devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) {
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) {
for (const int i : curves_range) {
const IndexRange points = curves.points_for_curve(i);
const IndexRange points = points_by_curve[i];
const int start = std::max(start_size[i], 0);
const int end = std::max(end_size[i], 0);

View File

@ -52,12 +52,13 @@ static void select_by_handle_type(const bke::CurvesGeometry &curves,
const GeometryNodeCurveHandleMode mode,
const MutableSpan<bool> r_selection)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
VArray<int8_t> curve_types = curves.curve_types();
VArray<int8_t> left = curves.handle_types_left();
VArray<int8_t> right = curves.handle_types_right();
for (const int i_curve : curves.curves_range()) {
const IndexRange points = curves.points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
if (curve_types[i_curve] != CURVE_TYPE_BEZIER) {
r_selection.slice(points).fill(false);
}

View File

@ -312,6 +312,7 @@ class SampleCurveFunction : public mf::MultiFunction {
evaluated_normals = curves.evaluated_normals();
}
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
const VArray<bool> cyclic = curves.cyclic();
@ -377,7 +378,7 @@ class SampleCurveFunction : public mf::MultiFunction {
}
}
if (!sampled_values.is_empty()) {
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
src_original_values.reinitialize(points.size());
source_data_->materialize_compressed_to_uninitialized(points, src_original_values.data());
src_evaluated_values.reinitialize(curves.evaluated_points_for_curve(curve_i).size());

View File

@ -58,6 +58,7 @@ static Array<float> accumulated_lengths_curve_domain(const bke::CurvesGeometry &
static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves)
{
curves.ensure_evaluated_lengths();
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolutions = curves.resolution();
const VArray<bool> cyclic = curves.cyclic();
@ -66,7 +67,7 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves)
threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) {
for (const int i_curve : range) {
const IndexRange points = curves.points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
const Span<float> evaluated_lengths = curves.evaluated_lengths_for_curve(i_curve,
cyclic[i_curve]);
MutableSpan<float> lengths = result.as_mutable_span().slice(points);
@ -114,10 +115,11 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry
if (domain == ATTR_DOMAIN_POINT) {
Array<float> result = curve_length_point_domain(curves);
MutableSpan<float> lengths = result.as_mutable_span();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) {
for (const int i_curve : range) {
MutableSpan<float> curve_lengths = lengths.slice(curves.points_for_curve(i_curve));
MutableSpan<float> curve_lengths = lengths.slice(points_by_curve[i_curve]);
const float total_length = curve_lengths.last();
if (total_length > 0.0f) {
const float factor = 1.0f / total_length;
@ -193,9 +195,10 @@ static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &c
if (domain == ATTR_DOMAIN_POINT) {
Array<int> result(curves.points_num());
MutableSpan<int> span = result.as_mutable_span();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) {
for (const int i_curve : range) {
MutableSpan<int> indices = span.slice(curves.points_for_curve(i_curve));
MutableSpan<int> indices = span.slice(points_by_curve[i_curve]);
for (const int i : indices.index_range()) {
indices[i] = i;
}

View File

@ -62,6 +62,7 @@ class PointsOfCurveInput final : public bke::CurvesFieldInput {
point_evaluator.add(sort_weight_);
point_evaluator.evaluate();
const VArray<float> all_sort_weights = point_evaluator.get_evaluated<float>(0);
const OffsetIndices points_by_curve = curves.points_by_curve();
Array<int> point_of_curve(mask.min_array_size());
threading::parallel_for(mask.index_range(), 256, [&](const IndexRange range) {
@ -77,7 +78,7 @@ class PointsOfCurveInput final : public bke::CurvesFieldInput {
continue;
}
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
/* Retrieve the weights for each point. */
sort_weights.reinitialize(points.size());
@ -142,8 +143,9 @@ class CurvePointCountInput final : public bke::CurvesFieldInput {
if (domain != ATTR_DOMAIN_CURVE) {
return {};
}
return VArray<int>::ForFunc(curves.curves_num(), [&, curves](const int64_t curve_i) {
return curves.points_num_for_curve(curve_i);
const OffsetIndices points_by_curve = curves.points_by_curve();
return VArray<int>::ForFunc(curves.curves_num(), [points_by_curve](const int64_t curve_i) {
return points_by_curve.size(curve_i);
});
}

View File

@ -74,6 +74,8 @@ static void deform_curves(const CurvesGeometry &curves,
const Span<MLoop> surface_loops_new = surface_mesh_new.loops();
const Span<MLoopTri> surface_looptris_new = surface_mesh_new.looptris();
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i];
@ -197,7 +199,7 @@ static void deform_curves(const CurvesGeometry &curves,
const float4x4 curve_transform = surface_to_curves * surface_transform * curves_to_surface;
/* Actually transform all points. */
const IndexRange points = curves.points_for_curve(curve_i);
const IndexRange points = points_by_curve[curve_i];
for (const int point_i : points) {
const float3 old_point_pos = r_positions[point_i];
const float3 new_point_pos = curve_transform * old_point_pos;

View File

@ -235,6 +235,9 @@ static void copy_curve_attributes_without_id(
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info);
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
@ -265,9 +268,9 @@ static void copy_curve_attributes_without_id(
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
const int i_src_curve = selection[i_selection];
const Span<T> curve_src = src.slice(src_curves.points_for_curve(i_src_curve));
const Span<T> curve_src = src.slice(src_points_by_curve[i_src_curve]);
for (const int i_dst_curve : range_for_offsets_index(curve_offsets, i_selection)) {
dst.slice(dst_curves.points_for_curve(i_dst_curve)).copy_from(curve_src);
dst.slice(dst_points_by_curve[i_dst_curve]).copy_from(curve_src);
}
}
});
@ -305,15 +308,17 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
VArraySpan<int> src{src_attribute.varray.typed<int>()};
MutableSpan<int> dst = dst_attribute.span.typed<int>();
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
const int i_src_curve = selection[i_selection];
const Span<int> curve_src = src.slice(src_curves.points_for_curve(i_src_curve));
const Span<int> curve_src = src.slice(src_points_by_curve[i_src_curve]);
const IndexRange duplicates_range = range_for_offsets_index(curve_offsets, i_selection);
for (const int i_duplicate : IndexRange(duplicates_range.size()).drop_front(1)) {
const int i_dst_curve = duplicates_range[i_duplicate];
copy_hashed_ids(
curve_src, i_duplicate, dst.slice(dst_curves.points_for_curve(i_dst_curve)));
copy_hashed_ids(curve_src, i_duplicate, dst.slice(dst_points_by_curve[i_dst_curve]));
}
}
});
@ -344,6 +349,8 @@ static void duplicate_curves(GeometrySet &geometry_set,
const VArray<int> counts = evaluator.get_evaluated<int>(0);
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
const OffsetIndices points_by_curve = curves.points_by_curve();
/* The offset in the result curve domain at every selected input curve. */
Array<int> curve_offsets(selection.size() + 1);
Array<int> point_offsets(selection.size() + 1);
@ -355,7 +362,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
curve_offsets[i_curve] = dst_curves_num;
point_offsets[i_curve] = dst_points_num;
dst_curves_num += count;
dst_points_num += count * curves.points_for_curve(selection[i_curve]).size();
dst_points_num += count * points_by_curve.size(selection[i_curve]);
}
curve_offsets.last() = dst_curves_num;
point_offsets.last() = dst_points_num;
@ -368,7 +375,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
const int i_src_curve = selection[i_selection];
const IndexRange src_curve_range = curves.points_for_curve(i_src_curve);
const IndexRange src_curve_range = points_by_curve[i_src_curve];
const IndexRange dst_curves_range = range_for_offsets_index(curve_offsets, i_selection);
MutableSpan<int> dst_offsets = all_dst_offsets.slice(dst_curves_range);
for (const int i_duplicate : IndexRange(dst_curves_range.size())) {
@ -814,10 +821,11 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
const int dst_num = offsets.last();
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
Array<int> point_to_curve_map(src_curves.points_num());
threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) {
for (const int i_curve : range) {
const IndexRange points = src_curves.points_for_curve(i_curve);
const IndexRange points = src_points_by_curve[i_curve];
point_to_curve_map.as_mutable_span().slice(points).fill(i_curve);
}
});

View File

@ -19,7 +19,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static VArray<int> construct_curve_point_count_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); };
const OffsetIndices points_by_curve = curves.points_by_curve();
auto count_fn = [points_by_curve](int64_t i) { return points_by_curve.size(i); };
if (domain == ATTR_DOMAIN_CURVE) {
return VArray<int>::ForFunc(curves.curves_num(), count_fn);

View File

@ -15,6 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b)
static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curves)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolutions = curves.resolution();
const VArray<bool> cyclic = curves.cyclic();
@ -26,7 +27,7 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve
threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) {
for (const int i_curve : range) {
const IndexRange points = curves.points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
const IndexRange evaluated_points = curves.evaluated_points_for_curve(i_curve);
MutableSpan<float3> curve_tangents = results.as_mutable_span().slice(points);

View File

@ -61,6 +61,7 @@ class ControlPointNeighborFieldInput final : public bke::CurvesFieldInput {
const eAttrDomain domain,
const IndexMask mask) const final
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
const Array<int> parent_curves = curves.point_to_curve_map();
@ -76,7 +77,7 @@ class ControlPointNeighborFieldInput final : public bke::CurvesFieldInput {
for (const int i_selection : mask) {
const int i_point = std::clamp(indices[i_selection], 0, curves.points_num() - 1);
const int i_curve = parent_curves[i_point];
const IndexRange curve_points = curves.points_for_curve(i_curve);
const IndexRange curve_points = points_by_curve[i_curve];
const int offset_point = i_point + offsets[i_point];
if (cyclic[i_curve]) {
@ -116,6 +117,7 @@ class OffsetValidFieldInput final : public bke::CurvesFieldInput {
const IndexMask mask) const final
{
const VArray<bool> cyclic = curves.cyclic();
const OffsetIndices points_by_curve = curves.points_by_curve();
const Array<int> parent_curves = curves.point_to_curve_map();
const bke::CurvesFieldContext context{curves, domain};
@ -135,7 +137,7 @@ class OffsetValidFieldInput final : public bke::CurvesFieldInput {
}
const int i_curve = parent_curves[i_point];
const IndexRange curve_points = curves.points_for_curve(i_curve);
const IndexRange curve_points = points_by_curve[i_curve];
if (cyclic[i_curve]) {
output[i_selection] = true;
continue;