Blender V4.3
BLI_math_angle_types.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
20#include <ostream>
21
22#include "BLI_math_base.hh"
24
25namespace blender::math {
26
34template<typename T> struct AngleRadianBase {
35 private:
36 T value_;
37
38 public:
39 AngleRadianBase() = default;
40
41 AngleRadianBase(const T &radian) : value_(radian){};
42 explicit AngleRadianBase(const T &cos, const T &sin) : value_(math::atan2(sin, cos)){};
43
47 {
48 return 0;
49 }
50
51 static AngleRadianBase from_degree(const T &degrees)
52 {
53 return degrees * T(numbers::pi / 180.0);
54 }
55
58 /* Return angle value in radian. */
59 explicit operator T() const
60 {
61 return value_;
62 }
63
64 /* Return angle value in degree. */
65 T degree() const
66 {
67 return value_ * T(180.0 / numbers::pi);
68 }
69
70 /* Return angle value in radian. */
71 T radian() const
72 {
73 return value_;
74 }
75
82 {
83 return math::mod_periodic(value_ + T(numbers::pi), T(2 * numbers::pi)) - T(numbers::pi);
84 }
85
93 {
94 return reference + (*this - reference).wrapped();
95 }
96
100 {
101 return a.value_ + b.value_;
102 }
103
105 {
106 return a.value_ - b.value_;
107 }
108
110 {
111 return a.value_ * b.value_;
112 }
113
115 {
116 return a.value_ / b.value_;
117 }
118
120 {
121 return -a.value_;
122 }
123
125 {
126 value_ += b.value_;
127 return *this;
128 }
129
131 {
132 value_ -= b.value_;
133 return *this;
134 }
135
137 {
138 value_ *= b.value_;
139 return *this;
140 }
141
143 {
144 value_ /= b.value_;
145 return *this;
146 }
147
149
150 friend std::ostream &operator<<(std::ostream &stream, const AngleRadianBase &rot)
151 {
152 return stream << "AngleRadian(" << rot.value_ << ")";
153 }
154};
155
167template<typename T> struct AngleCartesianBase {
168 private:
169 T cos_;
170 T sin_;
171
172 public:
174
178 AngleCartesianBase(const T &x, const T &y) : cos_(x), sin_(y)
179 {
180 BLI_assert(math::abs(x * x + y * y - T(1)) < T(1e-4));
181 }
182
189 : AngleCartesianBase(math::cos(angle.radian()), math::sin(angle.radian())){};
190
194 {
195 return {1, 0};
196 }
197
198 static AngleCartesianBase from_degree(const T &degrees)
199 {
200 return AngleCartesianBase(degrees * T(numbers::pi / 180.0));
201 }
202
207 static AngleCartesianBase from_point(const T &x, const T &y)
208 {
209 T norm = math::sqrt(x * x + y * y);
210 if (norm == 0) {
211 return identity();
212 }
213 return AngleCartesianBase(x / norm, y / norm);
214 }
215
218 /* Return angle value in radian. */
219 explicit operator T() const
220 {
221 return math::atan2(sin_, cos_);
222 }
223
224 /* Return angle value in degree. */
225 T degree() const
226 {
227 return T(*this) * T(180.0 / numbers::pi);
228 }
229
230 /* Return angle value in radian. */
231 T radian() const
232 {
233 return T(*this);
234 }
235
238 T cos() const
239 {
240 return cos_;
241 }
242
243 T sin() const
244 {
245 return sin_;
246 }
247
248 T tan() const
249 {
250 return sin_ / cos_;
251 }
252
266 {
267 return {a.cos_ * b.cos_ - a.sin_ * b.sin_, a.sin_ * b.cos_ + a.cos_ * b.sin_};
268 }
269
271 {
272 return {a.cos_ * b.cos_ + a.sin_ * b.sin_, a.sin_ * b.cos_ - a.cos_ * b.sin_};
273 }
274
276 {
277 if (b == T(2)) {
278 return {a.cos_ * a.cos_ - a.sin_ * a.sin_, T(2) * a.sin_ * a.cos_};
279 }
280 if (b == T(3)) {
281 return {T(4) * (a.cos_ * a.cos_ * a.cos_) - T(3) * a.cos_,
282 T(3) * a.sin_ - T(4) * (a.sin_ * a.sin_ * a.sin_)};
283 }
285 "Arbitrary angle product isn't supported with AngleCartesianBase<T> for "
286 "performance reason. Use AngleRadianBase<T> instead.");
287 return identity();
288 }
289
291 {
292 return a * b;
293 }
294
295 friend AngleCartesianBase operator/(const AngleCartesianBase &a, const T &divisor)
296 {
297 if (divisor == T(2)) {
298 /* Still costly but faster than using `atan()`. */
299 AngleCartesianBase result = {math::sqrt((T(1) + a.cos_) / T(2)),
300 math::sqrt((T(1) - a.cos_) / T(2))};
301 /* Recover sign only for sine. Cosine of half angle is given to be positive or 0 since the
302 * angle stored in #AngleCartesianBase is in the range [-pi..pi]. */
303 /* TODO(fclem): Could use `copysign` here. */
304 if (a.sin_ < T(0)) {
305 result.sin_ = -result.sin_;
306 }
307 return result;
308 }
310 "Arbitrary angle quotient isn't supported with AngleCartesianBase<T> for "
311 "performance reason. Use AngleRadianBase<T> instead.");
312 return identity();
313 }
314
316 {
317 return {a.cos_, -a.sin_};
318 }
319
321 {
322 *this = *this + b;
323 return *this;
324 }
325
327 {
328 *this = *this * b;
329 return *this;
330 }
331
333 {
334 *this = *this - b;
335 return *this;
336 }
337
339 {
340 *this = *this / b;
341 return *this;
342 }
343
345
346 friend std::ostream &operator<<(std::ostream &stream, const AngleCartesianBase &rot)
347 {
348 return stream << "AngleCartesian(x=" << rot.cos_ << ", y=" << rot.sin_ << ")";
349 }
350};
351
375template<typename T = float> struct AngleFraction {
376 private:
380 int64_t numerator_;
381 int64_t denominator_;
382
387 AngleFraction(int64_t numerator, int64_t denominator = 1)
388 : numerator_(numerator), denominator_(denominator){};
389
390 public:
394 {
395 return {0};
396 }
397
399 {
400 return {1};
401 }
402
404 {
405 return {2};
406 }
407
410 /* Return angle value in degree. */
411 T degree() const
412 {
413 return T(numerator_ * 180) / T(denominator_);
414 }
415
416 /* Return angle value in radian. */
417 T radian() const
418 {
419 /* This can be refined at will. This tries to reduce the float precision error to a minimum. */
420 const bool is_negative = numerator_ < 0;
421 /* TODO jump table. */
422 if (abs(numerator_) == denominator_ * 2) {
423 return is_negative ? T(-numbers::pi * 2) : T(numbers::pi * 2);
424 }
425 if (abs(numerator_) == denominator_) {
426 return is_negative ? T(-numbers::pi) : T(numbers::pi);
427 }
428 if (numerator_ == 0) {
429 return T(0);
430 }
431 if (abs(numerator_) * 2 == denominator_) {
432 return is_negative ? T(-numbers::pi * 0.5) : T(numbers::pi * 0.5);
433 }
434 if (abs(numerator_) * 4 == denominator_) {
435 return is_negative ? T(-numbers::pi * 0.25) : T(numbers::pi * 0.25);
436 }
437 /* TODO(fclem): No idea if this is precise or not. Just doing something for now. */
438 const int64_t number_of_pi = numerator_ / denominator_;
439 const int64_t slice_numerator = numerator_ - number_of_pi * denominator_;
440 T slice_of_pi;
441 /* Avoid integer overflow. */
442 /* TODO(fclem): This is conservative. Could find a better threshold. */
443 if (slice_numerator > 0xFFFFFFFF || denominator_ > 0xFFFFFFFF) {
444 /* Certainly loose precision. */
445 slice_of_pi = T(numbers::pi) * slice_numerator / T(denominator_);
446 }
447 else {
448 /* Pi as a fraction can be expressed as 80143857 / 25510582 with 15th digit of precision. */
449 slice_of_pi = T(slice_numerator * 80143857) / T(denominator_ * 25510582);
450 }
451 /* If angle is inside [-pi..pi] range, `number_of_pi` is 0 and has no effect on precision. */
452 return slice_of_pi + T(numbers::pi) * number_of_pi;
453 }
454
461 {
462 if (abs(numerator_) <= denominator_) {
463 return *this;
464 }
465 return {mod_periodic(numerator_ + denominator_, denominator_ * 2) - denominator_,
466 denominator_};
467 }
468
476 {
477 return reference + (*this - reference).wrapped();
478 }
479
488 {
489 if (a.denominator_ == b.denominator_) {
490 return {a.numerator_ + b.numerator_, a.denominator_};
491 }
492 return {(a.numerator_ * b.denominator_) + (b.numerator_ * a.denominator_),
493 a.denominator_ * b.denominator_};
494 }
495
497 {
498 return a + (-b);
499 }
500
502 {
503 return {a.numerator_ * b.numerator_, a.denominator_ * b.denominator_};
504 }
505
507 {
508 return a * AngleFraction(b.denominator_, b.numerator_);
509 }
510
512 {
513 return a * AngleFraction(b);
514 }
515
517 {
518 return a / AngleFraction(b);
519 }
520
522 {
523 return AngleFraction(a) * b;
524 }
525
527 {
528 return AngleFraction(a) / b;
529 }
530
532 {
533 return a;
534 }
535
537 {
538 return {-a.numerator_, a.denominator_};
539 }
540
542 {
543 return *this = *this + b;
544 }
545
547 {
548 return *this = *this - b;
549 }
550
552 {
553 return *this = *this * b;
554 }
555
557 {
558 return *this = *this / b;
559 }
560
562 {
563 return *this = *this * b;
564 }
565
567 {
568 return *this = *this / b;
569 }
570
571 friend bool operator==(const AngleFraction &a, const AngleFraction &b)
572 {
573 if (a.numerator_ == 0 && b.numerator_ == 0) {
574 return true;
575 }
576 if (a.denominator_ == b.denominator_) {
577 return a.numerator_ == b.numerator_;
578 }
579 return a.numerator_ * b.denominator_ == b.numerator_ * a.denominator_;
580 }
581
582 friend bool operator!=(const AngleFraction &a, const AngleFraction &b)
583 {
584 return !(a == b);
585 }
586
587 friend std::ostream &operator<<(std::ostream &stream, const AngleFraction &rot)
588 {
589 return stream << "AngleFraction(num=" << rot.numerator_ << ", denom=" << rot.denominator_
590 << ")";
591 }
592
593 operator AngleCartesianBase<T>() const
594 {
595 AngleFraction a = this->wrapped();
596 BLI_assert(abs(a.numerator_) <= a.denominator_);
597 BLI_assert(a.denominator_ > 0);
598
599 /* By default, creating a circle from an integer: calling #sinf & #cosf on the fraction
600 * doesn't create symmetrical values (because floats can't represent Pi exactly). Resolve this
601 * when the rotation is calculated from a fraction by mapping the `numerator` to lower values
602 * so X/Y values for points around a circle are exactly symmetrical, see #87779.
603 *
604 * Multiply both the `numerator` and `denominator` by 4 to ensure we can divide the circle
605 * into 8 octants. For each octant, we then use symmetry and negation to bring the `numerator`
606 * closer to the origin where precision is highest.
607 */
608 /* Save negative sign so we cane assume unsigned angle for the rest of the computation.. */
609 const bool is_negative = a.numerator_ < 0;
610 /* Multiply numerator the same as denominator. */
611 a.numerator_ = abs(a.numerator_) * 4;
612 /* Determine the octant. */
613 const int64_t octant = a.numerator_ / a.denominator_;
614 const int64_t rest = a.numerator_ - octant * a.denominator_;
615 /* Ensure denominator is a multiple of 4. */
616 a.denominator_ *= 4;
617
618 /* TODO jump table. */
619 T x, y;
620 /* If rest is 0, the angle is an angle with precise value. */
621 if (rest == 0) {
622 switch (octant) {
623 case 0:
624 case 4:
625 x = T(1);
626 y = T(0);
627 break;
628 case 2:
629 x = T(0);
630 y = T(1);
631 break;
632 case 1:
633 case 3:
634 x = y = math::rcp(T(numbers::sqrt2));
635 break;
636 default:
638 }
639 }
640 else {
641 switch (octant) {
642 case 4:
643 /* -Pi or Pi case. */
644 case 0:
645 /* Primary octant, nothing to do. */
646 break;
647 case 1:
648 /* Pi / 2 - angle. */
649 a.numerator_ = a.denominator_ / 2 - a.numerator_;
650 break;
651 case 2:
652 /* Angle - Pi / 2. */
653 a.numerator_ = a.numerator_ - a.denominator_ / 2;
654 break;
655 case 3:
656 /* Pi - angle. */
657 a.numerator_ = a.denominator_ - a.numerator_;
658 break;
659 default:
661 }
662 /* Resulting angle should be oscillating in [0..pi/4] range. */
663 BLI_assert(a.numerator_ >= 0 && a.numerator_ <= a.denominator_ / 4);
664 T angle = T(numbers::pi) * (T(a.numerator_) / T(a.denominator_));
665 x = math::cos(angle);
666 y = math::sin(angle);
667 /* Diagonal symmetry "unfolding". */
668 if (ELEM(octant, 1, 2)) {
669 std::swap(x, y);
670 }
671 }
672 /* Y axis symmetry. */
673 if (octant >= 2) {
674 x = -x;
675 }
676 /* X axis symmetry. */
677 if (is_negative) {
678 y = -y;
679 }
680 return AngleCartesianBase<T>(x, y);
681 }
682};
683
684template<typename T> T cos(const AngleRadianBase<T> &a)
685{
686 return cos(a.radian());
687}
688template<typename T> T sin(const AngleRadianBase<T> &a)
689{
690 return sin(a.radian());
691}
692template<typename T> T tan(const AngleRadianBase<T> &a)
693{
694 return tan(a.radian());
695}
696
697template<typename T> T cos(const AngleCartesianBase<T> &a)
698{
699 return a.cos();
700}
701template<typename T> T sin(const AngleCartesianBase<T> &a)
702{
703 return a.sin();
704}
705template<typename T> T tan(const AngleCartesianBase<T> &a)
706{
707 return a.tan();
708}
709
710template<typename T> T cos(const AngleFraction<T> &a)
711{
712 return cos(AngleCartesianBase<T>(a));
713}
714template<typename T> T sin(const AngleFraction<T> &a)
715{
716 return sin(AngleCartesianBase<T>(a));
717}
718template<typename T> T tan(const AngleFraction<T> &a)
719{
720 return tan(AngleCartesianBase<T>(a));
721}
722
725
726} // namespace blender::math
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define BLI_STRUCT_EQUALITY_OPERATORS_1(Type, m)
#define BLI_STRUCT_EQUALITY_OPERATORS_2(Type, m1, m2)
#define ELEM(...)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
local_group_size(16, 16) .push_constant(Type b
#define rot(x, k)
#define T
T cos(const AngleRadianBase< T > &a)
T sqrt(const T &a)
bool is_negative(const MatBase< T, Size, Size > &mat)
T atan2(const T &y, const T &x)
T rcp(const T &a)
T mod_periodic(const T &a, const T &b)
T sin(const AngleRadianBase< T > &a)
T abs(const T &a)
T tan(const AngleRadianBase< T > &a)
__int64 int64_t
Definition stdint.h:89
friend AngleCartesianBase operator*(const AngleCartesianBase &a, const T &b)
static AngleCartesianBase identity()
friend AngleCartesianBase operator/(const AngleCartesianBase &a, const T &divisor)
AngleCartesianBase(const AngleRadianBase< T > &angle)
AngleCartesianBase & operator+=(const AngleCartesianBase &b)
static AngleCartesianBase from_degree(const T &degrees)
friend AngleCartesianBase operator-(const AngleCartesianBase &a)
AngleCartesianBase & operator/=(const T &b)
AngleCartesianBase & operator-=(const AngleCartesianBase &b)
AngleCartesianBase & operator*=(const T &b)
friend AngleCartesianBase operator-(const AngleCartesianBase &a, const AngleCartesianBase &b)
friend AngleCartesianBase operator+(const AngleCartesianBase &a, const AngleCartesianBase &b)
friend AngleCartesianBase operator*(const T &b, const AngleCartesianBase &a)
static AngleCartesianBase from_point(const T &x, const T &y)
friend AngleFraction operator+(const AngleFraction &a, const AngleFraction &b)
friend AngleFraction operator/(const AngleFraction &a, const int64_t &b)
AngleFraction & operator*=(const AngleFraction &b)
AngleFraction wrapped_around(const AngleFraction &reference) const
AngleFraction & operator*=(const int64_t &b)
friend AngleFraction operator-(const AngleFraction &a, const AngleFraction &b)
friend bool operator!=(const AngleFraction &a, const AngleFraction &b)
AngleFraction & operator/=(const int64_t &b)
friend bool operator==(const AngleFraction &a, const AngleFraction &b)
friend AngleFraction operator*(const AngleFraction &a, const int64_t &b)
friend AngleFraction operator/(const AngleFraction &a, const AngleFraction &b)
friend AngleFraction operator*(const AngleFraction &a, const AngleFraction &b)
AngleFraction & operator+=(const AngleFraction &b)
friend AngleFraction operator+(const AngleFraction &a)
AngleFraction & operator-=(const AngleFraction &b)
AngleFraction & operator/=(const AngleFraction &b)
friend AngleFraction operator*(const int64_t &a, const AngleFraction &b)
friend AngleFraction operator-(const AngleFraction &a)
friend AngleFraction operator/(const int64_t &a, const AngleFraction &b)
friend std::ostream & operator<<(std::ostream &stream, const AngleFraction &rot)
static AngleRadianBase identity()
AngleRadianBase wrapped_around(const AngleRadianBase &reference) const
friend AngleRadianBase operator-(const AngleRadianBase &a)
friend AngleRadianBase operator/(const AngleRadianBase &a, const AngleRadianBase &b)
friend AngleRadianBase operator*(const AngleRadianBase &a, const AngleRadianBase &b)
friend AngleRadianBase operator+(const AngleRadianBase &a, const AngleRadianBase &b)
AngleRadianBase & operator+=(const AngleRadianBase &b)
static AngleRadianBase from_degree(const T &degrees)
friend AngleRadianBase operator-(const AngleRadianBase &a, const AngleRadianBase &b)
AngleRadianBase & operator*=(const AngleRadianBase &b)
AngleRadianBase & operator/=(const AngleRadianBase &b)
AngleRadianBase(const T &cos, const T &sin)
AngleRadianBase & operator-=(const AngleRadianBase &b)