Fix #106927: Crash when removing handle position attribute

Bezier curve position evaluation expects the handle position attributes
to exist and doesn't handle the case where they don't. Swith to using
a utility function to evaluate each curve type so Bezier evaluation can
stop early in that case.
This commit is contained in:
Hans Goudey
2023-04-16 21:33:28 -04:00
parent 2fade47a9d
commit 7bd7043a74

View File

@@ -617,65 +617,80 @@ Span<float3> CurvesGeometry::evaluated_positions() const
[&](Vector<float3> &r_data) { r_data.clear_and_shrink(); });
return this->positions();
}
this->ensure_nurbs_basis_cache();
runtime.evaluated_position_cache.ensure([&](Vector<float3> &r_data) {
r_data.resize(this->evaluated_points_num());
MutableSpan<float3> evaluated_positions = r_data;
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<int> resolution = this->resolution();
const Span<float3> positions = this->positions();
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_offsets = runtime.evaluated_offsets_cache.data().all_bezier_offsets;
const VArray<int8_t> nurbs_orders = this->nurbs_orders();
const Span<float> nurbs_weights = this->nurbs_weights();
const Span<curves::nurbs::BasisCache> nurbs_basis_cache = runtime.nurbs_basis_cache.data();
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 = evaluated_points_by_curve[curve_index];
switch (types[curve_index]) {
case CURVE_TYPE_CATMULL_ROM:
curves::catmull_rom::interpolate_to_evaluated(
positions.slice(points),
cyclic[curve_index],
resolution[curve_index],
evaluated_positions.slice(evaluated_points));
break;
case CURVE_TYPE_POLY:
evaluated_positions.slice(evaluated_points).copy_from(positions.slice(points));
break;
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),
all_bezier_offsets.slice(offsets),
evaluated_positions.slice(evaluated_points));
break;
}
case CURVE_TYPE_NURBS:
curves::nurbs::interpolate_to_evaluated(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;
auto evaluate_catmull = [&](const IndexMask selection) {
const VArray<bool> cyclic = this->cyclic();
const VArray<int> resolution = this->resolution();
threading::parallel_for(selection.index_range(), 128, [&](const IndexRange range) {
for (const int curve_index : selection.slice(range)) {
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
curves::catmull_rom::interpolate_to_evaluated(
positions.slice(points),
cyclic[curve_index],
resolution[curve_index],
evaluated_positions.slice(evaluated_points));
}
});
};
auto evaluate_poly = [&](const IndexMask selection) {
curves::copy_point_data(
points_by_curve, evaluated_points_by_curve, selection, positions, evaluated_positions);
};
auto evaluate_bezier = [&](const IndexMask selection) {
const Span<float3> handle_positions_left = this->handle_positions_left();
const Span<float3> handle_positions_right = this->handle_positions_right();
if (handle_positions_left.is_empty() || handle_positions_right.is_empty()) {
curves::fill_points(evaluated_points_by_curve, selection, float3(0), evaluated_positions);
return;
}
});
const Span<int> all_bezier_offsets =
runtime.evaluated_offsets_cache.data().all_bezier_offsets;
threading::parallel_for(selection.index_range(), 128, [&](const IndexRange range) {
for (const int curve_index : selection.slice(range)) {
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
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),
all_bezier_offsets.slice(offsets),
evaluated_positions.slice(evaluated_points));
}
});
};
auto evaluate_nurbs = [&](const IndexMask selection) {
this->ensure_nurbs_basis_cache();
const VArray<int8_t> nurbs_orders = this->nurbs_orders();
const Span<float> nurbs_weights = this->nurbs_weights();
const Span<curves::nurbs::BasisCache> nurbs_basis_cache = runtime.nurbs_basis_cache.data();
threading::parallel_for(selection.index_range(), 128, [&](const IndexRange range) {
for (const int curve_index : selection.slice(range)) {
const IndexRange points = points_by_curve[curve_index];
const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
curves::nurbs::interpolate_to_evaluated(nurbs_basis_cache[curve_index],
nurbs_orders[curve_index],
nurbs_weights.slice_safe(points),
positions.slice(points),
evaluated_positions.slice(evaluated_points));
}
});
};
curves::foreach_curve_by_type(this->curve_types(),
this->curve_type_counts(),
this->curves_range(),
evaluate_catmull,
evaluate_poly,
evaluate_bezier,
evaluate_nurbs);
});
return runtime.evaluated_position_cache.data();
}