Cleanup: Use OffsetIndices class in more cases

The same logic from D17025 is used in other places in the curve code.
This patch uses the class for the evaluated point offsets and the Bezier
control point offsets. This helps to standardize the behavior and make
it easier to read.

Previously the Bezier control point offsets used a slightly different standard
where the first point was the first offset, just so they could have the same
size as the number of points. However two nodes used a helper function
to use the same `OffsetIndices` system, so switch to that there too.
That requires removing the subtraction by one to find the actual offset.

Also add const when accessing data arrays from curves, for consistency.

Differential Revision: https://developer.blender.org/D17038
This commit is contained in:
Hans Goudey 2023-01-19 13:48:20 -06:00
parent d3ea931647
commit 38a45e46bc
Notes: blender-bot 2024-03-25 12:30:38 +01:00
Referenced by commit b6278c5a96, Fix: crash when subdividing curves
16 changed files with 323 additions and 310 deletions

View File

@ -30,17 +30,6 @@
namespace blender::bke {
template<typename T, BLI_ENABLE_IF(std::is_integral_v<T>)>
constexpr IndexRange offsets_to_range(Span<T> offsets, int64_t index)
{
BLI_assert(index >= 0);
BLI_assert(index < offsets.size());
const int offset = offsets[index];
const int offset_next = offsets[index + 1];
return {offset, offset_next - offset};
}
namespace curves::nurbs {
struct BasisCache {
@ -81,7 +70,7 @@ class CurvesGeometryRuntime {
* evaluated points, Bezier curve vector segments, different resolutions per curve, etc.
*/
mutable Vector<int> evaluated_offsets_cache;
mutable Vector<int> bezier_evaluated_offsets;
mutable Vector<int> all_bezier_evaluated_offsets;
mutable CacheMutex offsets_cache_mutex;
mutable Vector<curves::nurbs::BasisCache> nurbs_basis_cache;
@ -303,25 +292,14 @@ class CurvesGeometry : public ::CurvesGeometry {
int evaluated_points_num() const;
/**
* Access a range of indices of point data for a specific curve.
* Call #evaluated_offsets() first to ensure that the evaluated offsets cache is current.
* The offsets of every curve's evaluated points.
*/
IndexRange evaluated_points_for_curve(int index) const;
IndexRange evaluated_points_for_curves(IndexRange curves) const;
OffsetIndices<int> evaluated_points_by_curve() const;
/**
* The index of the first evaluated point for every curve. The size of this span is one larger
* than the number of curves. Consider using #evaluated_points_for_curve rather than using the
* offsets directly.
*/
Span<int> evaluated_offsets() const;
/** Makes sure the data described by #evaluated_offsets if necessary. */
void ensure_evaluated_offsets() const;
/**
* Retrieve offsets into a Bezier curve's evaluated points for each control point.
* Call #ensure_evaluated_offsets() first to ensure that the evaluated offsets cache is current.
* Retrieve offsets into a Bezier curve's evaluated points for each control point. Stored in the
* same format as #OffsetIndices. Call #evaluated_points_by_curve() first to ensure that the
* evaluated offsets cache is current.
*/
Span<int> bezier_evaluated_offsets_for_curve(int curve_index) const;
@ -489,6 +467,17 @@ inline float3 decode_surface_bary_coord(const float2 &v)
return {v.x, v.y, 1.0f - v.x - v.y};
}
/**
* Return a range used to retrieve values from an array of values stored per point, but with an
* extra element at the end of each curve. This is useful for offsets within curves, where it is
* convenient to store the first 0 and have the last offset be the total result curve size, using
* the same rules as #OffsetIndices.
*/
inline IndexRange per_curve_point_offsets_range(const IndexRange points, const int curve_index)
{
return {curve_index + points.start(), points.size() + 1};
}
/** \} */
/* -------------------------------------------------------------------- */
@ -567,8 +556,9 @@ bool point_is_sharp(Span<int8_t> handle_types_left, Span<int8_t> handle_types_ri
* point edges generate the number of edges specified by the resolution, vector segments only
* generate one edge.
*
* The size of the offsets array must be the same as the number of points. The value at each index
* is the evaluated point offset including the following segment.
* The expectations for the result \a evaluated_offsets are the same as for #OffsetIndices, so the
* size must be one greater than the number of points. The value at each index is the evaluated
* point at the start of that segment.
*/
void calculate_evaluated_offsets(Span<int8_t> handle_types_left,
Span<int8_t> handle_types_right,
@ -668,7 +658,7 @@ void evaluate_segment(const float3 &point_0,
void calculate_evaluated_positions(Span<float3> positions,
Span<float3> handles_left,
Span<float3> handles_right,
Span<int> evaluated_offsets,
OffsetIndices<int> evaluated_offsets,
MutableSpan<float3> evaluated_positions);
/**
@ -676,7 +666,7 @@ void calculate_evaluated_positions(Span<float3> positions,
* #evaluated_offsets. Unlike other curve types, for Bezier curves generic data and positions
* are treated separately, since attribute values aren't stored for the handle control points.
*/
void interpolate_to_evaluated(GSpan src, Span<int> evaluated_offsets, GMutableSpan dst);
void interpolate_to_evaluated(GSpan src, OffsetIndices<int> evaluated_offsets, GMutableSpan dst);
} // namespace bezier
@ -702,12 +692,12 @@ int calculate_evaluated_num(int points_num, bool cyclic, int resolution);
void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst);
/**
* Evaluate the Catmull Rom curve. The size of each segment and its offset in the #dst span
* is encoded in #evaluated_offsets, with the same method as #CurvesGeometry::offsets().
* Evaluate the Catmull Rom curve. The placement of each segment in the #dst span is desribed by
* #evaluated_offsets.
*/
void interpolate_to_evaluated(const GSpan src,
const bool cyclic,
const Span<int> evaluated_offsets,
const OffsetIndices<int> evaluated_offsets,
GMutableSpan dst);
void calculate_basis(const float parameter, float4 &r_weights);
@ -877,36 +867,22 @@ inline OffsetIndices<int> CurvesGeometry::points_by_curve() const
inline int CurvesGeometry::evaluated_points_num() const
{
/* This could avoid calculating offsets in the future in simple circumstances. */
return this->evaluated_offsets().last();
}
inline IndexRange CurvesGeometry::evaluated_points_for_curve(int index) const
{
BLI_assert(this->runtime->offsets_cache_mutex.is_cached());
return offsets_to_range(this->runtime->evaluated_offsets_cache.as_span(), index);
}
inline IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange curves) const
{
BLI_assert(this->runtime->offsets_cache_mutex.is_cached());
BLI_assert(this->curve_num > 0);
const int offset = this->runtime->evaluated_offsets_cache[curves.start()];
const int offset_next = this->runtime->evaluated_offsets_cache[curves.one_after_last()];
return {offset, offset_next - offset};
return this->evaluated_points_by_curve().total_size();
}
inline Span<int> CurvesGeometry::bezier_evaluated_offsets_for_curve(const int curve_index) const
{
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);
const IndexRange range = curves::per_curve_point_offsets_range(points, curve_index);
return this->runtime->all_bezier_evaluated_offsets.as_span().slice(range);
}
inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index,
const bool cyclic) const
{
BLI_assert(cyclic == this->cyclic()[curve_index]);
const IndexRange points = this->evaluated_points_for_curve(curve_index);
const IndexRange points = this->evaluated_points_by_curve()[curve_index];
const int start = points.start() + curve_index;
return {start, curves::segments_num(points.size(), cyclic)};
}

View File

@ -33,20 +33,21 @@ void calculate_evaluated_offsets(const Span<int8_t> handle_types_left,
MutableSpan<int> evaluated_offsets)
{
const int size = handle_types_left.size();
BLI_assert(evaluated_offsets.size() == size);
BLI_assert(evaluated_offsets.size() == size + 1);
evaluated_offsets.first() = 0;
if (size == 1) {
evaluated_offsets.first() = 1;
evaluated_offsets.last() = 1;
return;
}
int offset = 0;
for (const int i : IndexRange(size - 1)) {
offset += segment_is_vector(handle_types_left, handle_types_right, i) ? 1 : resolution;
evaluated_offsets[i] = offset;
offset += segment_is_vector(handle_types_left, handle_types_right, i) ? 1 : resolution;
}
evaluated_offsets.last(1) = offset;
if (cyclic) {
offset += last_cyclic_segment_is_vector(handle_types_left, handle_types_right) ? 1 :
resolution;
@ -233,12 +234,11 @@ void evaluate_segment(const float3 &point_0,
void calculate_evaluated_positions(const Span<float3> positions,
const Span<float3> handles_left,
const Span<float3> handles_right,
const Span<int> evaluated_offsets,
const OffsetIndices<int> evaluated_offsets,
MutableSpan<float3> evaluated_positions)
{
BLI_assert(evaluated_offsets.last() == evaluated_positions.size());
BLI_assert(evaluated_offsets.size() == positions.size());
if (evaluated_offsets.last() == 1) {
BLI_assert(evaluated_offsets.total_size() == evaluated_positions.size());
if (evaluated_offsets.total_size() == 1) {
evaluated_positions.first() = positions.first();
return;
}
@ -248,29 +248,29 @@ void calculate_evaluated_positions(const Span<float3> positions,
handles_right.first(),
handles_left[1],
positions[1],
evaluated_positions.take_front(evaluated_offsets.first()));
evaluated_positions.slice(evaluated_offsets[0]));
/* Give each task fewer segments as the resolution gets larger. */
const int grain_size = std::max<int>(evaluated_positions.size() / positions.size() * 32, 1);
threading::parallel_for(
positions.index_range().drop_back(1).drop_front(1), grain_size, [&](IndexRange range) {
for (const int i : range) {
const IndexRange evaluated_range = offsets_to_range(evaluated_offsets, i - 1);
if (evaluated_range.size() == 1) {
evaluated_positions[evaluated_range.first()] = positions[i];
}
else {
evaluate_segment(positions[i],
handles_right[i],
handles_left[i + 1],
positions[i + 1],
evaluated_positions.slice(evaluated_range));
}
}
});
const IndexRange inner_segments = positions.index_range().drop_back(1).drop_front(1);
threading::parallel_for(inner_segments, grain_size, [&](IndexRange range) {
for (const int i : range) {
const IndexRange evaluated_range = evaluated_offsets[i];
if (evaluated_range.size() == 1) {
evaluated_positions[evaluated_range.first()] = positions[i];
}
else {
evaluate_segment(positions[i],
handles_right[i],
handles_left[i + 1],
positions[i + 1],
evaluated_positions.slice(evaluated_range));
}
}
});
/* Evaluate the final cyclic segment if necessary. */
const IndexRange last_segment_points = offsets_to_range(evaluated_offsets, positions.size() - 2);
const IndexRange last_segment_points = evaluated_offsets[positions.index_range().last()];
if (last_segment_points.size() == 1) {
evaluated_positions.last() = positions.last();
}
@ -295,34 +295,34 @@ static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> d
template<typename T>
static void interpolate_to_evaluated(const Span<T> src,
const Span<int> evaluated_offsets,
const OffsetIndices<int> evaluated_offsets,
MutableSpan<T> dst)
{
BLI_assert(!src.is_empty());
BLI_assert(evaluated_offsets.size() == src.size());
BLI_assert(evaluated_offsets.last() == dst.size());
BLI_assert(evaluated_offsets.total_size() == dst.size());
if (src.size() == 1) {
BLI_assert(dst.size() == 1);
dst.first() = src.first();
return;
}
linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first()));
linear_interpolation(src.first(), src[1], dst.slice(evaluated_offsets[0]));
threading::parallel_for(
src.index_range().drop_back(1).drop_front(1), 512, [&](IndexRange range) {
for (const int i : range) {
const IndexRange segment_points = offsets_to_range(evaluated_offsets, i - 1);
linear_interpolation(src[i], src[i + 1], dst.slice(segment_points));
const IndexRange segment = evaluated_offsets[i];
linear_interpolation(src[i], src[i + 1], dst.slice(segment));
}
});
const IndexRange last_segment_points(evaluated_offsets.last(1),
evaluated_offsets.last() - evaluated_offsets.last(1));
linear_interpolation(src.last(), src.first(), dst.slice(last_segment_points));
const IndexRange last_segment = evaluated_offsets[src.index_range().last()];
linear_interpolation(src.last(), src.first(), dst.slice(last_segment));
}
void interpolate_to_evaluated(const GSpan src, const Span<int> evaluated_offsets, GMutableSpan dst)
void interpolate_to_evaluated(const GSpan src,
const OffsetIndices<int> evaluated_offsets,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);

View File

@ -123,7 +123,7 @@ static void interpolate_to_evaluated(const Span<T> src,
template<typename T>
static void interpolate_to_evaluated(const Span<T> src,
const bool cyclic,
const Span<int> evaluated_offsets,
const OffsetIndices<int> evaluated_offsets,
MutableSpan<T> dst)
{
@ -131,7 +131,7 @@ static void interpolate_to_evaluated(const Span<T> src,
src,
cyclic,
[evaluated_offsets](const int segment_i) -> IndexRange {
return bke::offsets_to_range(evaluated_offsets, segment_i);
return evaluated_offsets[segment_i];
},
dst);
}
@ -149,7 +149,7 @@ void interpolate_to_evaluated(const GSpan src,
void interpolate_to_evaluated(const GSpan src,
const bool cyclic,
const Span<int> evaluated_offsets,
const OffsetIndices<int> evaluated_offsets,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {

View File

@ -166,7 +166,7 @@ static void mark_bezier_vector_edges_sharp(const int profile_point_num,
for (const int i : IndexRange(profile_point_num).drop_front(1)) {
if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, i)) {
const int offset = main_edges_start + main_segment_num * control_point_offsets[i - 1];
const int offset = main_edges_start + main_segment_num * control_point_offsets[i];
sharp_edges.slice(offset, main_segment_num).fill(true);
}
}
@ -246,8 +246,8 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
result.main_indices.reinitialize(result.total);
result.profile_indices.reinitialize(result.total);
info.main.ensure_evaluated_offsets();
info.profile.ensure_evaluated_offsets();
const OffsetIndices<int> main_offsets = info.main.evaluated_points_by_curve();
const OffsetIndices<int> profile_offsets = info.profile.evaluated_points_by_curve();
int mesh_index = 0;
int vert_offset = 0;
@ -256,7 +256,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
int poly_offset = 0;
for (const int i_main : info.main.curves_range()) {
const bool main_cyclic = info.main_cyclic[i_main];
const int main_point_num = info.main.evaluated_points_for_curve(i_main).size();
const int main_point_num = main_offsets.size(i_main);
const int main_segment_num = curves::segments_num(main_point_num, main_cyclic);
for (const int i_profile : info.profile.curves_range()) {
result.vert[mesh_index] = vert_offset;
@ -268,7 +268,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
result.profile_indices[mesh_index] = i_profile;
const bool profile_cyclic = info.profile_cyclic[i_profile];
const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size();
const int profile_point_num = profile_offsets.size(i_profile);
const int profile_segment_num = curves::segments_num(profile_point_num, profile_cyclic);
const bool has_caps = fill_caps && !main_cyclic && profile_cyclic;
@ -377,13 +377,19 @@ static void foreach_curve_combination(const CurvesInfo &info,
const ResultOffsets &offsets,
const Fn &fn)
{
const OffsetIndices<int> main_offsets = info.main.evaluated_points_by_curve();
const OffsetIndices<int> profile_offsets = info.profile.evaluated_points_by_curve();
const OffsetIndices<int> vert_offsets(offsets.vert);
const OffsetIndices<int> edge_offsets(offsets.edge);
const OffsetIndices<int> poly_offsets(offsets.poly);
const OffsetIndices<int> loop_offsets(offsets.loop);
threading::parallel_for(IndexRange(offsets.total), 512, [&](IndexRange range) {
for (const int i : range) {
const int i_main = offsets.main_indices[i];
const int i_profile = offsets.profile_indices[i];
const IndexRange main_points = info.main.evaluated_points_for_curve(i_main);
const IndexRange profile_points = info.profile.evaluated_points_for_curve(i_profile);
const IndexRange main_points = main_offsets[i_main];
const IndexRange profile_points = profile_offsets[i_profile];
const bool main_cyclic = info.main_cyclic[i_main];
const bool profile_cyclic = info.profile_cyclic[i_profile];
@ -399,10 +405,10 @@ static void foreach_curve_combination(const CurvesInfo &info,
profile_cyclic,
curves::segments_num(main_points.size(), main_cyclic),
curves::segments_num(profile_points.size(), profile_cyclic),
offsets_to_range(offsets.vert.as_span(), i),
offsets_to_range(offsets.edge.as_span(), i),
offsets_to_range(offsets.poly.as_span(), i),
offsets_to_range(offsets.loop.as_span(), i)});
vert_offsets[i],
edge_offsets[i],
poly_offsets[i],
loop_offsets[i]});
}
});
}
@ -570,7 +576,7 @@ static void copy_profile_point_domain_attribute_to_mesh(const CurvesInfo &curves
template<typename T>
static void copy_indices_to_offset_ranges(const VArray<T> &src,
const Span<int> curve_indices,
const Span<int> mesh_offsets,
const OffsetIndices<int> mesh_offsets,
MutableSpan<T> dst)
{
/* This unnecessarily instantiates the "is single" case (which should be handled elsewhere if
@ -579,7 +585,7 @@ static void copy_indices_to_offset_ranges(const VArray<T> &src,
devirtualize_varray(src, [&](const auto &src) {
threading::parallel_for(curve_indices.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
dst.slice(offsets_to_range(mesh_offsets, i)).fill(src[curve_indices[i]]);
dst.slice(mesh_offsets[i]).fill(src[curve_indices[i]]);
}
});
});

View File

@ -455,18 +455,18 @@ template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const Co
static void calculate_evaluated_offsets(const CurvesGeometry &curves,
MutableSpan<int> offsets,
MutableSpan<int> bezier_evaluated_offsets)
MutableSpan<int> all_bezier_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();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolution = curves.resolution();
const VArray<bool> cyclic = curves.cyclic();
VArraySpan<int8_t> handle_types_left{curves.handle_types_left()};
VArraySpan<int8_t> handle_types_right{curves.handle_types_right()};
const VArraySpan<int8_t> handle_types_left{curves.handle_types_left()};
const VArraySpan<int8_t> handle_types_right{curves.handle_types_right()};
VArray<int8_t> nurbs_orders = curves.nurbs_orders();
VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
const VArray<int8_t> nurbs_orders = curves.nurbs_orders();
const VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
build_offsets(offsets, [&](const int curve_index) -> int {
const IndexRange points = points_by_curve[curve_index];
@ -476,13 +476,15 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves,
points.size(), cyclic[curve_index], resolution[curve_index]);
case CURVE_TYPE_POLY:
return points.size();
case CURVE_TYPE_BEZIER:
case CURVE_TYPE_BEZIER: {
const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points),
handle_types_right.slice(points),
cyclic[curve_index],
resolution[curve_index],
bezier_evaluated_offsets.slice(points));
return bezier_evaluated_offsets[points.last()];
all_bezier_offsets.slice(offsets));
return all_bezier_offsets[offsets.last()];
}
case CURVE_TYPE_NURBS:
return curves::nurbs::calculate_evaluated_num(points.size(),
nurbs_orders[curve_index],
@ -495,27 +497,24 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves,
});
}
void CurvesGeometry::ensure_evaluated_offsets() const
OffsetIndices<int> CurvesGeometry::evaluated_points_by_curve() const
{
this->runtime->offsets_cache_mutex.ensure([&]() {
this->runtime->evaluated_offsets_cache.resize(this->curves_num() + 1);
if (this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
this->runtime->bezier_evaluated_offsets.resize(this->points_num());
this->runtime->all_bezier_evaluated_offsets.resize(this->points_num() + this->curves_num());
}
else {
this->runtime->bezier_evaluated_offsets.clear_and_shrink();
this->runtime->all_bezier_evaluated_offsets.clear_and_shrink();
}
calculate_evaluated_offsets(
*this, this->runtime->evaluated_offsets_cache, this->runtime->bezier_evaluated_offsets);
calculate_evaluated_offsets(*this,
this->runtime->evaluated_offsets_cache,
this->runtime->all_bezier_evaluated_offsets);
});
}
Span<int> CurvesGeometry::evaluated_offsets() const
{
this->ensure_evaluated_offsets();
return this->runtime->evaluated_offsets_cache;
return OffsetIndices<int>(this->runtime->evaluated_offsets_cache);
}
IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
@ -557,14 +556,15 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
MutableSpan<curves::nurbs::BasisCache> basis_caches(this->runtime->nurbs_basis_cache);
const OffsetIndices<int> 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();
const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
const VArray<bool> cyclic = this->cyclic();
const VArray<int8_t> orders = this->nurbs_orders();
const 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 = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
const int8_t order = orders[curve_index];
const bool is_cyclic = cyclic[curve_index];
@ -603,24 +603,25 @@ Span<float3> CurvesGeometry::evaluated_positions() const
this->runtime->evaluated_positions_span = evaluated_positions;
const OffsetIndices<int> points_by_curve = this->points_by_curve();
VArray<int8_t> types = this->curve_types();
VArray<bool> cyclic = this->cyclic();
VArray<int> resolution = this->resolution();
Span<float3> positions = this->positions();
const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
const VArray<int8_t> types = this->curve_types();
const VArray<bool> cyclic = this->cyclic();
const VArray<int> resolution = this->resolution();
const Span<float3> positions = this->positions();
Span<float3> handle_positions_left = this->handle_positions_left();
Span<float3> handle_positions_right = this->handle_positions_right();
Span<int> bezier_evaluated_offsets = this->runtime->bezier_evaluated_offsets;
const Span<float3> handle_positions_left = this->handle_positions_left();
const Span<float3> handle_positions_right = this->handle_positions_right();
const Span<int> all_bezier_evaluated_offsets = this->runtime->all_bezier_evaluated_offsets;
VArray<int8_t> nurbs_orders = this->nurbs_orders();
Span<float> nurbs_weights = this->nurbs_weights();
const VArray<int8_t> nurbs_orders = this->nurbs_orders();
const Span<float> nurbs_weights = this->nurbs_weights();
this->ensure_nurbs_basis_cache();
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
for (const int curve_index : curves_range) {
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
switch (types[curve_index]) {
case CURVE_TYPE_CATMULL_ROM:
@ -633,22 +634,23 @@ Span<float3> CurvesGeometry::evaluated_positions() const
case CURVE_TYPE_POLY:
evaluated_positions.slice(evaluated_points).copy_from(positions.slice(points));
break;
case CURVE_TYPE_BEZIER:
case CURVE_TYPE_BEZIER: {
const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
curves::bezier::calculate_evaluated_positions(
positions.slice(points),
handle_positions_left.slice(points),
handle_positions_right.slice(points),
bezier_evaluated_offsets.slice(points),
all_bezier_evaluated_offsets.slice(offsets),
evaluated_positions.slice(evaluated_points));
break;
case CURVE_TYPE_NURBS: {
}
case CURVE_TYPE_NURBS:
curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
nurbs_orders[curve_index],
nurbs_weights.slice_safe(points),
positions.slice(points),
evaluated_positions.slice(evaluated_points));
break;
}
default:
BLI_assert_unreachable();
break;
@ -662,6 +664,7 @@ Span<float3> CurvesGeometry::evaluated_positions() const
Span<float3> CurvesGeometry::evaluated_tangents() const
{
this->runtime->tangent_cache_mutex.ensure([&]() {
const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
const Span<float3> evaluated_positions = this->evaluated_positions();
const VArray<bool> cyclic = this->cyclic();
@ -670,7 +673,7 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
for (const int curve_index : curves_range) {
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points),
cyclic[curve_index],
tangents.slice(evaluated_points));
@ -694,7 +697,7 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
continue;
}
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
const float epsilon = 1e-6f;
if (!math::almost_equal_relative(
@ -729,7 +732,7 @@ static void evaluate_generic_data_for_curve(
const VArray<int8_t> &types,
const VArray<bool> &cyclic,
const VArray<int> &resolution,
const Span<int> bezier_evaluated_offsets,
const Span<int> all_bezier_evaluated_offsets,
const Span<curves::nurbs::BasisCache> nurbs_basis_cache,
const VArray<int8_t> &nurbs_orders,
const Span<float> nurbs_weights,
@ -744,9 +747,12 @@ static void evaluate_generic_data_for_curve(
case CURVE_TYPE_POLY:
dst.copy_from(src);
break;
case CURVE_TYPE_BEZIER:
curves::bezier::interpolate_to_evaluated(src, bezier_evaluated_offsets.slice(points), dst);
case CURVE_TYPE_BEZIER: {
const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
curves::bezier::interpolate_to_evaluated(
src, all_bezier_evaluated_offsets.slice(offsets), dst);
break;
}
case CURVE_TYPE_NURBS:
curves::nurbs::interpolate_to_evaluated(nurbs_basis_cache[curve_index],
nurbs_orders[curve_index],
@ -761,6 +767,7 @@ Span<float3> CurvesGeometry::evaluated_normals() const
{
this->runtime->normal_cache_mutex.ensure([&]() {
const OffsetIndices<int> points_by_curve = this->points_by_curve();
const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_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();
@ -784,7 +791,7 @@ Span<float3> CurvesGeometry::evaluated_normals() const
Vector<float> evaluated_tilts;
for (const int curve_index : curves_range) {
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
switch (normal_mode[curve_index]) {
case NORMAL_MODE_Z_UP:
curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points),
@ -813,7 +820,7 @@ Span<float3> CurvesGeometry::evaluated_normals() const
types,
cyclic,
resolution,
this->runtime->bezier_evaluated_offsets.as_span(),
this->runtime->all_bezier_evaluated_offsets.as_span(),
this->runtime->nurbs_basis_cache.as_span(),
nurbs_orders,
nurbs_weights,
@ -839,13 +846,13 @@ void CurvesGeometry::interpolate_to_evaluated(const int 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());
BLI_assert(dst.size() == this->evaluated_points_by_curve().size(curve_index));
evaluate_generic_data_for_curve(curve_index,
points,
this->curve_types(),
this->cyclic(),
this->resolution(),
this->runtime->bezier_evaluated_offsets.as_span(),
this->runtime->all_bezier_evaluated_offsets.as_span(),
this->runtime->nurbs_basis_cache.as_span(),
this->nurbs_orders(),
this->nurbs_weights(),
@ -858,6 +865,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 OffsetIndices evaluated_points_by_curve = this->evaluated_points_by_curve();
const VArray<int8_t> types = this->curve_types();
const VArray<int> resolution = this->resolution();
const VArray<bool> cyclic = this->cyclic();
@ -867,13 +875,13 @@ 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 = points_by_curve[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
evaluate_generic_data_for_curve(curve_index,
points,
types,
cyclic,
resolution,
this->runtime->bezier_evaluated_offsets,
this->runtime->all_bezier_evaluated_offsets,
this->runtime->nurbs_basis_cache,
nurbs_orders,
nurbs_weights,
@ -892,13 +900,14 @@ void CurvesGeometry::ensure_evaluated_lengths() const
this->runtime->evaluated_length_cache.resize(total_num);
MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache;
Span<float3> evaluated_positions = this->evaluated_positions();
VArray<bool> curves_cyclic = this->cyclic();
const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
const Span<float3> evaluated_positions = this->evaluated_positions();
const VArray<bool> curves_cyclic = this->cyclic();
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
for (const int curve_index : curves_range) {
const bool cyclic = curves_cyclic[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points),
cyclic,
@ -910,7 +919,7 @@ void CurvesGeometry::ensure_evaluated_lengths() const
void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
{
this->ensure_evaluated_offsets();
this->evaluated_points_by_curve();
this->ensure_nurbs_basis_cache();
}

View File

@ -144,6 +144,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 OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolutions = curves.resolution();
const VArray<bool> curves_cyclic = curves.cyclic();
@ -160,7 +161,7 @@ static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves
for (const int i_curve : range) {
const IndexRange points = points_by_curve[i_curve];
const IndexRange evaluated_points = curves.evaluated_points_for_curve(i_curve);
const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
MutableSpan<float3> curve_normals = results.as_mutable_span().slice(points);
@ -181,7 +182,7 @@ static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves
curve_normals.first() = normals.first();
const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve);
for (const int i : IndexRange(points.size()).drop_front(1)) {
curve_normals[i] = normals[offsets[i - 1]];
curve_normals[i] = normals[offsets[i]];
}
break;
}
@ -242,7 +243,7 @@ static VArray<float> construct_curve_length_gvarray(const CurvesGeometry &curves
{
curves.ensure_evaluated_lengths();
VArray<bool> cyclic = curves.cyclic();
const VArray<bool> cyclic = curves.cyclic();
VArray<float> lengths = VArray<float>::ForFunc(
curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) {
return curves.evaluated_length_total_for_curve(index, cyclic[index]);

View File

@ -39,6 +39,18 @@ template<typename T> class OffsetIndices {
return size;
}
/** Return the total number of elements in the the referenced arrays. */
T total_size() const
{
return offsets_.last();
}
/** Return the number of ranges encoded by the offsets. */
T ranges_num() const
{
return offsets_.size() - 1;
}
IndexRange operator[](const int64_t index) const
{
BLI_assert(index >= 0);
@ -56,6 +68,17 @@ template<typename T> class OffsetIndices {
const int64_t size = end - begin;
return IndexRange(begin, size);
}
/**
* Return a subset of the offsets desribing the specified range of source elements.
* This is a slice into the source ranges rather than the indexed elements described by the
* offset values.
*/
OffsetIndices slice(const IndexRange range) const
{
BLI_assert(offsets_.index_range().drop_back(1).contains(range.last()));
return OffsetIndices(offsets_.slice(range.start(), range.one_after_last()));
}
};
/**

View File

@ -42,6 +42,7 @@ using blender::Array;
using blender::ColorGeometry4f;
using blender::float3;
using blender::IndexRange;
using blender::OffsetIndices;
using blender::Span;
/* See: edit_curve_point_vert.glsl for duplicate includes. */
@ -103,14 +104,14 @@ static void curve_eval_render_wire_verts_edges_len_get(const blender::bke::Curve
int *r_vert_len,
int *r_edge_len)
{
*r_curve_len = curves.curves_num();
*r_vert_len = curves.evaluated_points_num();
*r_edge_len = 0;
const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
const blender::VArray<bool> cyclic = curves.cyclic();
*r_curve_len = curves.curves_num();
*r_vert_len = points_by_curve.total_size();
*r_edge_len = 0;
for (const int i : curves.curves_range()) {
const IndexRange points = curves.evaluated_points_for_curve(i);
*r_edge_len += blender::bke::curves::segments_num(points.size(), cyclic[i]);
*r_edge_len += blender::bke::curves::segments_num(points_by_curve.size(i), cyclic[i]);
}
}
@ -480,6 +481,7 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv
static void curve_create_attribute(CurveRenderData *rdata, GPUVertBuf *vbo_attr)
{
using namespace blender;
if (rdata->curve_eval == nullptr) {
return;
}
@ -493,18 +495,17 @@ static void curve_create_attribute(CurveRenderData *rdata, GPUVertBuf *vbo_attr)
GPU_vertbuf_init_with_format(vbo_attr, &format);
GPU_vertbuf_data_alloc(vbo_attr, vert_len);
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
rdata->curve_eval->geometry);
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(rdata->curve_eval->geometry);
curves.ensure_can_interpolate_to_evaluated();
const blender::VArraySpan<ColorGeometry4f> colors = curves.attributes().lookup<ColorGeometry4f>(
const VArraySpan<ColorGeometry4f> colors = curves.attributes().lookup<ColorGeometry4f>(
".viewer", ATTR_DOMAIN_POINT);
ColorGeometry4f *vbo_data = static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(vbo_attr));
curves.interpolate_to_evaluated(colors,
blender::MutableSpan<ColorGeometry4f>{vbo_data, vert_len});
curves.interpolate_to_evaluated(colors, MutableSpan<ColorGeometry4f>{vbo_data, vert_len});
}
static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines)
{
using namespace blender;
if (rdata->curve_eval == nullptr) {
return;
}
@ -518,11 +519,12 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
rdata->curve_eval->geometry);
const blender::VArray<bool> cyclic = curves.cyclic();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(rdata->curve_eval->geometry);
const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
for (const int i : curves.curves_range()) {
const IndexRange points = curves.evaluated_points_for_curve(i);
const IndexRange points = points_by_curve[i];
if (cyclic[i] && points.size() > 1) {
GPU_indexbuf_add_generic_vert(&elb, points.last());
}

View File

@ -13,22 +13,14 @@
namespace blender::geometry {
/**
* Return a range used to retrieve values from an array of values stored per point, but with an
* extra element at the end of each curve. This is useful for offsets within curves, where it is
* convenient to store the first 0 and have the last offset be the total result curve size.
*/
static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index)
{
return {curve_index + points.start(), points.size() + 1};
}
template<typename T>
static void threaded_slice_fill(const Span<T> src, const Span<int> offsets, MutableSpan<T> dst)
static void threaded_slice_fill(const Span<T> src,
const OffsetIndices<int> offsets,
MutableSpan<T> dst)
{
threading::parallel_for(src.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
dst.slice(bke::offsets_to_range(offsets, i)).fill(src[i]);
dst.slice(offsets[i]).fill(src[i]);
}
});
}
@ -37,7 +29,7 @@ template<typename T>
static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
const bke::CurvesGeometry &dst_curves,
const IndexMask curve_selection,
const Span<int> point_offsets,
const Span<int> all_point_offsets,
const Span<T> src,
MutableSpan<T> dst)
{
@ -47,7 +39,8 @@ static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
for (const int curve_i : curve_selection.slice(range)) {
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));
const IndexRange offsets_range = bke::curves::per_curve_point_offsets_range(src_points, curve_i);
const OffsetIndices<int> offsets(all_point_offsets.slice(offsets_range));
threaded_slice_fill(src.slice(src_points), offsets, dst.slice(dst_points));
}
});
@ -56,14 +49,14 @@ static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
const bke::CurvesGeometry &dst_curves,
const IndexMask selection,
const Span<int> point_offsets,
const Span<int> all_point_offsets,
const GSpan src,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
using T = decltype(dummy);
duplicate_fillet_point_data(
src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>());
src_curves, dst_curves, selection, all_point_offsets, src.typed<T>(), dst.typed<T>());
});
}
@ -82,7 +75,7 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const IndexRange src_points = points_by_curve[curve_i];
const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i);
const IndexRange offsets_range = bke::curves::per_curve_point_offsets_range(src_points, curve_i);
MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range);
MutableSpan<int> point_counts = point_offsets.drop_back(1);
@ -235,13 +228,13 @@ static void calculate_fillet_positions(const Span<float3> src_positions,
const Span<float> angles,
const Span<float> radii,
const Span<float3> directions,
const Span<int> dst_offsets,
const OffsetIndices<int> dst_offsets,
MutableSpan<float3> dst)
{
const int i_src_last = src_positions.index_range().last();
threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) {
for (const int i_src : range) {
const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
const IndexRange arc = dst_offsets[i_src];
const float3 &src = src_positions[i_src];
if (arc.size() == 1) {
dst[arc.first()] = src;
@ -292,7 +285,7 @@ static void calculate_bezier_handles_bezier_mode(const Span<float3> src_handles_
const Span<float> angles,
const Span<float> radii,
const Span<float3> directions,
const Span<int> dst_offsets,
const OffsetIndices<int> dst_offsets,
const Span<float3> dst_positions,
MutableSpan<float3> dst_handles_l,
MutableSpan<float3> dst_handles_r,
@ -303,7 +296,7 @@ static void calculate_bezier_handles_bezier_mode(const Span<float3> src_handles_
const int i_dst_last = dst_positions.index_range().last();
threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
for (const int i_src : range) {
const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
const IndexRange arc = dst_offsets[i_src];
if (arc.size() == 1) {
dst_handles_l[arc.first()] = src_handles_l[i_src];
dst_handles_r[arc.first()] = src_handles_r[i_src];
@ -354,7 +347,7 @@ static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
const Span<float3> src_handles_r,
const Span<int8_t> src_types_l,
const Span<int8_t> src_types_r,
const Span<int> dst_offsets,
const OffsetIndices<int> dst_offsets,
const Span<float3> dst_positions,
MutableSpan<float3> dst_handles_l,
MutableSpan<float3> dst_handles_r,
@ -364,7 +357,7 @@ static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
const int i_dst_last = dst_positions.index_range().last();
threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
for (const int i_src : range) {
const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
const IndexRange arc = dst_offsets[i_src];
if (arc.size() == 1) {
dst_handles_l[arc.first()] = src_handles_l[i_src];
dst_handles_r[arc.first()] = src_handles_r[i_src];
@ -427,7 +420,7 @@ static bke::CurvesGeometry fillet_curves(
cyclic,
dst_curves.offsets_for_write(),
dst_point_offsets);
const Span<int> point_offsets = dst_point_offsets.as_span();
const Span<int> all_point_offsets = dst_point_offsets.as_span();
dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
@ -463,7 +456,8 @@ static bke::CurvesGeometry fillet_curves(
for (const int curve_i : curve_selection.slice(range)) {
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 offsets_range = bke::curves::per_curve_point_offsets_range(src_points, curve_i);
const OffsetIndices<int> offsets(all_point_offsets.slice(offsets_range));
const IndexRange dst_points = dst_points_by_curve[curve_i];
const Span<float3> src_positions = positions.slice(src_points);
@ -528,8 +522,12 @@ static bke::CurvesGeometry fillet_curves(
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) {
duplicate_fillet_point_data(
src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span);
duplicate_fillet_point_data(src_curves,
dst_curves,
curve_selection,
all_point_offsets,
attribute.src,
attribute.dst.span);
attribute.dst.finish();
}

View File

@ -225,11 +225,10 @@ static void normalize_span(MutableSpan<float3> data)
}
}
static void normalize_curve_point_data(const CurvesGeometry &curves,
const IndexMask curve_selection,
static void normalize_curve_point_data(const IndexMask curve_selection,
const OffsetIndices<int> points_by_curve,
MutableSpan<float3> data)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i_curve : curve_selection) {
normalize_span(data.slice(points_by_curve[i_curve]));
}
@ -252,7 +251,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
src_curves.curves_num());
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
const bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(count_field, dst_offsets.drop_back(1));
@ -269,9 +268,11 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
/* All resampled curves are poly curves. */
dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
VArray<bool> curves_cyclic = src_curves.cyclic();
VArray<int8_t> curve_types = src_curves.curve_types();
Span<float3> evaluated_positions = src_curves.evaluated_positions();
const OffsetIndices evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
const VArray<bool> curves_cyclic = src_curves.cyclic();
const VArray<int8_t> curve_types = src_curves.curve_types();
const Span<float3> evaluated_positions = src_curves.evaluated_positions();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
AttributesForInterpolation attributes;
@ -335,7 +336,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
dst.slice(dst_points));
}
else {
const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
const int evaluated_size = evaluated_points_by_curve.size(i_curve);
evaluated_buffer.clear();
evaluated_buffer.resize(sizeof(T) * evaluated_size);
MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
@ -352,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 src_points = evaluated_points_by_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),
@ -366,11 +367,11 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
if (!attributes.dst_tangents.is_empty()) {
interpolate_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents);
normalize_curve_point_data(sliced_selection, dst_points_by_curve, attributes.dst_tangents);
}
if (!attributes.dst_normals.is_empty()) {
interpolate_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals);
normalize_curve_point_data(sliced_selection, dst_points_by_curve, attributes.dst_normals);
}
/* Fill the default value for non-interpolating attributes that still must be copied. */
@ -413,15 +414,15 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
const fn::Field<bool> &selection_field,
const ResampleCurvesOutputAttributeIDs &output_ids)
{
src_curves.ensure_evaluated_offsets();
bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
const bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range(), nullptr);
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
CurvesGeometry dst_curves(0, src_curves.curves_num());
@ -433,16 +434,16 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
src_curves.curves_num());
/* All resampled curves are poly curves. */
dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
src_curves.ensure_can_interpolate_to_evaluated();
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size();
dst_offsets[i] = src_evaluated_points_by_curve.size(i);
}
});
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
@ -453,8 +454,7 @@ 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();
src_curves.ensure_can_interpolate_to_evaluated();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
const IndexMask sliced_selection = selection.slice(selection_range);
@ -476,7 +476,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 src_points = src_evaluated_points_by_curve[i_curve];
const IndexRange dst_points = dst_points_by_curve[i_curve];
dst.slice(dst_points).copy_from(src.slice(src_points));
}
@ -487,11 +487,11 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
if (!attributes.dst_tangents.is_empty()) {
copy_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents);
normalize_curve_point_data(sliced_selection, dst_points_by_curve, attributes.dst_tangents);
}
if (!attributes.dst_normals.is_empty()) {
copy_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals);
normalize_curve_point_data(sliced_selection, dst_points_by_curve, attributes.dst_normals);
}
/* Fill the default value for non-interpolating attributes that still must be copied. */

View File

@ -11,16 +11,6 @@
namespace blender::geometry {
/**
* Return a range used to retrieve values from an array of values stored per point, but with an
* extra element at the end of each curve. This is useful for offsets within curves, where it is
* convenient to store the first 0 and have the last offset be the total result curve size.
*/
static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index)
{
return {curve_index + points.start(), points.size() + 1};
}
static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<IndexRange> unselected_ranges,
@ -35,7 +25,8 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
const IndexRange src_segments = bke::curves::per_curve_point_offsets_range(src_points,
curve_i);
MutableSpan<int> point_offsets = dst_point_offsets.slice(src_segments);
MutableSpan<int> point_counts = point_offsets.drop_back(1);
@ -76,7 +67,7 @@ template<typename T>
static void subdivide_attribute_linear(const bke::CurvesGeometry &src_curves,
const bke::CurvesGeometry &dst_curves,
const IndexMask selection,
const Span<int> point_offsets,
const Span<int> all_point_offsets,
const Span<T> src,
MutableSpan<T> dst)
{
@ -85,22 +76,21 @@ static void subdivide_attribute_linear(const bke::CurvesGeometry &src_curves,
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
for (const int curve_i : selection.slice(selection_range)) {
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 src_segments = bke::curves::per_curve_point_offsets_range(src_points,
curve_i);
const OffsetIndices<int> curve_offsets = all_point_offsets.slice(src_segments);
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);
threading::parallel_for(curve_src.index_range().drop_back(1), 1024, [&](IndexRange range) {
for (const int i : range) {
const IndexRange segment_points = bke::offsets_to_range(offsets, i);
const IndexRange segment_points = curve_offsets[src_segments[i]];
linear_interpolation(curve_src[i], curve_src[i + 1], curve_dst.slice(segment_points));
}
});
const IndexRange dst_last_segment = dst_points.slice(
bke::offsets_to_range(offsets, src_points.size() - 1));
const IndexRange dst_last_segment = dst_points.slice(curve_offsets[src_points.size() - 1]);
linear_interpolation(curve_src.last(), curve_src.first(), dst.slice(dst_last_segment));
}
});
@ -109,14 +99,14 @@ static void subdivide_attribute_linear(const bke::CurvesGeometry &src_curves,
static void subdivide_attribute_linear(const bke::CurvesGeometry &src_curves,
const bke::CurvesGeometry &dst_curves,
const IndexMask selection,
const Span<int> point_offsets,
const Span<int> all_point_offsets,
const GSpan src,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
using T = decltype(dummy);
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>());
src_curves, dst_curves, selection, all_point_offsets, src.typed<T>(), dst.typed<T>());
});
}
@ -124,7 +114,7 @@ template<typename T>
static void subdivide_attribute_catmull_rom(const bke::CurvesGeometry &src_curves,
const bke::CurvesGeometry &dst_curves,
const IndexMask selection,
const Span<int> point_offsets,
const Span<int> all_point_offsets,
const Span<bool> cyclic,
const Span<T> src,
MutableSpan<T> dst)
@ -134,12 +124,12 @@ static void subdivide_attribute_catmull_rom(const bke::CurvesGeometry &src_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_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
const IndexRange src_segments = bke::curves::per_curve_point_offsets_range(src_points,
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],
point_offsets.slice(src_segments),
all_point_offsets.slice(src_segments),
dst.slice(dst_points));
}
});
@ -148,15 +138,20 @@ static void subdivide_attribute_catmull_rom(const bke::CurvesGeometry &src_curve
static void subdivide_attribute_catmull_rom(const bke::CurvesGeometry &src_curves,
const bke::CurvesGeometry &dst_curves,
const IndexMask selection,
const Span<int> point_offsets,
const Span<int> all_point_offsets,
const Span<bool> cyclic,
const GSpan src,
GMutableSpan dst)
{
attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
using T = decltype(dummy);
subdivide_attribute_catmull_rom(
src_curves, dst_curves, selection, point_offsets, cyclic, src.typed<T>(), dst.typed<T>());
subdivide_attribute_catmull_rom(src_curves,
dst_curves,
selection,
all_point_offsets,
cyclic,
src.typed<T>(),
dst.typed<T>());
});
}
@ -231,7 +226,7 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
const Span<int8_t> src_types_r,
const Span<float3> src_handles_l,
const Span<float3> src_handles_r,
const Span<int> evaluated_offsets,
const OffsetIndices<int> evaluated_offsets,
const bool cyclic,
MutableSpan<float3> dst_positions,
MutableSpan<int8_t> dst_types_l,
@ -241,7 +236,7 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
{
threading::parallel_for(src_positions.index_range().drop_back(1), 512, [&](IndexRange range) {
for (const int segment_i : range) {
const IndexRange segment = bke::offsets_to_range(evaluated_offsets, segment_i);
const IndexRange segment = evaluated_offsets[segment_i];
subdivide_bezier_segment(src_positions[segment_i],
src_handles_r[segment_i],
src_handles_l[segment_i + 1],
@ -260,7 +255,7 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
if (cyclic) {
const int last_index = src_positions.index_range().last();
const IndexRange segment = bke::offsets_to_range(evaluated_offsets, last_index);
const IndexRange segment = evaluated_offsets[last_index];
const HandleType type_prev = HandleType(src_types_r.last());
const HandleType type_next = HandleType(src_types_l.first());
subdivide_bezier_segment(src_positions.last(),
@ -325,9 +320,9 @@ bke::CurvesGeometry subdivide_curves(
*
* Storing the leading zero is unnecessary but makes the array a bit simpler to use by avoiding
* a check for the first segment, and because some existing utilities also use leading zeros. */
Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num());
Array<int> all_point_offset_data(src_curves.points_num() + src_curves.curves_num());
#ifdef DEBUG
dst_point_offsets.fill(-1);
all_point_offset_data.fill(-1);
#endif
calculate_result_offsets(src_curves,
selection,
@ -335,8 +330,8 @@ bke::CurvesGeometry subdivide_curves(
cuts,
cyclic,
dst_curves.offsets_for_write(),
dst_point_offsets);
const Span<int> point_offsets = dst_point_offsets.as_span();
all_point_offset_data);
const Span<int> all_point_offsets(all_point_offset_data);
dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
@ -349,7 +344,7 @@ bke::CurvesGeometry subdivide_curves(
subdivide_attribute_catmull_rom(src_curves,
dst_curves,
selection,
point_offsets,
all_point_offsets,
cyclic,
attribute.src,
attribute.dst.span);
@ -361,7 +356,7 @@ bke::CurvesGeometry subdivide_curves(
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
src_curves, dst_curves, selection, all_point_offsets, attribute.src, attribute.dst.span);
attribute.dst.finish();
}
};
@ -384,15 +379,15 @@ bke::CurvesGeometry subdivide_curves(
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const IndexRange src_points = src_points_by_curve[curve_i];
const IndexRange src_segments = curve_dst_offsets(src_points, curve_i);
const IndexRange src_segments = bke::curves::per_curve_point_offsets_range(src_points,
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),
src_handles_l.slice(src_points),
src_handles_r.slice(src_points),
point_offsets.slice(src_segments),
all_point_offsets.slice(src_segments),
cyclic[curve_i],
dst_positions.slice(dst_points),
dst_types_l.slice(dst_points),
@ -412,7 +407,7 @@ bke::CurvesGeometry subdivide_curves(
"handle_right",
"handle_left"})) {
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
src_curves, dst_curves, selection, all_point_offsets, attribute.src, attribute.dst.span);
attribute.dst.finish();
}
};

View File

@ -107,30 +107,29 @@ static bke::curves::CurvePoint lookup_point_bezier(const Span<int> bezier_offset
/* Find the segment index from the offset mapping. */
const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index);
const int left = offset - bezier_offsets.begin();
const int left = offset - bezier_offsets.begin() - 1;
const int right = left == last_index ? 0 : left + 1;
const int prev_offset = left == 0 ? 0 : bezier_offsets[int64_t(left) - 1];
const int prev_offset = bezier_offsets[left];
const float offset_in_segment = eval_factor + (eval_index - prev_offset);
const int segment_resolution = bezier_offsets[left] - prev_offset;
const int segment_resolution = bezier_offsets[left + 1] - prev_offset;
const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f);
return {{left, right}, parameter};
}
static bke::curves::CurvePoint lookup_point_bezier(const bke::CurvesGeometry &src_curves,
const int64_t curve_index,
const Span<float> accumulated_lengths,
const float sample_length,
const bool cyclic,
const int resolution,
const int num_curve_points)
static bke::curves::CurvePoint lookup_point_bezier(
const bke::CurvesGeometry &src_curves,
const OffsetIndices<int> evaluated_points_by_curve,
const int64_t curve_index,
const Span<float> accumulated_lengths,
const float sample_length,
const bool cyclic,
const int resolution,
const int num_curve_points)
{
if (bke::curves::bezier::has_vector_handles(
num_curve_points,
src_curves.evaluated_points_for_curve(curve_index).size(),
cyclic,
resolution)) {
num_curve_points, evaluated_points_by_curve.size(curve_index), cyclic, resolution)) {
const Span<int> bezier_offsets = src_curves.bezier_evaluated_offsets_for_curve(curve_index);
return lookup_point_bezier(
bezier_offsets, accumulated_lengths, sample_length, cyclic, num_curve_points);
@ -141,14 +140,16 @@ static bke::curves::CurvePoint lookup_point_bezier(const bke::CurvesGeometry &sr
}
}
static bke::curves::CurvePoint lookup_curve_point(const bke::CurvesGeometry &src_curves,
const CurveType curve_type,
const int64_t curve_index,
const Span<float> accumulated_lengths,
const float sample_length,
const bool cyclic,
const int resolution,
const int num_curve_points)
static bke::curves::CurvePoint lookup_curve_point(
const bke::CurvesGeometry &src_curves,
const OffsetIndices<int> evaluated_points_by_curve,
const CurveType curve_type,
const int64_t curve_index,
const Span<float> accumulated_lengths,
const float sample_length,
const bool cyclic,
const int resolution,
const int num_curve_points)
{
if (num_curve_points == 1) {
return {{0, 0}, 0.0f};
@ -160,6 +161,7 @@ static bke::curves::CurvePoint lookup_curve_point(const bke::CurvesGeometry &src
}
else if (curve_type == CURVE_TYPE_BEZIER) {
return lookup_point_bezier(src_curves,
evaluated_points_by_curve,
curve_index,
accumulated_lengths,
sample_length,
@ -173,10 +175,8 @@ static bke::curves::CurvePoint lookup_curve_point(const bke::CurvesGeometry &src
else {
/* Handle evaluated curve. */
BLI_assert(resolution > 0);
return lookup_point_polygonal(accumulated_lengths,
sample_length,
cyclic,
src_curves.evaluated_points_for_curve(curve_index).size());
return lookup_point_polygonal(
accumulated_lengths, sample_length, cyclic, evaluated_points_by_curve.size(curve_index));
}
}
@ -762,15 +762,15 @@ static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves,
MutableSpan<bke::AttributeTransferData> transfer_attributes)
{
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_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 src_evaluated_points = src_evaluated_points_by_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),
dst_positions,
src_ranges[curve_i],
@ -789,8 +789,7 @@ static void trim_evaluated_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)) {
/* Interpolate onto the evaluated point domain and sample the evaluated domain. */
const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i);
GArray<> evaluated_data(CPPType::get<T>(), src_evaluated_points.size());
GArray<> evaluated_data(CPPType::get<T>(), src_evaluated_points_by_curve.size(curve_i));
GMutableSpan evaluated_span = evaluated_data.as_mutable_span();
src_curves.interpolate_to_evaluated(
curve_i, attribute.src.slice(src_points_by_curve[curve_i]), evaluated_span);
@ -836,6 +835,7 @@ static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves,
MutableSpan<bke::curves::IndexRangeCyclic> src_ranges)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const OffsetIndices evaluated_points_by_curve = curves.evaluated_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();
@ -848,7 +848,7 @@ static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves,
int point_count;
if (curve_type == CURVE_TYPE_NURBS) {
dst_curve_types[curve_i] = CURVE_TYPE_POLY;
point_count = curves.evaluated_points_for_curve(curve_i).size();
point_count = evaluated_points_by_curve.size(curve_i);
}
else {
dst_curve_types[curve_i] = curve_type;
@ -885,6 +885,7 @@ static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves,
}
start_points[curve_i] = lookup_curve_point(curves,
evaluated_points_by_curve,
curve_type,
curve_i,
lengths,
@ -920,6 +921,7 @@ static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves,
else {
/* General case. */
end_points[curve_i] = lookup_curve_point(curves,
evaluated_points_by_curve,
curve_type,
curve_i,
lengths,
@ -968,7 +970,6 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
Array<bke::curves::IndexRangeCyclic, 12> src_ranges(src_curves.curves_num());
if (src_curves.has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS})) {
src_curves.ensure_evaluated_offsets();
if (src_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
src_curves.evaluated_positions();
}
@ -1059,7 +1060,6 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
};
auto trim_evaluated = [&](const IndexMask selection) {
/* Ensure evaluated positions are available. */
src_curves.ensure_evaluated_offsets();
src_curves.evaluated_positions();
trim_evaluated_curves(src_curves,
dst_curves,

View File

@ -43,15 +43,16 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
static meshintersect::CDT_result<double> do_cdt(const bke::CurvesGeometry &curves,
const CDT_output_type output_type)
{
const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
const Span<float3> positions = curves.evaluated_positions();
meshintersect::CDT_input<double> input;
input.need_ids = false;
input.vert.reinitialize(curves.evaluated_points_num());
input.vert.reinitialize(points_by_curve.total_size());
input.face.reinitialize(curves.curves_num());
Span<float3> positions = curves.evaluated_positions();
for (const int i_curve : curves.curves_range()) {
const IndexRange points = curves.evaluated_points_for_curve(i_curve);
const IndexRange points = points_by_curve[i_curve];
for (const int i : points) {
input.vert[i] = double2(positions[i].x, positions[i].y);

View File

@ -313,6 +313,7 @@ class SampleCurveFunction : public mf::MultiFunction {
}
const OffsetIndices points_by_curve = curves.points_by_curve();
const OffsetIndices evaluated_points_by_curve = curves.evaluated_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();
@ -354,7 +355,7 @@ class SampleCurveFunction : public mf::MultiFunction {
sample_indices_and_factors_to_compressed(
accumulated_lengths, lengths, length_mode_, mask, indices, factors);
const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i);
const IndexRange evaluated_points = evaluated_points_by_curve[curve_i];
if (!sampled_positions.is_empty()) {
length_parameterize::interpolate_to_masked<float3>(
evaluated_positions.slice(evaluated_points),
@ -381,7 +382,7 @@ class SampleCurveFunction : public mf::MultiFunction {
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());
src_evaluated_values.reinitialize(evaluated_points.size());
curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
using T = decltype(dummy);

View File

@ -35,7 +35,7 @@ static Array<float> accumulated_lengths_curve_domain(const bke::CurvesGeometry &
curves.ensure_evaluated_lengths();
Array<float> lengths(curves.curves_num());
VArray<bool> cyclic = curves.cyclic();
const VArray<bool> cyclic = curves.cyclic();
float length = 0.0f;
for (const int i : curves.curves_range()) {
lengths[i] = length;
@ -86,7 +86,7 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves)
case CURVE_TYPE_BEZIER: {
const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve);
for (const int i : IndexRange(points.size()).drop_back(1)) {
lengths[i + 1] = evaluated_lengths[offsets[i] - 1];
lengths[i + 1] = evaluated_lengths[offsets[i + 1] - 1];
}
break;
}
@ -110,7 +110,7 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry
const IndexMask /*mask*/,
const eAttrDomain domain)
{
VArray<bool> cyclic = curves.cyclic();
const VArray<bool> cyclic = curves.cyclic();
if (domain == ATTR_DOMAIN_POINT) {
Array<float> result = curve_length_point_domain(curves);

View File

@ -16,6 +16,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 OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolutions = curves.resolution();
const VArray<bool> cyclic = curves.cyclic();
@ -28,7 +29,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 = points_by_curve[i_curve];
const IndexRange evaluated_points = curves.evaluated_points_for_curve(i_curve);
const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
MutableSpan<float3> curve_tangents = results.as_mutable_span().slice(points);
@ -49,7 +50,7 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve
curve_tangents.first() = tangents.first();
const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve);
for (const int i : IndexRange(points.size()).drop_front(1)) {
curve_tangents[i] = tangents[offsets[i - 1]];
curve_tangents[i] = tangents[offsets[i]];
}
break;
}