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