Curves: Use copied original data for invalid NURBS curves
NURBS curves can be invalid when the order is less than the number of points, or in a few other situations. Currently the evaluated data of an invalid NURBS curve is empty. This is inconvenient because it requires checking for empty curves when it otherwise wouldn't be necessary. This patch replaces that fallback with copying the original data to the evaluated points. This makes conceptual sense too, as if the curve couldn't be evaluated-- which wouldn't necessarily delete it. Usually the UI protects against this happening, but it's currently possible to create an invalid curve with some operations like the delete geometry node. Differential Revision: https://developer.blender.org/D14837
This commit is contained in:
parent
7dc94155f6
commit
2d80f814cc
Notes:
blender-bot
2023-02-14 07:31:32 +01:00
Referenced by issue #102144, Regression: Specific Geometry Nodes tree (involving curve nodes) crashes with specific input mesh Referenced by issue #101160, Regression: deleting a point in a (cyclic) nurbs curve permanently converts to poly, subdividing as well Referenced by issue #98917, Regression: Curve to Mesh node crashes blender if there is a single vertice curve Referenced by issue #98661, 3.2: Potential candidates for corrective releases
|
@ -48,6 +48,13 @@ struct BasisCache {
|
|||
* In other words, the index of the first control point that influences this evaluated point.
|
||||
*/
|
||||
Vector<int> start_indices;
|
||||
|
||||
/**
|
||||
* The result of #check_valid_size_and_order, to avoid retrieving its inputs later on.
|
||||
* If this is true, the data above will be invalid, and original data should be copied
|
||||
* to the evaluated result.
|
||||
*/
|
||||
bool invalid = false;
|
||||
};
|
||||
|
||||
} // namespace curves::nurbs
|
||||
|
@ -760,7 +767,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index,
|
|||
BLI_assert(cyclic == this->cyclic()[curve_index]);
|
||||
const IndexRange points = this->evaluated_points_for_curve(curve_index);
|
||||
const int start = points.start() + curve_index;
|
||||
return {start, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)};
|
||||
return {start, curves::curve_segment_size(points.size(), cyclic)};
|
||||
}
|
||||
|
||||
inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index,
|
||||
|
@ -775,8 +782,7 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in
|
|||
const bool cyclic) const
|
||||
{
|
||||
const Span<float> lengths = this->evaluated_lengths_for_curve(curve_index, cyclic);
|
||||
/* Check for curves that have no evaluated segments. */
|
||||
return lengths.is_empty() ? 0.0f : lengths.last();
|
||||
return lengths.last();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -36,7 +36,7 @@ int calculate_evaluated_size(const int points_num,
|
|||
const KnotsMode knots_mode)
|
||||
{
|
||||
if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) {
|
||||
return 0;
|
||||
return points_num;
|
||||
}
|
||||
return resolution * curve_segment_size(points_num, cyclic);
|
||||
}
|
||||
|
@ -232,8 +232,12 @@ void interpolate_to_evaluated(const BasisCache &basis_cache,
|
|||
const GSpan src,
|
||||
GMutableSpan dst)
|
||||
{
|
||||
BLI_assert(dst.size() == basis_cache.start_indices.size());
|
||||
if (basis_cache.invalid) {
|
||||
dst.copy_from(src);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(dst.size() == basis_cache.start_indices.size());
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
|
|
|
@ -577,6 +577,11 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
|
|||
const bool is_cyclic = cyclic[curve_index];
|
||||
const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
|
||||
|
||||
if (!curves::nurbs::check_valid_size_and_order(points.size(), order, is_cyclic, mode)) {
|
||||
basis_caches[curve_index].invalid = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic);
|
||||
Array<float> knots(knots_size);
|
||||
curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots);
|
||||
|
@ -696,9 +701,6 @@ 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);
|
||||
if (UNLIKELY(evaluated_points.is_empty())) {
|
||||
continue;
|
||||
}
|
||||
curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points),
|
||||
cyclic[curve_index],
|
||||
tangents.slice(evaluated_points));
|
||||
|
@ -773,9 +775,6 @@ Span<float3> CurvesGeometry::evaluated_normals() const
|
|||
|
||||
for (const int curve_index : curves_range) {
|
||||
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
||||
if (UNLIKELY(evaluated_points.is_empty())) {
|
||||
continue;
|
||||
}
|
||||
switch (normal_mode[curve_index]) {
|
||||
case NORMAL_MODE_Z_UP:
|
||||
curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points),
|
||||
|
@ -916,9 +915,6 @@ void CurvesGeometry::ensure_evaluated_lengths() const
|
|||
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);
|
||||
if (UNLIKELY(evaluated_points.is_empty())) {
|
||||
continue;
|
||||
}
|
||||
const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
|
||||
length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points),
|
||||
cyclic,
|
||||
|
|
Loading…
Reference in New Issue