FastLED 3.10.8
Loading...
Searching...
No Matches
xypath_impls.cpp.hpp
Go to the documentation of this file.
1
3
4#include "fl/math/math.h"
5
6#include "fl/stl/assert.h"
7#include "fl/stl/function.h"
8#include "fl/math/lut.h"
9#include "fl/math/math.h"
10#include "fl/math/math.h"
11#include "fl/gfx/raster.h"
12
14#include "fl/stl/noexcept.h"
15
16namespace fl {
17
18LinePath::LinePath(float x0, float y0, float x1, float y1) FL_NO_EXCEPT {
20 params().x0 = x0;
21 params().y0 = y0;
22 params().x1 = x1;
23 params().y1 = y1;
24}
25
27 // α in [0,1] → (x,y) on the line
28 float x = params().x0 + alpha * (params().x1 - params().x0);
29 float y = params().y0 + alpha * (params().y1 - params().y0);
30 return {x, y};
31}
32
33void LinePath::set(float x0, float y0, float x1, float y1) FL_NO_EXCEPT {
34 params().x0 = x0;
35 params().y0 = y0;
36 params().x1 = x1;
37 params().y1 = y1;
38}
39
41
43 // α in [0,1] → (x,y) on the unit circle [-1, 1]
44 float t = alpha * 2.0f * FL_PI;
45 float x = cosf(t);
46 float y = sinf(t);
47 return vec2f(x, y);
48}
49
51
53
55 // Parametric equation for a heart shape
56 // α in [0,1] → (x,y) on the heart curve
57 float t = alpha * 2.0f * FL_PI;
58
59 // Heart formula based on a modified cardioid with improved aesthetics
60 float x = 16.0f * powf(sinf(t), 3);
61
62 // Modified y formula for a more balanced heart shape
63 // This creates a fuller bottom and more defined top curve
64 float y = -(13.0f * cosf(t) - 5.0f * cosf(2.0f * t) -
65 2.0f * cosf(3.0f * t) - cosf(4.0f * t));
66
67 // Scale to fit in [-1, 1] range
68 // The 16.0f divisor for x ensures x is in [-1, 1]
69 x /= 16.0f;
70
71 // Scale y to ensure it's in [-1, 1]
72 // The 16.0f divisor ensures proper scaling while maintaining proportions
73 y /= -16.0f;
74
75 // Apply a slight vertical stretch to fill the [-1, 1] range better
76 y *= 1.10f;
77
78 y += 0.17f; // Adjust y to fit within the range of [-1, 1]
79
80 return vec2f(x, y);
81}
82
84 FL_NO_EXCEPT : mTurns(turns), mRadius(radius) {}
85
87 // Parametric equation for an Archimedean spiral
88 // α in [0,1] → (x,y) on the spiral curve
89
90 // Calculate the angle based on the number of turns
91 float theta = alpha * 2.0f * FL_PI * mTurns;
92
93 // Calculate the radius at this angle (grows linearly with angle)
94 // Scale by alpha to ensure we start at center and grow outward
95 float r = alpha * mRadius;
96
97 // Convert polar coordinates (r, theta) to Cartesian (x, y)
98 float x = r * cosf(theta);
99 float y = r * sinf(theta);
100
101 // Ensure the spiral fits within [-1, 1] range
102 // No additional scaling needed as we control the radius directly
103
104 return vec2f(x, y);
105}
106
112
114 // Parametric equation for a rose curve (rhodonea)
115 // α in [0,1] → (x,y) on the rose curve
116
117 // Map alpha to the full range needed for the rose
118 // For a complete rose, we need to go through k*FL_PI radians where k is:
119 // - k = n if n is odd and d is 1
120 // - k = 2n if n is even and d is 1
121 // - k = n*d if n and d are coprime
122 // For simplicity, we'll use 2*FL_PI*n as a good approximation
123 float theta = alpha * 2.0f * FL_PI * params().n;
124
125 // Calculate the radius using the rose formula: r = cos(n*θ/d)
126 // We use cosine for a rose that starts with a petal at theta=0
127 float r = cosf(params().n * theta / params().d);
128
129 // Scale to ensure the rose fits within [-1, 1] range
130 // The absolute value ensures we get the proper shape
131 r = fabsf(r);
132
133 // Convert polar coordinates (r, theta) to Cartesian (x, y)
134 float x = r * cosf(theta);
135 float y = r * sinf(theta);
136
137 return vec2f(x, y);
138}
139
141 // total number of points you want in the pattern
142 const float N = static_cast<float>(params().c);
143
144 // continuous “index” from 0…N
145 float n = alpha * N;
146
147 // use the golden angle in radians:
148 // π * (3 – √5) ≈ 2.399963229728653
149 constexpr float goldenAngle = FL_PI * (3.0f - 1.6180339887498948f);
150
151 // normalized radius [0…1]: sqrt(n/N) gives uniform point density
152 float r = sqrtf(n / N);
153
154 // spiral angle
155 float theta = n * goldenAngle;
156
157 // polar → Cartesian
158 float x = r * cosf(theta);
159 float y = r * sinf(theta);
160
161 return vec2f{x, y};
162}
163
165 // 1) map alpha to angle θ ∈ [0 … 2π)
166 constexpr float kTwoPi = 6.283185307179586f;
167 float theta = alpha * kTwoPi;
168
169 // 2) superformula parameters (members of your path)
170 // a, b control the “shape scale” (often both = 1)
171 // m controls symmetry (integer number of lobes)
172 // n1,n2,n3 control curvature/sharpness
173 float a = params().a;
174 float b = params().b;
175 float m = params().m;
176 float n1 = params().n1;
177 float n2 = params().n2;
178 float n3 = params().n3;
179
180 // 3) compute radius from superformula
181 float t2 = m * theta / 4.0f;
182 float part1 = powf(fabsf(cosf(t2) / a), n2);
183 float part2 = powf(fabsf(sinf(t2) / b), n3);
184 float r = powf(part1 + part2, -1.0f / n1);
185
186 // 4) polar → Cartesian in unit circle
187 float x = r * cosf(theta);
188 float y = r * sinf(theta);
189
190 return vec2f{x, y};
191}
192
193const string CirclePath::name() const FL_NO_EXCEPT { return "CirclePath"; }
194
196 FASTLED_UNUSED(alpha);
197 return mPoint;
198}
199
200const string PointPath::name() const FL_NO_EXCEPT { return "PointPath"; }
201
202void PointPath::set(float x, float y) FL_NO_EXCEPT { set(vec2f(x, y)); }
203
205
207
209
210const string LinePath::name() const FL_NO_EXCEPT { return "LinePath"; }
211
213
215
216LinePath::LinePath(const LinePathParamsPtr &params) FL_NO_EXCEPT : mParams(params) {}
217
218const string HeartPath::name() const FL_NO_EXCEPT { return "HeartPath"; }
219
221 return "ArchimedeanSpiralPath";
222}
223
225
227
228const string RosePath::name() const FL_NO_EXCEPT { return "RosePath"; }
229
231
233
235
237
239
240const string PhyllotaxisPath::name() const FL_NO_EXCEPT { return "PhyllotaxisPath"; }
241
244
246
248
251
252const string GielisCurvePath::name() const FL_NO_EXCEPT { return "GielisCurvePath"; }
253void GielisCurvePath::setA(float a) FL_NO_EXCEPT { params().a = a; }
254void GielisCurvePath::setB(float b) FL_NO_EXCEPT { params().b = b; }
255void GielisCurvePath::setM(float m) FL_NO_EXCEPT { params().m = m; }
256void GielisCurvePath::setN1(float n1) FL_NO_EXCEPT { params().n1 = n1; }
257void GielisCurvePath::setN2(float n2) FL_NO_EXCEPT { params().n2 = n2; }
258void GielisCurvePath::setN3(float n3) FL_NO_EXCEPT { params().n3 = n3; }
261
263
265
266void CatmullRomPath::addPoint(float x, float y) FL_NO_EXCEPT { params().addPoint(x, y); }
267
269
270fl::size CatmullRomPath::size() const FL_NO_EXCEPT { return params().size(); }
271
273
275
277 const auto &points = params().points;
278
279 // Need at least 2 points to define a path
280 if (points.size() < 2) {
281 // Return origin if not enough points
282 return vec2f(0.0f, 0.0f);
283 }
284
285 // If only 2 points, do linear interpolation
286 if (points.size() == 2) {
287 return vec2f(points[0].x + alpha * (points[1].x - points[0].x),
288 points[0].y + alpha * (points[1].y - points[0].y));
289 }
290
291 // For Catmull-Rom, we need 4 points to interpolate between the middle two
292 // Scale alpha to the number of segments
293 float scaledAlpha = alpha * (points.size() - 1);
294
295 // Determine which segment we're in
296 int segment = static_cast<int>(scaledAlpha);
297
298 // Clamp to valid range
299 if (segment >= static_cast<int>(points.size()) - 1) {
300 segment = points.size() - 2;
301 scaledAlpha = static_cast<float>(segment) + 1.0f;
302 }
303
304 // Get local alpha within this segment [0,1]
305 float t = scaledAlpha - static_cast<float>(segment);
306
307 // Get the four points needed for interpolation
308 vec2f p0, p1, p2, p3;
309
310 // Handle boundary cases
311 if (segment == 0) {
312 // For the first segment, duplicate the first point
313 p0 = points[0];
314 p1 = points[0];
315 p2 = points[1];
316 p3 = (points.size() > 2) ? points[2] : points[1];
317 } else if (segment == static_cast<int>(points.size()) - 2) {
318 // For the last segment, duplicate the last point
319 p0 = (segment > 0) ? points[segment - 1] : points[0];
320 p1 = points[segment];
321 p2 = points[segment + 1];
322 p3 = points[segment + 1];
323 } else {
324 // Normal case - we have points before and after
325 p0 = points[segment - 1];
326 p1 = points[segment];
327 p2 = points[segment + 1];
328 p3 = points[segment + 2];
329 }
330
331 // Perform Catmull-Rom interpolation
332 auto out = interpolate(p0, p1, p2, p3, t);
333 return out;
334}
335
337 const vec2f &p2, const vec2f &p3,
338 float t) const FL_NO_EXCEPT {
339
340 // Catmull-Rom interpolation formula
341 // Using alpha=0.5 for the "tension" parameter (standard Catmull-Rom)
342 float t2 = t * t;
343 float t3 = t2 * t;
344
345 // Coefficients for x and y
346 float a = -0.5f * p0.x + 1.5f * p1.x - 1.5f * p2.x + 0.5f * p3.x;
347 float b = p0.x - 2.5f * p1.x + 2.0f * p2.x - 0.5f * p3.x;
348 float c = -0.5f * p0.x + 0.5f * p2.x;
349 float d = p1.x;
350
351 float x = a * t3 + b * t2 + c * t + d;
352
353 a = -0.5f * p0.y + 1.5f * p1.y - 1.5f * p2.y + 0.5f * p3.y;
354 b = p0.y - 2.5f * p1.y + 2.0f * p2.y - 0.5f * p3.y;
355 c = -0.5f * p0.y + 0.5f * p2.y;
356 d = p1.y;
357
358 float y = a * t3 + b * t2 + c * t + d;
359
360 return vec2f(x, y);
361}
362
363const string CatmullRomPath::name() const FL_NO_EXCEPT { return "CatmullRomPath"; }
364
365} // namespace fl
const string name() const FL_NO_EXCEPT override
ArchimedeanSpiralPath(u8 turns=3, float radius=1.0f) FL_NO_EXCEPT
void setTurns(u8 turns) FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
void setRadius(float radius) FL_NO_EXCEPT
void clear() FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
fl::shared_ptr< CatmullRomParams > mParams
CatmullRomPath(const fl::shared_ptr< CatmullRomParams > &p=fl::make_shared< CatmullRomParams >()) FL_NO_EXCEPT
const string name() const FL_NO_EXCEPT override
CatmullRomParams & params() FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
fl::size size() const FL_NO_EXCEPT
Get the number of control points.
vec2f interpolate(const vec2f &p0, const vec2f &p1, const vec2f &p2, const vec2f &p3, float t) const FL_NO_EXCEPT
void addPoint(vec2f p) FL_NO_EXCEPT
Add a point in [0,1]² to the path.
void clear() FL_NO_EXCEPT
Clear all control points.
CirclePath() FL_NO_EXCEPT
const string name() const FL_NO_EXCEPT override
vec2f compute(float alpha) FL_NO_EXCEPT override
fl::shared_ptr< GielisCurveParams > mParams
void setN1(float n1) FL_NO_EXCEPT
const string name() const FL_NO_EXCEPT override
GielisCurveParams & params() FL_NO_EXCEPT
GielisCurvePath(const fl::shared_ptr< GielisCurveParams > &p=fl::make_shared< GielisCurveParams >()) FL_NO_EXCEPT
void setB(float b) FL_NO_EXCEPT
void setN2(float n2) FL_NO_EXCEPT
void setN3(float n3) FL_NO_EXCEPT
void setA(float a) FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
void setM(float m) FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
HeartPath() FL_NO_EXCEPT
const string name() const FL_NO_EXCEPT override
const string name() const FL_NO_EXCEPT override
void set(float x0, float y0, float x1, float y1) FL_NO_EXCEPT
fl::shared_ptr< LinePathParams > mParams
LinePathParams & params() FL_NO_EXCEPT
LinePath(const LinePathParamsPtr &params=fl::make_shared< LinePathParams >()) FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
const string name() const FL_NO_EXCEPT override
PhyllotaxisPath(const fl::shared_ptr< PhyllotaxisParams > &p=fl::make_shared< PhyllotaxisParams >()) FL_NO_EXCEPT
fl::shared_ptr< PhyllotaxisParams > mParams
PhyllotaxisParams & params() FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
void set(float x, float y) FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
const string name() const FL_NO_EXCEPT override
PointPath(float x, float y) FL_NO_EXCEPT
fl::shared_ptr< RosePathParams > mParams
void setN(u8 n) FL_NO_EXCEPT
void setD(u8 d) FL_NO_EXCEPT
const string name() const FL_NO_EXCEPT override
RosePath(const fl::shared_ptr< RosePathParams > &p=fl::make_shared< RosePathParams >()) FL_NO_EXCEPT
RosePathParams & params() FL_NO_EXCEPT
vec2f compute(float alpha) FL_NO_EXCEPT override
#define FASTLED_UNUSED(x)
#define FL_PI
Definition math.h:26
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
Definition shared_ptr.h:423
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
float sinf(float value) FL_NO_EXCEPT
Definition math.h:360
vec2< float > vec2f
Definition geometry.h:333
float sqrtf(float value) FL_NO_EXCEPT
Definition math.h:461
float cosf(float value) FL_NO_EXCEPT
Definition math.h:366
float fabsf(float value) FL_NO_EXCEPT
Definition math.h:516
float powf(float base, float exponent) FL_NO_EXCEPT
Definition math.h:444
Base definition for an LED controller.
Definition crgb.hpp:179