diff options
author | Hans Goudey <h.goudey@me.com> | 2021-05-14 19:26:42 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-05-14 19:26:42 +0300 |
commit | db59f0b943acd684b2c571e5ef30a4c53e6ab5c4 (patch) | |
tree | 54c205bb93d0f6dc05d2126e4fe7b18185dfce9a /source/blender | |
parent | 7178e541222038bd27cde4491c747d27ef614301 (diff) |
Fix T88262: Curve to mesh crash with vector last segment
The code incorrectly used the size of the second to last segment rather
than the last segment's size. That was a problem when the last segment
is a vector segment but the second to last isn't.
I also used the opportunity to slightly refactor the control point
offsets cache, making it one longer so it also contains information
about the size of the last segment, simplifying other code.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/intern/spline_bezier.cc | 46 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 2 |
2 files changed, 24 insertions, 24 deletions
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 0bc6e71fa4d..ba0f33e0093 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -258,9 +258,13 @@ bool BezierSpline::point_is_sharp(const int index) const bool BezierSpline::segment_is_vector(const int index) const { if (index == this->size() - 1) { - BLI_assert(is_cyclic_); - return handle_types_right_.last() == HandleType::Vector && - handle_types_left_.first() == HandleType::Vector; + if (is_cyclic_) { + return handle_types_right_.last() == HandleType::Vector && + handle_types_left_.first() == HandleType::Vector; + } + /* There is actually no segment in this case, but it's nice to avoid + * having a special case for the last segment in calling code. */ + return true; } return handle_types_right_[index] == HandleType::Vector && handle_types_left_[index + 1] == HandleType::Vector; @@ -279,15 +283,8 @@ void BezierSpline::mark_cache_invalid() int BezierSpline::evaluated_points_size() const { - const int points_len = this->size(); - BLI_assert(points_len > 0); - - const int last_offset = this->control_point_offsets().last(); - if (is_cyclic_ && points_len > 1) { - return last_offset + (this->segment_is_vector(points_len - 1) ? 1 : resolution_); - } - - return last_offset + 1; + BLI_assert(this->size() > 0); + return this->control_point_offsets().last(); } /** @@ -358,8 +355,12 @@ void BezierSpline::evaluate_bezier_segment(const int index, /** * Returns access to a cache of offsets into the evaluated point array for each control point. - * This is important because while most control point edges generate the number of edges specified - * by the resolution, vector segments only generate one edge. + * While most control point edges generate the number of edges specified by the resolution, vector + * segments only generate one edge. + * + * \note The length of the result is one greater than the number of points, so that the last item + * is the total number of evaluated points. This is useful to avoid recalculating the size of the + * last segment everywhere. */ Span<int> BezierSpline::control_point_offsets() const { @@ -373,12 +374,12 @@ Span<int> BezierSpline::control_point_offsets() const } const int points_len = this->size(); - offset_cache_.resize(points_len); + offset_cache_.resize(points_len + 1); MutableSpan<int> offsets = offset_cache_; int offset = 0; - for (const int i : IndexRange(points_len - 1)) { + for (const int i : IndexRange(points_len)) { offsets[i] = offset; offset += this->segment_is_vector(i) ? 1 : resolution_; } @@ -400,7 +401,7 @@ static void calculate_mappings_linear_resolution(Span<int> offsets, } const int grain_size = std::max(2048 / resolution, 1); - blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { + parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { for (const int i_control_point : range) { const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point]; const float segment_len_inv = 1.0f / segment_len; @@ -411,7 +412,7 @@ static void calculate_mappings_linear_resolution(Span<int> offsets, }); if (is_cyclic) { - const int last_segment_len = offsets[size - 1] - offsets[size - 2]; + const int last_segment_len = offsets[size] - offsets[size - 1]; const float last_segment_len_inv = 1.0f / last_segment_len; for (const int i : IndexRange(last_segment_len)) { r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv; @@ -471,25 +472,24 @@ Span<float3> BezierSpline::evaluated_positions() const this->ensure_auto_handles(); + const int size = this->size(); const int eval_size = this->evaluated_points_size(); evaluated_position_cache_.resize(eval_size); MutableSpan<float3> positions = evaluated_position_cache_; Span<int> offsets = this->control_point_offsets(); - BLI_assert(offsets.last() <= eval_size); const int grain_size = std::max(512 / resolution_, 1); - blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) { + parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { for (const int i : range) { this->evaluate_bezier_segment( i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); } }); - - const int i_last = this->size() - 1; if (is_cyclic_) { - this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), resolution_)); + this->evaluate_bezier_segment( + size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1])); } else { /* Since evaluating the bezier segment doesn't add the final point, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index da275ce0521..f0effdc71f6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -198,7 +198,7 @@ static void spline_extrude_to_mesh_data(const Spline &spline, if (profile_spline.type() == Spline::Type::Bezier) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline); Span<int> control_point_offsets = bezier_spline.control_point_offsets(); - for (const int i : control_point_offsets.index_range()) { + for (const int i : IndexRange(bezier_spline.size())) { if (bezier_spline.point_is_sharp(i)) { mark_edges_sharp(r_edges.slice( spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len)); |