FastLED 3.9.3
Loading...
Searching...
No Matches
gen.py
1"""
2Generates the hexegon using math.
3"""
4
5
6from dataclasses import dataclass
7from enum import Enum
8import json
9
10
11from math import pi, cos, sin
12
13LED_PER_STRIP = 14
14SPACE_PER_LED = 30.0 # Increased for better visibility
15LED_DIAMETER = SPACE_PER_LED / 4
16MIRROR_X = True # Diagramed from the reverse side. Reverse the x-axis
17
18SMALLEST_ANGLE = 360 / 6
19
20
21class HexagonAngle(Enum):
22 UP = 90
23 DOWN = 270
24 RIGHT_UP = 30
25 RIGHT_DOWN = 360 - 30
26 LEFT_UP = 150 # (RIGHT_DOWN + 180) % 360
27 LEFT_DOWN = 210 # (RIGHT_UP + 180) % 360
28
29
30def toRads(angle: float) -> float:
31 return angle * (pi / 180)
32
33
34@dataclass
35class Point:
36 x: float
37 y: float
38
39 @staticmethod
40 def toJson(points: list["Point"]) -> list[dict]:
41 x_values = [p.x for p in points]
42 y_values = [p.y for p in points]
43 # round
44 x_values = [round(x, 4) for x in x_values]
45 y_values = [round(y, 4) for y in y_values]
46 if MIRROR_X:
47 x_values = [-x for x in x_values]
48
49 return {"x": x_values, "y": y_values, "diameter": LED_DIAMETER}
50
51 def copy(self) -> "Point":
52 return Point(self.x, self.y)
53
54 def __repr__(self) -> str:
55 x_rounded = round(self.x, 2)
56 y_rounded = round(self.y, 2)
57 return f"({x_rounded}, {y_rounded})"
58
59
60def next_point(pos: Point, angle: HexagonAngle, space: float) -> Point:
61 degrees = angle.value
62 angle_rad = toRads(degrees)
63 x = pos.x + space * cos(angle_rad)
64 y = pos.y + space * sin(angle_rad)
65 return Point(x, y)
66
67
68def gen_points(
69 input: list[HexagonAngle], leds_per_strip: int, startPos: Point,
70 exclude: list[int] | None = None,
71 add_last: bool = False
72) -> list[Point]:
73 points: list[Point] = []
74 if (not input) or (not leds_per_strip):
75 return points
76 exclude = exclude or []
77 # Start FSM. Start pointer get's put into the accumulator.
78 curr_point: Point = Point(startPos.x, startPos.y)
79 # points.append(curr_point)
80 last_angle = input[0]
81 for i,angle in enumerate(input):
82 excluded = i in exclude
83 values = list(range(leds_per_strip))
84 last_angle = angle
85 for v in values:
86 last_angle = angle
87 curr_point = next_point(curr_point, angle, SPACE_PER_LED)
88 if not excluded:
89 points.append(curr_point)
90 #if i == len(input) - 1:
91 # break
92 # Next starting point
93 curr_point = next_point(curr_point, last_angle, SPACE_PER_LED)
94 #if not excluded:
95 # points.append(curr_point)
96 if add_last:
97 points.append(curr_point)
98 return points
99
100
101
102def main() -> None:
103 startPos = Point(0, 0)
104 hexagon_angles = [
105 HexagonAngle.UP,
106 HexagonAngle.RIGHT_UP,
107 HexagonAngle.RIGHT_DOWN,
108 HexagonAngle.DOWN,
109 HexagonAngle.LEFT_DOWN,
110 HexagonAngle.LEFT_UP,
111 ]
112 points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
113
114 print(points)
115
116
117
118def simple_test() -> None:
119 startPos = Point(0, 0)
120 hexagon_angles = [
121 HexagonAngle.UP,
122 ]
123 points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
124 print(points)
125 # assert len(points) == LED_PER_STRIP + 1
126
127def two_angle_test() -> None:
128 startPos = Point(0, 0)
129 hexagon_angles = [
130 HexagonAngle.UP,
131 HexagonAngle.UP,
132 ]
133 points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
134 print(points)
135 # assert len(points) == LED_PER_STRIP * 2, f"Expected {LED_PER_STRIP * 2} points, got {len(points)} points"
136
137
138
139def two_angle_test2() -> None:
140 print("two_angle_test2")
141 startPos = Point(0, 0)
142 hexagon_angles = [
143 HexagonAngle.UP,
144 HexagonAngle.DOWN,
145 ]
146 points = gen_points(hexagon_angles, LED_PER_STRIP, startPos)
147 print(points)
148 # assert len(points) == LED_PER_STRIP * 2, f"Expected {LED_PER_STRIP * 2} points, got {len(points)} points"
149
150# Red is defined by this instruction tutorial: https://voidstar.dozuki.com/Guide/Chromance+Assembly+Instructions/6
151def find_red_anchor_point() -> list[Point]:
152 hexagon_angles = [
153 HexagonAngle.LEFT_UP,
154 HexagonAngle.LEFT_UP,
155 HexagonAngle.UP,
156 HexagonAngle.RIGHT_UP,
157 ]
158 points = gen_points(hexagon_angles, LED_PER_STRIP, Point(0, 0), add_last=True)
159 return points
160
161def find_green_anchore_point() -> list[Point]:
162 hexagon_angles = [
163 HexagonAngle.RIGHT_UP,
164 HexagonAngle.RIGHT_UP,
165 HexagonAngle.UP,
166 ]
167 points = gen_points(hexagon_angles, LED_PER_STRIP, Point(0, 0), add_last=True)
168 return points
169
170
171RED_ANCHOR_POINT = find_red_anchor_point()[-1]
172BLACK_ANCHOR_POINT = Point(0,0) # Black
173GREEN_ANCHOR_POINT = find_green_anchore_point()[-1]
174BLUE_ANCHOR_POINT = Point(0, 0)
175
176
177def generate_red_points() -> list[Point]:
178 starting_point = RED_ANCHOR_POINT.copy()
179 hexagon_angles = [
180 HexagonAngle.UP,
181 HexagonAngle.LEFT_UP,
182 HexagonAngle.LEFT_DOWN,
183 HexagonAngle.DOWN,
184 HexagonAngle.RIGHT_DOWN,
185 HexagonAngle.UP,
186 HexagonAngle.LEFT_UP
187 ]
188 points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point, exclude=[5])
189 return points
190
191
192def generate_black_points() -> list[Point]:
193 starting_point = BLACK_ANCHOR_POINT.copy()
194 hexagon_angles = [
195 HexagonAngle.LEFT_UP,
196 HexagonAngle.LEFT_UP,
197 HexagonAngle.UP,
198 HexagonAngle.RIGHT_UP,
199 HexagonAngle.RIGHT_DOWN,
200 HexagonAngle.DOWN,
201 HexagonAngle.LEFT_DOWN,
202 HexagonAngle.UP,
203 HexagonAngle.LEFT_UP,
204 HexagonAngle.UP,
205 HexagonAngle.RIGHT_UP,
206 ]
207 points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point)
208 return points
209
210
211def generate_green_points() -> list[Point]:
212 starting_point = GREEN_ANCHOR_POINT.copy()
213 hexagon_angles = [
214 HexagonAngle.RIGHT_UP,
215 HexagonAngle.UP,
216 HexagonAngle.LEFT_UP,
217 HexagonAngle.LEFT_DOWN,
218 HexagonAngle.DOWN,
219 HexagonAngle.RIGHT_DOWN, # skip
220 HexagonAngle.LEFT_DOWN, # skip
221 HexagonAngle.LEFT_UP,
222 HexagonAngle.UP,
223 HexagonAngle.RIGHT_UP,
224 HexagonAngle.LEFT_UP,
225 HexagonAngle.LEFT_DOWN,
226 HexagonAngle.RIGHT_DOWN,
227 HexagonAngle.RIGHT_UP, # skip
228 HexagonAngle.RIGHT_DOWN,
229 ]
230 points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point, exclude=[5,6,13])
231 return points
232
233def generate_blue_points() -> list[Point]:
234 starting_point = BLUE_ANCHOR_POINT.copy()
235 hexagon_angles = [
236 HexagonAngle.RIGHT_UP,
237 HexagonAngle.RIGHT_UP,
238 HexagonAngle.UP,
239 HexagonAngle.LEFT_UP,
240 HexagonAngle.LEFT_DOWN,
241 HexagonAngle.LEFT_DOWN,
242 HexagonAngle.RIGHT_DOWN, # skip
243 HexagonAngle.RIGHT_DOWN,
244 HexagonAngle.UP,
245 HexagonAngle.RIGHT_UP,
246 HexagonAngle.UP,
247 HexagonAngle.RIGHT_UP,
248 ]
249 points = gen_points(hexagon_angles, LED_PER_STRIP, starting_point, exclude=[6])
250 return points
251
252def unit_test() -> None:
253 #simple_test()
254 #two_angle_test()
255 out = {}
256 map = out.setdefault("map", {})
257 map.update({
258 "red_segment": Point.toJson(generate_red_points()),
259 "back_segment": Point.toJson(generate_black_points()),
260 "green_segment": Point.toJson(generate_green_points()),
261 "blue_segment": Point.toJson(generate_blue_points()),
262 })
263 print(json.dumps(out))
264 # write it out to a file
265 with open("output.json", "w") as f:
266 f.write(json.dumps(out))
267
268
269if __name__ == "__main__":
270 unit_test()