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.h"
10#include "fl/point.h"
11
12namespace fl {
13
23template <typename GridVisitor>
24void traverseGridSegment(const vec2f &start, const vec2f &end,
25 GridVisitor &visitor);
26
33template <typename GridVisitor>
34void traverseGridSegment16(const vec2f &start, const vec2f &end,
35 GridVisitor &visitor);
36
37// @brief Traverse a grid segment using fixed-point 24.8 arithmetic.
43template <typename GridVisitor>
44void traverseGridSegment32(const vec2f &start, const vec2f &end,
45 GridVisitor &visitor);
46
54template <typename GridVisitor>
55void traverseGridSegmentFloat(const vec2f &start, const vec2f &end,
56 GridVisitor &visitor);
57
59
66template <typename GridVisitor>
67inline void traverseGridSegmentFloat(const vec2f &start, const vec2f &end,
68 GridVisitor &visitor) {
69 int x0 = static_cast<int>(fl::floor(start.x));
70 int y0 = static_cast<int>(fl::floor(start.y));
71 int x1 = static_cast<int>(fl::floor(end.x));
72 int y1 = static_cast<int>(fl::floor(end.y));
73
74 int stepX = (x1 > x0) ? 1 : (x1 < x0) ? -1 : 0;
75 int stepY = (y1 > y0) ? 1 : (y1 < y0) ? -1 : 0;
76
77 float dx = end.x - start.x;
78 float dy = end.y - start.y;
79
80 float tDeltaX = (dx != 0.0f) ? ABS(1.0f / dx) : FLT_MAX;
81 float tDeltaY = (dy != 0.0f) ? ABS(1.0f / dy) : FLT_MAX;
82
83 float nextX = (stepX > 0) ? (fl::floor(start.x) + 1) : fl::floor(start.x);
84 float nextY = (stepY > 0) ? (fl::floor(start.y) + 1) : fl::floor(start.y);
85
86 float tMaxX = (dx != 0.0f) ? ABS((nextX - start.x) / dx) : FLT_MAX;
87 float tMaxY = (dy != 0.0f) ? ABS((nextY - start.y) / dy) : FLT_MAX;
88
89 float maxT = 1.0f;
90
91 int currentX = x0;
92 int currentY = y0;
93
94 while (true) {
95 visitor.visit(currentX, currentY);
96 float t = MIN(tMaxX, tMaxY);
97 if (t > maxT)
98 break;
99
100 if (tMaxX < tMaxY) {
101 tMaxX += tDeltaX;
102 currentX += stepX;
103 } else {
104 tMaxY += tDeltaY;
105 currentY += stepY;
106 }
107 }
108
109 // Ensure the end cell (x1, y1) is visited at least once
110 if (currentX != x1 || currentY != y1) {
111 visitor.visit(x1, y1);
112 }
113}
114
121template <typename GridVisitor>
122inline void traverseGridSegment16(const vec2f &start, const vec2f &end,
123 GridVisitor &visitor) {
124 const int16_t FP_SHIFT = 8;
125 const int16_t FP_ONE = 1 << FP_SHIFT;
126 // const int16_t FP_MASK = FP_ONE - 1;
127
128 // Convert to fixed-point (Q8.8), signed
129 int16_t startX_fp = static_cast<int16_t>(start.x * FP_ONE);
130 int16_t startY_fp = static_cast<int16_t>(start.y * FP_ONE);
131 int16_t endX_fp = static_cast<int16_t>(end.x * FP_ONE);
132 int16_t endY_fp = static_cast<int16_t>(end.y * FP_ONE);
133
134 int16_t x0 = startX_fp >> FP_SHIFT;
135 int16_t y0 = startY_fp >> FP_SHIFT;
136 int16_t x1 = endX_fp >> FP_SHIFT;
137 int16_t y1 = endY_fp >> FP_SHIFT;
138
139 int16_t stepX = (x1 > x0) ? 1 : (x1 < x0) ? -1 : 0;
140 int16_t stepY = (y1 > y0) ? 1 : (y1 < y0) ? -1 : 0;
141
142 int16_t deltaX_fp = endX_fp - startX_fp;
143 int16_t deltaY_fp = endY_fp - startY_fp;
144
145 uint16_t absDeltaX_fp =
146 (deltaX_fp != 0) ? static_cast<uint16_t>(
147 ABS((int32_t(FP_ONE) << FP_SHIFT) / deltaX_fp))
148 : UINT16_MAX;
149 uint16_t absDeltaY_fp =
150 (deltaY_fp != 0) ? static_cast<uint16_t>(
151 ABS((int32_t(FP_ONE) << FP_SHIFT) / deltaY_fp))
152 : UINT16_MAX;
153
154 int16_t nextX_fp = (stepX > 0) ? ((x0 + 1) << FP_SHIFT) : (x0 << FP_SHIFT);
155 int16_t nextY_fp = (stepY > 0) ? ((y0 + 1) << FP_SHIFT) : (y0 << FP_SHIFT);
156
157 uint16_t tMaxX_fp =
158 (deltaX_fp != 0)
159 ? static_cast<uint16_t>(
160 ABS(int32_t(nextX_fp - startX_fp)) * absDeltaX_fp >> FP_SHIFT)
161 : UINT16_MAX;
162 uint16_t tMaxY_fp =
163 (deltaY_fp != 0)
164 ? static_cast<uint16_t>(
165 ABS(int32_t(nextY_fp - startY_fp)) * absDeltaY_fp >> FP_SHIFT)
166 : UINT16_MAX;
167
168 const uint16_t maxT_fp = FP_ONE;
169
170 int16_t currentX = x0;
171 int16_t currentY = y0;
172
173 while (true) {
174 visitor.visit(currentX, currentY);
175
176 uint16_t t_fp = (tMaxX_fp < tMaxY_fp) ? tMaxX_fp : tMaxY_fp;
177 if (t_fp > maxT_fp)
178 break;
179
180 if (tMaxX_fp < tMaxY_fp) {
181 tMaxX_fp += absDeltaX_fp;
182 currentX += stepX;
183 } else {
184 tMaxY_fp += absDeltaY_fp;
185 currentY += stepY;
186 }
187 }
188
189 // Ensure the end cell (x1, y1) is visited at least once
190 if (currentX != x1 || currentY != y1) {
191 visitor.visit(x1, y1);
192 }
193}
194
195// @brief Traverse a grid segment using fixed-point 24.8 arithmetic.
201template <typename GridVisitor>
202inline void traverseGridSegment32(const vec2f &start, const vec2f &end,
203 GridVisitor &visitor) {
204 const int32_t FP_SHIFT = 8;
205 const int32_t FP_ONE = 1 << FP_SHIFT;
206 // const int32_t FP_MASK = FP_ONE - 1;
207
208 // Convert to fixed-point (Q24.8) signed
209 int32_t startX_fp = static_cast<int32_t>(start.x * FP_ONE);
210 int32_t startY_fp = static_cast<int32_t>(start.y * FP_ONE);
211 int32_t endX_fp = static_cast<int32_t>(end.x * FP_ONE);
212 int32_t endY_fp = static_cast<int32_t>(end.y * FP_ONE);
213
214 int32_t x0 = startX_fp >> FP_SHIFT;
215 int32_t y0 = startY_fp >> FP_SHIFT;
216 int32_t x1 = endX_fp >> FP_SHIFT;
217 int32_t y1 = endY_fp >> FP_SHIFT;
218
219 int32_t stepX = (x1 > x0) ? 1 : (x1 < x0) ? -1 : 0;
220 int32_t stepY = (y1 > y0) ? 1 : (y1 < y0) ? -1 : 0;
221
222 int32_t deltaX_fp = endX_fp - startX_fp;
223 int32_t deltaY_fp = endY_fp - startY_fp;
224
225 uint32_t absDeltaX_fp =
226 (deltaX_fp != 0) ? static_cast<uint32_t>(
227 ABS((int64_t(FP_ONE) << FP_SHIFT) / deltaX_fp))
228 : UINT32_MAX;
229 uint32_t absDeltaY_fp =
230 (deltaY_fp != 0) ? static_cast<uint32_t>(
231 ABS((int64_t(FP_ONE) << FP_SHIFT) / deltaY_fp))
232 : UINT32_MAX;
233
234 int32_t nextX_fp = (stepX > 0) ? ((x0 + 1) << FP_SHIFT) : (x0 << FP_SHIFT);
235 int32_t nextY_fp = (stepY > 0) ? ((y0 + 1) << FP_SHIFT) : (y0 << FP_SHIFT);
236
237 uint32_t tMaxX_fp =
238 (deltaX_fp != 0)
239 ? static_cast<uint32_t>(
240 ABS(int64_t(nextX_fp - startX_fp)) * absDeltaX_fp >> FP_SHIFT)
241 : UINT32_MAX;
242 uint32_t tMaxY_fp =
243 (deltaY_fp != 0)
244 ? static_cast<uint32_t>(
245 ABS(int64_t(nextY_fp - startY_fp)) * absDeltaY_fp >> FP_SHIFT)
246 : UINT32_MAX;
247
248 const uint32_t maxT_fp = FP_ONE;
249
250 int32_t currentX = x0;
251 int32_t currentY = y0;
252
253 while (true) {
254 visitor.visit(currentX, currentY);
255
256 uint32_t t_fp = (tMaxX_fp < tMaxY_fp) ? tMaxX_fp : tMaxY_fp;
257 if (t_fp > maxT_fp)
258 break;
259
260 if (tMaxX_fp < tMaxY_fp) {
261 tMaxX_fp += absDeltaX_fp;
262 currentX += stepX;
263 } else {
264 tMaxY_fp += absDeltaY_fp;
265 currentY += stepY;
266 }
267 }
268
269 if (currentX != x1 || currentY != y1) {
270 visitor.visit(x1, y1);
271 }
272}
273
274template <typename GridVisitor>
275inline void traverseGridSegment(const vec2f &start, const vec2f &end,
276 GridVisitor &visitor) {
277 float dx = ABS(end.x - start.x);
278 float dy = ABS(end.y - start.y);
279 float maxRange = MAX(dx, dy);
280
281 // if (maxRange < 256.0f) {
282 // // Use Q8.8 (16-bit signed) if within ±127
283 // traverseGridSegment16(start, end, visitor);
284 // }
285 // else if (maxRange < 16777216.0f) {
286 // // Use Q24.8 (32-bit signed) if within ±8 million
287 // traverseGridSegment32(start, end, visitor);
288 // }
289 // else {
290 // // Fall back to floating-point
291 // traverseGridSegment(start, end, visitor);
292 // }
293
294 if (maxRange < 256.0f) {
295 // Use Q8.8 (16-bit signed) if within ±127
296 traverseGridSegment16(start, end, visitor);
297 } else {
298 // Use Q24.8 (32-bit signed) if within ±8 million
299 traverseGridSegment32(start, end, visitor);
300 }
301}
302
303} // namespace fl
#define MIN(a, b)
Definition math_macros.h:15
#define FLT_MAX
Definition math_macros.h:53
#define ABS(x)
Definition math_macros.h:19
#define MAX(a, b)
Definition math_macros.h:11
void traverseGridSegmentFloat(const vec2f &start, const vec2f &end, GridVisitor &visitor)
Traverse a grid segment using floating point arithmetic.
void traverseGridSegment16(const vec2f &start, const vec2f &end, GridVisitor &visitor)
Traverse a grid segment using fixed-point 8.8 arithmetic.
vec2< float > vec2f
Definition geometry.h:151
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)
T floor(T value)
Definition math.h:11
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16