forked from FastLED/FastLED
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathcorkscrew.h
More file actions
251 lines (196 loc) · 8.84 KB
/
corkscrew.h
File metadata and controls
251 lines (196 loc) · 8.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#pragma once
/**
* @file corkscrew.h
* @brief Corkscrew LED strip projection and rendering
*
* The Corkscrew class provides a complete solution for drawing to densely-wrapped
* helical LED strips. It maps a cylindrical coordinate system to a linear LED
* strip, allowing you to draw on a rectangular surface and have it correctly
* projected onto the corkscrew topology.
*
* Usage:
* 1. Create a Corkscrew with the number of turns and LEDs
* 2. Draw patterns on the input surface using surface()
* 3. Call draw() to map the surface to LED pixels
* 4. Access the final LED data via rawData()
*
* The class handles:
* - Automatic cylindrical dimension calculation
* - Pixel storage (external span or internal allocation)
* - Multi-sampling for smooth projections
* - Gap compensation for non-continuous wrapping
* - Iterator interface for advanced coordinate access
*
* Parameters:
* - totalTurns: Number of helical turns around the cylinder
* - numLeds: Total number of LEDs in the strip
* - invert: Reverse the mapping direction (default: false)
* - gapParams: Optional gap compensation for solder points in a strip.
*/
#include "fl/allocator.h"
#include "fl/geometry.h"
#include "fl/math.h"
#include "fl/math_macros.h"
#include "fl/pair.h"
#include "fl/tile2x2.h"
#include "fl/vector.h"
#include "fl/shared_ptr.h"
#include "fl/variant.h"
#include "fl/span.h"
#include "crgb.h"
#include "fl/int.h"
namespace fl {
// Forward declarations
class Leds;
class ScreenMap;
template<typename T> class Grid;
// Simple constexpr functions for compile-time corkscrew dimension calculation
constexpr fl::u16 calculateCorkscrewWidth(float totalTurns, fl::u16 numLeds) {
return static_cast<fl::u16>(ceil_constexpr(static_cast<float>(numLeds) / totalTurns));
}
constexpr fl::u16 calculateCorkscrewHeight(float totalTurns, fl::u16 numLeds) {
return (calculateCorkscrewWidth(totalTurns, numLeds) * static_cast<int>(ceil_constexpr(totalTurns)) > numLeds) ?
static_cast<fl::u16>(ceil_constexpr(static_cast<float>(numLeds) / static_cast<float>(calculateCorkscrewWidth(totalTurns, numLeds)))) :
static_cast<fl::u16>(ceil_constexpr(totalTurns));
}
/**
* Struct representing gap parameters for corkscrew mapping
*/
struct Gap {
int num_leds = 0; // Number of LEDs after which gap is activated, 0 = no gap
float gap = 0.0f; // Gap value from 0 to 1, represents percentage of width unit to add
Gap() = default;
Gap(float g) : num_leds(0), gap(g) {} // Backwards compatibility constructor
Gap(int n, float g) : num_leds(n), gap(g) {} // New constructor with num_leds
// Rule of 5 for POD data
Gap(const Gap &other) = default;
Gap &operator=(const Gap &other) = default;
Gap(Gap &&other) noexcept = default;
Gap &operator=(Gap &&other) noexcept = default;
};
// Maps a Corkscrew defined by the input to a cylindrical mapping for rendering
// a densly wrapped LED corkscrew.
class Corkscrew {
public:
// Pixel storage variants - can hold either external span or owned vector
using PixelStorage = fl::Variant<fl::span<CRGB>, fl::vector<CRGB, fl::allocator_psram<CRGB>>>;
// Iterator class moved from CorkscrewState
class iterator {
public:
using value_type = vec2f;
using difference_type = fl::i32;
using pointer = vec2f *;
using reference = vec2f &;
iterator(const Corkscrew *corkscrew, fl::size position)
: corkscrew_(corkscrew), position_(position) {}
vec2f operator*() const;
iterator &operator++() {
++position_;
return *this;
}
iterator operator++(int) {
iterator temp = *this;
++position_;
return temp;
}
iterator &operator--() {
--position_;
return *this;
}
iterator operator--(int) {
iterator temp = *this;
--position_;
return temp;
}
bool operator==(const iterator &other) const {
return position_ == other.position_;
}
bool operator!=(const iterator &other) const {
return position_ != other.position_;
}
difference_type operator-(const iterator &other) const {
return static_cast<difference_type>(position_) -
static_cast<difference_type>(other.position_);
}
private:
const Corkscrew *corkscrew_;
fl::size position_;
};
// Constructors that integrate input parameters directly
// Primary constructor with default values for invert and gapParams
Corkscrew(float totalTurns, fl::u16 numLeds, bool invert = false, const Gap& gapParams = Gap());
// Constructor with external pixel buffer - these pixels will be drawn to directly
Corkscrew(float totalTurns, fl::span<CRGB> dstPixels, bool invert = false, const Gap& gapParams = Gap());
Corkscrew(const Corkscrew &) = default;
Corkscrew(Corkscrew &&) = default;
// Caching control
void setCachingEnabled(bool enabled);
// Essential API - Core functionality
fl::u16 cylinderWidth() const { return mWidth; }
fl::u16 cylinderHeight() const { return mHeight; }
// Enhanced surface handling with shared_ptr
// Note: Input surface will be created on first call
fl::shared_ptr<fl::Grid<CRGB>>& getOrCreateInputSurface();
// Draw like a regular rectangle surface - access input surface directly
fl::Grid<CRGB>& surface();
// Draw the corkscrew by reading from the internal surface and populating LED pixels
void draw(bool use_multi_sampling = true);
// Pixel storage access - works with both external and owned pixels
// This represents the pixels that will be drawn after draw() is called
CRGB* rawData();
// Returns span of pixels that will be written to when draw() is called
fl::span<CRGB> data();
fl::size pixelCount() const;
// Create and return a fully constructed ScreenMap for this corkscrew
// Each LED index will be mapped to its exact position on the cylindrical surface
fl::ScreenMap toScreenMap(float diameter = 0.5f) const;
// STL-style container interface
fl::size size() const;
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, size()); }
// Non-essential API - Lower level access
vec2f at_no_wrap(fl::u16 i) const;
vec2f at_exact(fl::u16 i) const;
Tile2x2_u8_wrap at_wrap(float i) const;
// Clear all buffers and free memory
void clear();
// Fill the input surface with a color
void fillInputSurface(const CRGB& color);
private:
// For internal use. Splats the pixel on the surface which
// extends past the width. This extended Tile2x2 is designed
// to be wrapped around with a Tile2x2_u8_wrap.
Tile2x2_u8 at_splat_extrapolate(float i) const;
// Read from fl::Grid<CRGB> object and populate our internal rectangular buffer
// by sampling from the XY coordinates mapped to each corkscrew LED position
// use_multi_sampling = true will use multi-sampling to sample from the source grid,
// this will give a little bit better accuracy and the screenmap will be more accurate.
void readFrom(const fl::Grid<CRGB>& source_grid, bool use_multi_sampling = true);
// Read from rectangular buffer using multi-sampling and store in target grid
// Uses Tile2x2_u8_wrap for sub-pixel accurate sampling with proper blending
void readFromMulti(const fl::Grid<CRGB>& target_grid) const;
// Initialize the rectangular buffer if not already done
void initializeBuffer() const;
// Initialize the cache if not already done and caching is enabled
void initializeCache() const;
// Calculate the tile at position i without using cache
Tile2x2_u8_wrap calculateTileAtWrap(float i) const;
// Core corkscrew parameters (moved from CorkscrewInput)
float mTotalTurns = 19.0f; // Total turns of the corkscrew
fl::u16 mNumLeds = 144; // Number of LEDs
Gap mGapParams; // Gap parameters for gap accounting
bool mInvert = false; // If true, reverse the mapping order
// Cylindrical mapping dimensions (moved from CorkscrewState)
fl::u16 mWidth = 0; // Width of cylindrical map (circumference of one turn)
fl::u16 mHeight = 0; // Height of cylindrical map (total vertical segments)
// Enhanced pixel storage - variant supports both external and owned pixels
PixelStorage mPixelStorage;
bool mOwnsPixels = false; // Track whether we own the pixel data
// Input surface for drawing operations
fl::shared_ptr<fl::Grid<CRGB>> mInputSurface;
// Caching for Tile2x2_u8_wrap objects
mutable fl::vector<Tile2x2_u8_wrap> mTileCache;
mutable bool mCacheInitialized = false;
bool mCachingEnabled = true; // Default to enabled
};
} // namespace fl