FastLED 3.9.15
Loading...
Searching...
No Matches
traverse_grid.h
Go to the documentation of this file.
1#pragma once
2
3/*
4Amanatides–Woo grid traversal algorithm in C++.
5Given a line defined by two points, this algorithm traverses the grid cells
6intersecting the line and calls a visitor function for each cell.
7*/
8
9#include "fl/math/math.h"
10#include "fl/math/geometry.h"
11#include "fl/math/math.h"
12#include "fl/stl/limits.h"
13#include "fl/stl/int.h"
14
15namespace fl {
16
26template <typename GridVisitor>
27void traverseGridSegment(const vec2f &start, const vec2f &end,
28 GridVisitor &visitor);
29
36template <typename GridVisitor>
37void traverseGridSegment16(const vec2f &start, const vec2f &end,
38 GridVisitor &visitor);
39
40// @brief Traverse a grid segment using fixed-point 24.8 arithmetic.
46template <typename GridVisitor>
47void traverseGridSegment32(const vec2f &start, const vec2f &end,
48 GridVisitor &visitor);
49
57template <typename GridVisitor>
58void traverseGridSegmentFloat(const vec2f &start, const vec2f &end,
59 GridVisitor &visitor);
60
62
69template <typename GridVisitor>
70inline void traverseGridSegmentFloat(const vec2f &start, const vec2f &end,
71 GridVisitor &visitor) {
72 int x0 = static_cast<int>(fl::floor(start.x));
73 int y0 = static_cast<int>(fl::floor(start.y));
74 int x1 = static_cast<int>(fl::floor(end.x));
75 int y1 = static_cast<int>(fl::floor(end.y));
76
77 int stepX = (x1 > x0) ? 1 : (x1 < x0) ? -1 : 0;
78 int stepY = (y1 > y0) ? 1 : (y1 < y0) ? -1 : 0;
79
80 float dx = end.x - start.x;
81 float dy = end.y - start.y;
82
83 float tDeltaX = (dx != 0.0f) ? fl::abs(1.0f / dx) : FL_FLT_MAX;
84 float tDeltaY = (dy != 0.0f) ? fl::abs(1.0f / dy) : FL_FLT_MAX;
85
86 float nextX = (stepX > 0) ? (fl::floor(start.x) + 1) : fl::floor(start.x);
87 float nextY = (stepY > 0) ? (fl::floor(start.y) + 1) : fl::floor(start.y);
88
89 float tMaxX = (dx != 0.0f) ? fl::abs((nextX - start.x) / dx) : FL_FLT_MAX;
90 float tMaxY = (dy != 0.0f) ? fl::abs((nextY - start.y) / dy) : FL_FLT_MAX;
91
92 float maxT = 1.0f;
93
94 int currentX = x0;
95 int currentY = y0;
96
97 while (true) {
98 visitor.visit(currentX, currentY);
99 float t = fl::min(tMaxX, tMaxY);
100 if (t > maxT)
101 break;
102
103 if (tMaxX < tMaxY) {
104 tMaxX += tDeltaX;
105 currentX += stepX;
106 } else {
107 tMaxY += tDeltaY;
108 currentY += stepY;
109 }
110 }
111
112 // Ensure the end cell (x1, y1) is visited at least once
113 if (currentX != x1 || currentY != y1) {
114 visitor.visit(x1, y1);
115 }
116}
117
124template <typename GridVisitor>
125inline void traverseGridSegment16(const vec2f &start, const vec2f &end,
126 GridVisitor &visitor) {
127 const i16 FP_SHIFT = 8;
128 const i16 FP_ONE = 1 << FP_SHIFT;
129 // const i16 FP_MASK = FP_ONE - 1;
130
131 // Convert to fixed-point (Q8.8), signed
132 i16 startX_fp = static_cast<i16>(start.x * FP_ONE);
133 i16 startY_fp = static_cast<i16>(start.y * FP_ONE);
134 i16 endX_fp = static_cast<i16>(end.x * FP_ONE);
135 i16 endY_fp = static_cast<i16>(end.y * FP_ONE);
136
137 i16 x0 = startX_fp >> FP_SHIFT;
138 i16 y0 = startY_fp >> FP_SHIFT;
139 i16 x1 = endX_fp >> FP_SHIFT;
140 i16 y1 = endY_fp >> FP_SHIFT;
141
142 i16 stepX = (x1 > x0) ? 1 : (x1 < x0) ? -1 : 0;
143 i16 stepY = (y1 > y0) ? 1 : (y1 < y0) ? -1 : 0;
144
145 i16 deltaX_fp = endX_fp - startX_fp;
146 i16 deltaY_fp = endY_fp - startY_fp;
147
148 // Use (max)() to prevent macro expansion by Arduino.h's max macro
149 u16 absDeltaX_fp =
150 (deltaX_fp != 0) ? static_cast<u16>(
151 fl::abs((i32(FP_ONE) << FP_SHIFT) / deltaX_fp))
153 u16 absDeltaY_fp =
154 (deltaY_fp != 0) ? static_cast<u16>(
155 fl::abs((i32(FP_ONE) << FP_SHIFT) / deltaY_fp))
157
158 i16 nextX_fp = (stepX > 0) ? ((x0 + 1) << FP_SHIFT) : (x0 << FP_SHIFT);
159 i16 nextY_fp = (stepY > 0) ? ((y0 + 1) << FP_SHIFT) : (y0 << FP_SHIFT);
160
161 // Use (max)() to prevent macro expansion by Arduino.h's max macro
162 u16 tMaxX_fp =
163 (deltaX_fp != 0)
164 ? static_cast<u16>(
165 fl::abs(i32(nextX_fp - startX_fp)) * absDeltaX_fp >> FP_SHIFT)
167 u16 tMaxY_fp =
168 (deltaY_fp != 0)
169 ? static_cast<u16>(
170 fl::abs(i32(nextY_fp - startY_fp)) * absDeltaY_fp >> FP_SHIFT)
172
173 const u16 maxT_fp = FP_ONE;
174
175 i16 currentX = x0;
176 i16 currentY = y0;
177
178 while (true) {
179 visitor.visit(currentX, currentY);
180
181 u16 t_fp = (tMaxX_fp < tMaxY_fp) ? tMaxX_fp : tMaxY_fp;
182 if (t_fp > maxT_fp)
183 break;
184
185 if (tMaxX_fp < tMaxY_fp) {
186 tMaxX_fp += absDeltaX_fp;
187 currentX += stepX;
188 } else {
189 tMaxY_fp += absDeltaY_fp;
190 currentY += stepY;
191 }
192 }
193
194 // Ensure the end cell (x1, y1) is visited at least once
195 if (currentX != x1 || currentY != y1) {
196 visitor.visit(x1, y1);
197 }
198}
199
200// @brief Traverse a grid segment using fixed-point 24.8 arithmetic.
206template <typename GridVisitor>
207inline void traverseGridSegment32(const vec2f &start, const vec2f &end,
208 GridVisitor &visitor) {
209 const i32 FP_SHIFT = 8;
210 const i32 FP_ONE = 1 << FP_SHIFT;
211 // const i32 FP_MASK = FP_ONE - 1;
212
213 // Convert to fixed-point (Q24.8) signed
214 i32 startX_fp = static_cast<i32>(start.x * FP_ONE);
215 i32 startY_fp = static_cast<i32>(start.y * FP_ONE);
216 i32 endX_fp = static_cast<i32>(end.x * FP_ONE);
217 i32 endY_fp = static_cast<i32>(end.y * FP_ONE);
218
219 i32 x0 = startX_fp >> FP_SHIFT;
220 i32 y0 = startY_fp >> FP_SHIFT;
221 i32 x1 = endX_fp >> FP_SHIFT;
222 i32 y1 = endY_fp >> FP_SHIFT;
223
224 i32 stepX = (x1 > x0) ? 1 : (x1 < x0) ? -1 : 0;
225 i32 stepY = (y1 > y0) ? 1 : (y1 < y0) ? -1 : 0;
226
227 i32 deltaX_fp = endX_fp - startX_fp;
228 i32 deltaY_fp = endY_fp - startY_fp;
229
230 // Use (max)() to prevent macro expansion by Arduino.h's max macro
231 u32 absDeltaX_fp =
232 (deltaX_fp != 0) ? static_cast<u32>(
233 fl::abs((fl::i64(FP_ONE) << FP_SHIFT) / deltaX_fp))
235 u32 absDeltaY_fp =
236 (deltaY_fp != 0) ? static_cast<u32>(
237 fl::abs((fl::i64(FP_ONE) << FP_SHIFT) / deltaY_fp))
239
240 i32 nextX_fp = (stepX > 0) ? ((x0 + 1) << FP_SHIFT) : (x0 << FP_SHIFT);
241 i32 nextY_fp = (stepY > 0) ? ((y0 + 1) << FP_SHIFT) : (y0 << FP_SHIFT);
242
243 // Use (max)() to prevent macro expansion by Arduino.h's max macro
244 u32 tMaxX_fp =
245 (deltaX_fp != 0)
246 ? static_cast<u32>(
247 fl::abs(fl::i64(nextX_fp - startX_fp)) * absDeltaX_fp >> FP_SHIFT)
249 u32 tMaxY_fp =
250 (deltaY_fp != 0)
251 ? static_cast<u32>(
252 fl::abs(fl::i64(nextY_fp - startY_fp)) * absDeltaY_fp >> FP_SHIFT)
254
255 const u32 maxT_fp = FP_ONE;
256
257 i32 currentX = x0;
258 i32 currentY = y0;
259
260 while (true) {
261 visitor.visit(currentX, currentY);
262
263 u32 t_fp = (tMaxX_fp < tMaxY_fp) ? tMaxX_fp : tMaxY_fp;
264 if (t_fp > maxT_fp)
265 break;
266
267 if (tMaxX_fp < tMaxY_fp) {
268 tMaxX_fp += absDeltaX_fp;
269 currentX += stepX;
270 } else {
271 tMaxY_fp += absDeltaY_fp;
272 currentY += stepY;
273 }
274 }
275
276 if (currentX != x1 || currentY != y1) {
277 visitor.visit(x1, y1);
278 }
279}
280
281template <typename GridVisitor>
282inline void traverseGridSegment(const vec2f &start, const vec2f &end,
283 GridVisitor &visitor) {
284 float dx = fl::abs(end.x - start.x);
285 float dy = fl::abs(end.y - start.y);
286 float maxRange = fl::max(dx, dy);
287
288 // if (maxRange < 256.0f) {
289 // // Use Q8.8 (16-bit signed) if within ±127
290 // traverseGridSegment16(start, end, visitor);
291 // }
292 // else if (maxRange < 16777216.0f) {
293 // // Use Q24.8 (32-bit signed) if within ±8 million
294 // traverseGridSegment32(start, end, visitor);
295 // }
296 // else {
297 // // Fall back to floating-point
298 // traverseGridSegment(start, end, visitor);
299 // }
300
301 if (maxRange < 256.0f) {
302 // Use Q8.8 (16-bit signed) if within ±127
303 traverseGridSegment16(start, end, visitor);
304 } else {
305 // Use Q24.8 (32-bit signed) if within ±8 million
306 traverseGridSegment32(start, end, visitor);
307 }
308}
309
310} // namespace fl
#define FL_FLT_MAX
Definition math.h:54
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
void traverseGridSegmentFloat(const vec2f &start, const vec2f &end, GridVisitor &visitor)
Traverse a grid segment using floating point arithmetic.
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
constexpr T * end(T(&array)[N]) FL_NOEXCEPT
vec2< float > vec2f
Definition geometry.h:333
constexpr enable_if< is_fixed_point< T >::value, T >::type floor(T x) FL_NOEXCEPT
static constexpr i32 FP_ONE
void traverseGridSegment16(const vec2f &start, const vec2f &end, GridVisitor &visitor)
Traverse a grid segment using fixed-point 8.8 arithmetic.
fl::i64 i64
Definition s16x16x4.h:222
void traverseGridSegment(const vec2f &start, const vec2f &end, GridVisitor &visitor)
Traverse a grid segment by selecting the cells that are crossed.
void traverseGridSegment32(const vec2f &start, const vec2f &end, GridVisitor &visitor)
constexpr enable_if< is_fixed_point< T >::value, T >::type abs(T x) FL_NOEXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
static constexpr T max() FL_NOEXCEPT
Definition limits.h:108
value_type y
Definition geometry.h:191
value_type x
Definition geometry.h:190