// RESOLVED - RAN OUT OF MEMORY FOR MORE THAN ONE SNOWFLAKE. CHANGE PROCESSOR FOR ESP32
// DISPLAYS ONE SNOWFLAKE MOVING FROM TOP TO BOTTOM in random vertical positions
// RESOLVED - CANNOT PRINT FLAKE OUT OF BOUNDS - INVESTIGATE
// next phase of development - show multiple snowflakes
// create array to store for each snowflake
// - size (Large, Medium, Small)
// - column to decend down
// - rotation - future development
// - colour
// - current position down the screen (row or virtual row (step 2))
// - speed of desent
// - height to start from to introduce some kind of delay and speed variation
// -
// display snowflake by combining current position with snowflake dimensions.
// rotate through snowflakes so it looks like they are all moving at the same time
// large will be less frequent than medium. tere will be more small than medium
// other ideas
// -----------
// define number of lines of each snowflake/object
// define direction of movement of object in x and axis or both.
// load the snowflake designs into an array so they can be called by their position in the array
// There are 8 bits shifted in this code
// 1 bit is shifted by doubling the dimensions of the object
// 1 bit is shifted when the snowflakes are loaded into the snowflakes[] array.
// 6 bit are shifted when calling wu_pixel.
//
// All the movement in the loop is done by stepping by two (+= 2) as the odd numbered
// objects look very different to the even numbered objects
#include <Arduino.h>
#include <FastLED.h>
#include <colorutils.h>
enum XY_matrix_config {
SERPENTINE = 1,
ROWMAJOR = 2,
FLIPMAJOR = 4,
FLIPMINOR = 8
};
#define LED_PIN 21
#define MATRIX_WIDTH 40
#define MATRIX_HEIGHT 12
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
#define XY_MATRIX (SERPENTINE | ROWMAJOR)
// #define XY_MATRIX (SERPENTINE | ROWMAJOR | FLIPMINOR)
#define frame_delay 100
CRGB leds[NUM_LEDS + 1];
#define snowflakeDesignCount 5
#define num_snowLines 8
#define num_snowFlakes 2
typedef struct {
int x1, y1, x2, y2;
} sfline;
typedef struct {
sfline sflines[num_snowLines];
CRGB color;
} snowflake;
snowflake snowFlakes[num_snowFlakes];
// snowflake snowFlake1 = { // diagonal line only
// {
// {-10, -10, 0, 0}
// },
// CRGB::White
// };
snowflake snowFlake1 = { // large 1
{
{-10, -10, 0, 0},
{-3, -9, -9, -3 },
{10, -10, 0, 0},
{3, -9, 9, -3},
{0, 0, 10, 10},
{9, 3, 3, 9},
{0, 0, -10, 10},
{-9, 3, -3, 9}
},
CRGB::White
};
// snowflake snowFlake1 = { // large 2
// {
// {-10, -10, 0, 0},
// {-10, -4, -4, -10},
// {10, -10, 0, 0},
// {4, -10, 10, -4},
// {10, 10, 0, 0},
// {4, 10, 10, 4},
// {-10, 10, 0, 0},
// {-10, 4, -4, 10}
// },
// CRGB::White
// };
// snowflake snowFlake1 = { // large 3
// {
// {-10, -10, 0, 0},
// {-9, -3, -3, -9},
// {10, -10, 0, 0},
// {3, -9, 9, -3},
// {10, 10, 0, 0},
// {3, 9, 9, 3},
// {-10, 10, 0, 0},
// {-9, 3, -3, 9}
// },
// CRGB::White
// };
// snowflake snowFlake1 = { // medium 1
// {
// {-5, -5, 0, 0},
// {-6, -3, -3, -6},
// {5, -5, 0, 0},
// {3, -6, 6, -3},
// {5, 5, 0, 0},
// {3, 6, 6, 3},
// {-5, 5, 0, 0},
// {-6, 3, -3, 6}
// },
// CRGB::White
// };
// snowflake snowFlake1 = { // Small 1
// {
// {-3, -3, 0, 0},
// {-4, -2, -2, -4},
// {3, -3, 0, 0},
// {2, -4, 4, -2},
// {3, 3, 0, 0},
// {2, 4, 4, 2},
// {-3, 3, 0, 0},
// {-4, 2, -2, 4}
// },
// CRGB::White
// };
// snowflake snowFlake1 = { // Tiny 1
// {
// {-2, -2, 0, 0},
// {2, -2, 1, -1},
// {2, 2, 1, 1},
// {-2, 2, -1, 1}
// },
// CRGB::White
// };
void setup() {
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
int xCent = 11;
int yCent = 11;
int i = 0;
//for (int i = 0; i < num_snowLines; i++) {
// snowFlakes[0].sflines[i].x1 = (snowFlake1.sflines[i].x1 + xCent) << 1;
// snowFlakes[0].sflines[i].y1 = (snowFlake1.sflines[i].y1 + yCent) << 1;
// snowFlakes[0].sflines[i].x2 = (snowFlake1.sflines[i].x2 + xCent) << 1;
// snowFlakes[0].sflines[i].y2 = (snowFlake1.sflines[i].y2 + yCent) << 1;
// Serial.print("i: ");
// Serial.println(i);
// Serial.print("snowFlake1 x1: ");
// Serial.print(snowFlake1.sflines[i].x1);
// Serial.print(", y1: ");
// Serial.print(snowFlake1.sflines[i].y1);
// Serial.print(", x2: ");
// Serial.print(snowFlake1.sflines[i].x2);
// Serial.print(", y2: ");
// Serial.println(snowFlake1.sflines[i].y2);
// Serial.print("snowFlakes[0] x1: ");
// Serial.print(snowFlakes[0].sflines[i].x1);
// Serial.print(", y1: ");
// Serial.print(snowFlakes[0].sflines[i].y1);
// Serial.print(", x2: ");
// Serial.print(snowFlakes[0].sflines[i].x2);
// Serial.print(", y2: ");
// Serial.println(snowFlakes[0].sflines[i].y2);
//}
snowFlakes[0].color = snowFlake1.color;
}
void loop() {
int xCent = random8(0,45)*2;
for (int yCent = -16; yCent < 40; yCent += 2) {
for (int i = 0; i < num_snowLines; i++) {
// NOTE: bitshift changes the size of the object normlly set to << 1 but could try << 2
snowFlakes[0].sflines[i].x1 = (snowFlake1.sflines[i].x1 + xCent); // << 1;
snowFlakes[0].sflines[i].y1 = (snowFlake1.sflines[i].y1 + yCent); // << 1;
snowFlakes[0].sflines[i].x2 = (snowFlake1.sflines[i].x2 + xCent); // << 1;
snowFlakes[0].sflines[i].y2 = (snowFlake1.sflines[i].y2 + yCent); // << 1;
}
FastLED.clear();
for (int i = 0; i < num_snowLines; i++) {
draw_line(snowFlakes[0].sflines[i], (snowFlakes[0].color));
}
// draw_line(snowFlakes[0].sflines[0], (snowFlakes[0].color));
FastLED.show();
delay(frame_delay);
//while (1) {}
}
}
void draw_line(sfline line, CRGB color) {
int x1 = line.x1;
int y1 = line.y1;
int x2 = line.x2;
int y2 = line.y2;
// Serial.print("x1: ");
// Serial.print(x1);
// Serial.print(", y1: ");
// Serial.print(y1);
// Serial.print(", x2: ");
// Serial.print(x2);
// Serial.print(", y2: ");
// Serial.println(y2);
// Calculate the absolute differences in x and y coordinates.
int dx = abs(x2 - x1);
int dy = abs(y2 - y1);
// Serial.print("dx: ");
// Serial.print(dx);
// Serial.print(", dy: ");
// Serial.println(dy);
// Determine the direction of the line in x and y.
int sx = x1 < x2 ? 1 : -1;
int sy = y1 < y2 ? 1 : -1;
// Initialize the error term to handle line drawing.
int err = dx - dy;
// Initialize the starting coordinates of the line.
int x = x1;
int y = y1;
// Start an infinite loop to continue drawing the line until it's finished.
while (true) {
// Serial.print("x << 7: ");
// Serial.print(x << 7);
// Serial.print(", y << 7: ");
// Serial.println(y << 7);
// Serial.print("color r/g/b: ");
// Serial.print(color.r);
// Serial.print("/");
// Serial.print(color.g);
// Serial.print("/");
// Serial.println(color.b);
wu_pixel(x << 7, y << 7, &color);
if (x == x2 && y == y2) {
break;
}
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x += sx;
}
if (e2 < dx) {
err += dx;
y += sy;
}
}
}
// Xiaolin Wu's line algorithm for drawing a smoothed line
void wu_pixel(uint32_t x, uint32_t y, CRGB *col) {
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
uint8_t wu[4] = {((ix * iy) + ix + iy) >> 8, ((xx * iy) + xx) >> 8,
((ix * yy) + ix + yy) >> 8, ((xx * yy) + xx) >> 8
};
// Serial.print("x: ");
// Serial.print(x);
// Serial.print(", xx: ");
// Serial.print(xx);
// Serial.print(", y: ");
// Serial.print(y);
// Serial.print(", yy: ");
// Serial.print(yy);
// Serial.print(", ix: ");
// Serial.print(ix);
// Serial.print(", iy: ");
// Serial.println(iy);
for (uint8_t i = 0; i < 4; i++) {
uint16_t xy = XY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1));
CRGB *led = &leds[xy];
led->r = qadd8(led->r, col->r * wu[i] >> 8);
led->g = qadd8(led->g, col->g * wu[i] >> 8);
led->b = qadd8(led->b, col->b * wu[i] >> 8);
}
}
uint16_t XY(uint8_t x, uint8_t y) {
uint8_t major, minor, sz_major, sz_minor;
if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT)
return NUM_LEDS;
if (XY_MATRIX & ROWMAJOR)
major = x, minor = y, sz_major = MATRIX_WIDTH, sz_minor = MATRIX_HEIGHT;
else
major = y, minor = x, sz_major = MATRIX_HEIGHT, sz_minor = MATRIX_WIDTH;
if ((XY_MATRIX & FLIPMAJOR) ^ (minor & 1 && (XY_MATRIX & SERPENTINE)))
major = sz_major - 1 - major;
if (XY_MATRIX & FLIPMINOR)
minor = sz_minor - 1 - minor;
return (uint16_t) minor * sz_major + major;
}