diff options
author | Hans Goudey <h.goudey@me.com> | 2022-04-01 16:11:58 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-04-01 16:12:41 +0300 |
commit | 00ba51d37bf5b152176409b393eafbb0ad9333e6 (patch) | |
tree | 499a1503c59f3558101bba52cef10d64c39a57fd /source/blender/blenkernel/intern/curve_bezier.cc | |
parent | a250d3d1b7d8d497c21a1ef845e64f07e68beda9 (diff) |
Geometry Nodes: Port set handle nodes to new data-block
This commit ports the "Set Handle Positions" and "Set Hanle Type"
nodes to use the new curves data-block. The nodes become simpler
and likely much faster too, though they're usually not the bottleneck
anyway.
Most of the code is ported from `BezierSpline` directly. The majority
of the complexity comes from the interaction between different
automatically calculated handle types. In comparison `BezierSpline`,
the calculation of auto handles is done eagerly-- mostly because it's
simpler. Eventually lazy calculation might be good to add.
Differential Revision: https://developer.blender.org/D14464
Diffstat (limited to 'source/blender/blenkernel/intern/curve_bezier.cc')
-rw-r--r-- | source/blender/blenkernel/intern/curve_bezier.cc | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc index 0d3bb2e3a7d..30a5869c976 100644 --- a/source/blender/blenkernel/intern/curve_bezier.cc +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -58,6 +58,131 @@ void calculate_evaluated_offsets(const Span<int8_t> handle_types_left, evaluated_offsets.last() = offset; } +static float3 calculate_aligned_handle(const float3 &position, + const float3 &other_handle, + const float3 &aligned_handle) +{ + /* Keep track of the old length of the opposite handle. */ + const float length = math::distance(aligned_handle, position); + /* Set the other handle to directly opposite from the current handle. */ + const float3 dir = math::normalize(other_handle - position); + return position - dir * length; +} + +static void calculate_point_handles(const HandleType type_left, + const HandleType type_right, + const float3 position, + const float3 prev_position, + const float3 next_position, + float3 &left, + float3 &right) +{ + if (ELEM(BEZIER_HANDLE_AUTO, type_left, type_right)) { + const float3 prev_diff = position - prev_position; + const float3 next_diff = next_position - position; + float prev_len = math::length(prev_diff); + float next_len = math::length(next_diff); + if (prev_len == 0.0f) { + prev_len = 1.0f; + } + if (next_len == 0.0f) { + next_len = 1.0f; + } + const float3 dir = next_diff / next_len + prev_diff / prev_len; + + /* This magic number is unfortunate, but comes from elsewhere in Blender. */ + const float len = math::length(dir) * 2.5614f; + if (len != 0.0f) { + if (type_left == BEZIER_HANDLE_AUTO) { + const float prev_len_clamped = std::min(prev_len, next_len * 5.0f); + left = position + dir * -(prev_len_clamped / len); + } + if (type_right == BEZIER_HANDLE_AUTO) { + const float next_len_clamped = std::min(next_len, prev_len * 5.0f); + right = position + dir * (next_len_clamped / len); + } + } + } + + if (type_left == BEZIER_HANDLE_VECTOR) { + left = math::interpolate(position, prev_position, 1.0f / 3.0f); + } + + if (type_right == BEZIER_HANDLE_VECTOR) { + right = math::interpolate(position, next_position, 1.0f / 3.0f); + } + + /* When one of the handles is "aligned" handle, it must be aligned with the other, i.e. point in + * the opposite direction. Don't handle the case of two aligned handles, because code elsewhere + * should keep the pair consistent, and the relative locations aren't affected by other points + * anyway. */ + if (type_left == BEZIER_HANDLE_ALIGN && type_right != BEZIER_HANDLE_ALIGN) { + left = calculate_aligned_handle(position, right, left); + } + else if (type_left != BEZIER_HANDLE_ALIGN && type_right == BEZIER_HANDLE_ALIGN) { + right = calculate_aligned_handle(position, left, right); + } +} + +void set_handle_position(const float3 &position, + const HandleType type, + const HandleType type_other, + const float3 &new_handle, + float3 &handle, + float3 &handle_other) +{ + /* Don't bother when the handle positions are calculated automatically anyway. */ + if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) { + return; + } + + handle = new_handle; + if (type_other == BEZIER_HANDLE_ALIGN) { + handle_other = calculate_aligned_handle(position, handle, handle_other); + } +} + +void calculate_auto_handles(const bool cyclic, + const Span<int8_t> types_left, + const Span<int8_t> types_right, + const Span<float3> positions, + MutableSpan<float3> positions_left, + MutableSpan<float3> positions_right) +{ + const int points_num = positions.size(); + if (points_num == 1) { + return; + } + + calculate_point_handles(HandleType(types_left.first()), + HandleType(types_right.first()), + positions.first(), + cyclic ? positions.last() : 2.0f * positions.first() - positions[1], + positions[1], + positions_left.first(), + positions_right.first()); + + threading::parallel_for(IndexRange(1, points_num - 2), 1024, [&](IndexRange range) { + for (const int i : range) { + calculate_point_handles(HandleType(types_left[i]), + HandleType(types_right[i]), + positions[i], + positions[i - 1], + positions[i + 1], + positions_left[i], + positions_right[i]); + } + }); + + calculate_point_handles(HandleType(types_left.last()), + HandleType(types_right.last()), + positions.last(), + positions.last(1), + cyclic ? positions.first() : 2.0f * positions.last() - positions.last(1), + positions_left.last(), + positions_right.last()); +} + void evaluate_segment(const float3 &point_0, const float3 &point_1, const float3 &point_2, |