Fix #138399: Key handle jumping when scaling to 0
When moving a handle so its length is 0, the other handle was automatically set to 0 as well. Cancelling the action didn't move the other handle back to 0, but set its length to 1. The reason for that was that the code tries to maintain the relation between the two handles, but that would lead to a divide by 0 when either of the handles has a length of 0. So it would set the handle length to 1 in that case. The fix is to ensure that the length of a handle is never 0. This is done by clamping it at the key position with a threshold of 0.001 and an extra floating point step to support large floating point values. This should not affect animation in any way. This was discussed in the animation & rigging module meeting https://devtalk.blender.org/t/2025-06-26-animation-rigging-module-meeting/41272#p-153605-patches-review-decision-time-5 Co-authored by Nathan Vegdahl Pull Request: https://projects.blender.org/blender/blender/pulls/141029
This commit is contained in:
committed by
Christoph Lendenfeld
parent
7b403a2b10
commit
d7dd45e4da
@@ -1206,9 +1206,14 @@ void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag)
|
||||
next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last);
|
||||
}
|
||||
|
||||
/* Clamp timing of handles to be on either side of beztriple. */
|
||||
CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]);
|
||||
CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]);
|
||||
/* Clamp timing of handles to be on either side of beztriple. The threshold with
|
||||
* increment/decrement ulp ensures that the handle length doesn't reach 0 at which point
|
||||
* there would be no way to ensure that handles stay aligned. This adds an issue where if a
|
||||
* handle is scaled to 0, the other side is set to be horizontal.
|
||||
* See #141029 for more info. */
|
||||
const float threshold = 0.001;
|
||||
CLAMP_MAX(bezt->vec[0][0], decrement_ulp(bezt->vec[1][0] - threshold));
|
||||
CLAMP_MIN(bezt->vec[2][0], increment_ulp(bezt->vec[1][0] + threshold));
|
||||
|
||||
/* Calculate auto-handles. */
|
||||
BKE_nurb_handle_calc_ex(bezt, prev, next, handle_sel_flag, true, fcu->auto_smoothing);
|
||||
|
||||
@@ -163,6 +163,26 @@ MINLINE uint ulp_diff_ff(float a, float b);
|
||||
MINLINE int compare_ff_relative(float a, float b, float max_diff, int max_ulps);
|
||||
MINLINE bool compare_threshold_relative(float value1, float value2, float thresh);
|
||||
|
||||
/**
|
||||
* Increment the given float to the next representable floating point value in
|
||||
* the positive direction.
|
||||
*
|
||||
* Infinities and NaNs are left untouched. Subnormal numbers are handled
|
||||
* correctly, as is crossing zero (i.e. 0 and -0 are considered a single value,
|
||||
* and progressing past zero continues on to the positive numbers).
|
||||
*/
|
||||
MINLINE float increment_ulp(float value);
|
||||
|
||||
/**
|
||||
* Decrement the given float to the next representable floating point value in
|
||||
* the negative direction.
|
||||
*
|
||||
* Infinities and NaNs are left untouched. Subnormal numbers are handled
|
||||
* correctly, as is zero (i.e. 0 and -0 are considered a single value, and
|
||||
* progressing past zero continues on to the negative numbers).
|
||||
*/
|
||||
MINLINE float decrement_ulp(float value);
|
||||
|
||||
MINLINE float signf(float f);
|
||||
MINLINE int signum_i_ex(float a, float eps);
|
||||
MINLINE int signum_i(float a);
|
||||
|
||||
@@ -512,6 +512,56 @@ MINLINE bool compare_threshold_relative(const float value1, const float value2,
|
||||
return abs_diff > thresh * fabsf(value2);
|
||||
}
|
||||
|
||||
MINLINE float increment_ulp(const float value)
|
||||
{
|
||||
if (!isfinite(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
union {
|
||||
float f;
|
||||
uint i;
|
||||
} v;
|
||||
v.f = value;
|
||||
|
||||
if (v.f > 0.0f) {
|
||||
v.i += 1;
|
||||
}
|
||||
else if (v.f < -0.0f) {
|
||||
v.i -= 1;
|
||||
}
|
||||
else {
|
||||
v.i = 0x00000001;
|
||||
}
|
||||
|
||||
return v.f;
|
||||
}
|
||||
|
||||
MINLINE float decrement_ulp(const float value)
|
||||
{
|
||||
if (!isfinite(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
union {
|
||||
float f;
|
||||
uint i;
|
||||
} v;
|
||||
v.f = value;
|
||||
|
||||
if (v.f > 0.0f) {
|
||||
v.i -= 1;
|
||||
}
|
||||
else if (v.f < -0.0f) {
|
||||
v.i += 1;
|
||||
}
|
||||
else {
|
||||
v.i = 0x80000001;
|
||||
}
|
||||
|
||||
return v.f;
|
||||
}
|
||||
|
||||
MINLINE float signf(float f)
|
||||
{
|
||||
return (f < 0.0f) ? -1.0f : 1.0f;
|
||||
|
||||
Reference in New Issue
Block a user