Plaquette
 
Loading...
Searching...
No Matches
pq_math.h
1/*
2 * pq_math.h
3 *
4 * Mathematical functions - replacements of Arduino macros.
5 *
6 * Arduino’s min/max/abs/etc. are unsafe macros: they re-evaluate arguments,
7 * lack type safety, and collide with <algorithm>/<cmath>. This header provides
8 * constexpr function alternatives in namespace pq (min, max, abs, constrain,
9 * round, radians, degrees, sq). Legacy macro names are redefined to call the
10 * safe pq:: functions for backwards compatibility.
11 *
12 * (c) 2025 Sofian Audry :: info(@)sofianaudry(.)com
13 *
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 */
27#ifndef PQ_MATH_H_
28#define PQ_MATH_H_
29
30#include <Arduino.h>
31
32// --- Neutralize Arduino's macros so they don't collide ---
33// On Teensy, min/max are already proper template functions (not macros),
34// so #undef has no effect and we must skip defining pq::min/max to avoid
35// ambiguous overloads. All other symbols (sq, abs, constrain, etc.) are
36// still macros on Teensy and need to be replaced.
37#ifdef min
38 #undef min
39#endif
40#ifdef max
41 #undef max
42#endif
43#ifdef abs
44 #undef abs
45#endif
46#ifdef constrain
47 #undef constrain
48#endif
49#ifdef round
50 #undef round
51#endif
52#ifdef radians
53 #undef radians
54#endif
55#ifdef degrees
56 #undef degrees
57#endif
58#ifdef sq
59 #undef sq
60#endif
61
62namespace pq {
63
64// --- Common type machinery ---------------------------------
65// Ensures math functions work safely when mixing types (e.g. int and float).
66#if defined(__has_include) && __has_include(<type_traits>)
67
68#include <type_traits>
69template <typename A, typename B>
70 using common_pair_t = typename std::common_type<A,B>::type;
71
72template <typename A, typename B, typename C>
73 using common_triple_t = typename std::common_type<A,B,C>::type;
74
75#else
76
77// Minimal fallback: works for most arithmetic types
78template <typename A, typename B>
80 using type = decltype(true ? A{} : B{});
81 };
82template <typename A, typename B>
83 using common_pair_t = typename common_pair_type<A,B>::type;
84
85template <typename A, typename B, typename C>
87 using type = decltype(true ? true ? A{} : B{} : C{});
88 };
89template <typename A, typename B, typename C>
90 using common_triple_t = typename common_triple_type<A,B,C>::type;
91
92#endif
93
94// --- Math functions ---------------------------------------
95
96// -------- min / max --------
97// Skip on Teensy: its wiring.h already provides proper template
98// min/max in the global namespace. Defining pq::min/max would
99// create ambiguous overloads when 'using namespace pq' is active.
100#if !defined(TEENSYDUINO)
101template <class A, class B>
102constexpr common_pair_t<A,B> min(A a, B b) {
103 typedef common_pair_t<A,B> R;
104 return (static_cast<R>(a) < static_cast<R>(b)) ?
105 static_cast<R>(a) :
106 static_cast<R>(b);
107}
108
109template <class A, class B>
110constexpr common_pair_t<A,B> max(A a, B b) {
111 typedef common_pair_t<A,B> R;
112 return (static_cast<R>(a) > static_cast<R>(b)) ?
113 static_cast<R>(a) :
114 static_cast<R>(b);
115}
116#endif
117
118// -------- abs --------
119template <typename T>
120constexpr T abs(T x) {
121 return (x < T{0}) ? -x : x;
122}
123
124
125// -------- constrain --------
126template <class A, class L, class H>
127constexpr common_triple_t<A,L,H>
128constrain(A amt, L low, H high) {
129 typedef common_triple_t<A,L,H> R;
130 return (static_cast<R>(amt) < static_cast<R>(low) ? static_cast<R>(low) :
131 static_cast<R>(amt) > static_cast<R>(high) ? static_cast<R>(high) :
132 static_cast<R>(amt));
133}
134
135// -------- round (Arduino semantics: return long) --------
136template <typename Float>
137constexpr long round(Float x) {
138 return (x >= Float{0})
139 ? static_cast<long>(x + Float{0.5})
140 : static_cast<long>(x - Float{0.5});
141}
142
143// -------- radians / degrees --------
144template <typename T>
145constexpr double radians(T deg) {
146 return deg * DEG_TO_RAD;
147}
148
149template <typename T>
150constexpr double degrees(T rad) {
151 return rad * RAD_TO_DEG;
152}
153
154// -------- sq --------
155template <typename T>
156constexpr T sq(T x) { return x * x; }
157
158} // namespace pq
159
160#endif
Definition pq_math.h:79
Definition pq_math.h:86