Cleanup: simplify sin_cos_from_fraction
Multiply numerator and denominator by 8 to split circle into octants. Use symmetry and negation to increase precision.
This commit is contained in:
parent
4a11c0aabb
commit
77c867a5ee
Notes:
blender-bot
2024-04-26 15:51:50 +02:00
Referenced by issue #121129, Regression: Crash on `bmesh.ops.create_uvsphere` if underspecified
|
@ -915,107 +915,63 @@ float tri_to_quat(float q[4], const float a[3], const float b[3], const float c[
|
|||
return len;
|
||||
}
|
||||
|
||||
void sin_cos_from_fraction(int numerator, const int denominator, float *r_sin, float *r_cos)
|
||||
void sin_cos_from_fraction(int numerator, int denominator, float *r_sin, float *r_cos)
|
||||
{
|
||||
/* By default, creating an circle from an integer: calling #sinf & #cosf on the fraction doesn't
|
||||
* create symmetrical values (because of float imprecision).
|
||||
/* By default, creating a circle from an integer: calling #sinf & #cosf on the fraction doesn't
|
||||
* create symmetrical values (because floats can't represent Pi exactly).
|
||||
* Resolve this when the rotation is calculated from a fraction by mapping the `numerator`
|
||||
* to lower values so X/Y values for points around a circle are exactly symmetrical, see T87779.
|
||||
*
|
||||
* - Numbers divisible by 4 are mapped to the lower 8th (8 axis symmetry).
|
||||
* - Even numbers are mapped to the lower quarter (4 axis symmetry).
|
||||
* - Odd numbers are mapped to the lower half (1 axis symmetry).
|
||||
* Multiply both the `numerator` and `denominator` by eight to ensure we can divide the circle
|
||||
* into 8 octants. For each octant, we then use symmetry and negation to bring the `numerator`
|
||||
* closer to the origin where precision is highest.
|
||||
*
|
||||
* Once the values are calculated, the are mapped back to their position in the circle
|
||||
* using negation & swapping values. */
|
||||
* Cases 2, 4, 5 and 7, use the trigonometric identity sin(-x) == -sin(x).
|
||||
* Cases 1, 2, 5 and 6, swap the pointers `r_sin` and `r_cos`.
|
||||
*/
|
||||
BLI_assert(0 <= numerator);
|
||||
BLI_assert(numerator <= denominator);
|
||||
BLI_assert(denominator > 0);
|
||||
|
||||
BLI_assert((numerator <= denominator) && (denominator > 0));
|
||||
enum { NEGATE_SIN_BIT = 0, NEGATE_COS_BIT = 1, SWAP_SIN_COS_BIT = 2 };
|
||||
enum {
|
||||
NEGATE_SIN = (1 << NEGATE_SIN_BIT),
|
||||
NEGATE_COS = (1 << NEGATE_COS_BIT),
|
||||
SWAP_SIN_COS = (1 << SWAP_SIN_COS_BIT),
|
||||
} xform = 0;
|
||||
if ((denominator & 3) == 0) {
|
||||
/* The denominator divides by 4, determine the quadrant then further refine the upper 8th. */
|
||||
const int denominator_4 = denominator / 4;
|
||||
if (numerator < denominator_4) {
|
||||
/* Fall through. */
|
||||
}
|
||||
else {
|
||||
if (numerator < denominator_4 * 2) {
|
||||
numerator -= denominator_4;
|
||||
xform = NEGATE_SIN | SWAP_SIN_COS;
|
||||
}
|
||||
else if (numerator == denominator_4 * 2) {
|
||||
numerator = 0;
|
||||
xform = NEGATE_COS;
|
||||
}
|
||||
else if (numerator < denominator_4 * 3) {
|
||||
numerator -= denominator_4 * 2;
|
||||
xform = NEGATE_SIN | NEGATE_COS;
|
||||
}
|
||||
else if (numerator == denominator_4 * 3) {
|
||||
numerator = 0;
|
||||
xform = NEGATE_COS | SWAP_SIN_COS;
|
||||
}
|
||||
else {
|
||||
numerator -= denominator_4 * 3;
|
||||
xform = NEGATE_COS | SWAP_SIN_COS;
|
||||
}
|
||||
}
|
||||
/* Further increase accuracy by using the range of the upper 8th. */
|
||||
const int numerator_test = denominator_4 - numerator;
|
||||
if (numerator_test < numerator) {
|
||||
numerator = numerator_test;
|
||||
xform ^= SWAP_SIN_COS;
|
||||
/* Swap #NEGATE_SIN, #NEGATE_COS flags. */
|
||||
xform = (xform & (uint)(~(NEGATE_SIN | NEGATE_COS))) |
|
||||
(((xform & NEGATE_SIN) >> NEGATE_SIN_BIT) << NEGATE_COS_BIT) |
|
||||
(((xform & NEGATE_COS) >> NEGATE_COS_BIT) << NEGATE_SIN_BIT);
|
||||
}
|
||||
}
|
||||
else if ((denominator & 1) == 0) {
|
||||
/* The denominator divides by 2, determine the quadrant then further refine the upper 4th. */
|
||||
const int denominator_2 = denominator / 2;
|
||||
if (numerator < denominator_2) {
|
||||
/* Fall through. */
|
||||
}
|
||||
else if (numerator == denominator_2) {
|
||||
numerator = 0;
|
||||
xform = NEGATE_COS;
|
||||
}
|
||||
else {
|
||||
numerator -= denominator_2;
|
||||
xform = NEGATE_SIN | NEGATE_COS;
|
||||
}
|
||||
/* Further increase accuracy by using the range of the upper 4th. */
|
||||
const int numerator_test = denominator_2 - numerator;
|
||||
if (numerator_test < numerator) {
|
||||
numerator = numerator_test;
|
||||
xform ^= NEGATE_COS;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* The denominator is an odd number, only refine the upper half. */
|
||||
const int numerator_test = denominator - numerator;
|
||||
if (numerator_test < numerator) {
|
||||
numerator = numerator_test;
|
||||
xform ^= NEGATE_SIN;
|
||||
}
|
||||
numerator *= 8; /* Multiply numerator the same as denominator. */
|
||||
const int octant = numerator / denominator; /* Determine the octant. */
|
||||
denominator *= 8; /* Ensure denominator is a multiple of eight. */
|
||||
float cos_sign = 1.0f; /* Either 1.0f or -1.0f. */
|
||||
|
||||
switch (octant) {
|
||||
case 0:
|
||||
/* Primary octant, nothing to do. */
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
numerator = (denominator / 4) - numerator;
|
||||
SWAP(float *, r_sin, r_cos);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
numerator = (denominator / 2) - numerator;
|
||||
cos_sign = -1.0f;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
numerator = numerator - (denominator * 3 / 4);
|
||||
SWAP(float *, r_sin, r_cos);
|
||||
cos_sign = -1.0f;
|
||||
break;
|
||||
case 7:
|
||||
numerator = numerator - denominator;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
const float phi = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator);
|
||||
const float sin_phi = sinf(phi) * ((xform & NEGATE_SIN) ? -1.0f : 1.0f);
|
||||
const float cos_phi = cosf(phi) * ((xform & NEGATE_COS) ? -1.0f : 1.0f);
|
||||
if ((xform & SWAP_SIN_COS) == 0) {
|
||||
*r_sin = sin_phi;
|
||||
*r_cos = cos_phi;
|
||||
}
|
||||
else {
|
||||
*r_sin = cos_phi;
|
||||
*r_cos = sin_phi;
|
||||
}
|
||||
BLI_assert(-denominator / 4 <= numerator); /* Numerator may be negative. */
|
||||
BLI_assert(numerator <= denominator / 4);
|
||||
BLI_assert(cos_sign == -1.0f || cos_sign == 1.0f);
|
||||
|
||||
const float angle = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator);
|
||||
*r_sin = sinf(angle);
|
||||
*r_cos = cosf(angle) * cos_sign;
|
||||
}
|
||||
|
||||
void print_qt(const char *str, const float q[4])
|
||||
|
|
Loading…
Reference in New Issue