Last active
February 26, 2024 13:34
-
-
Save sutaburosu/4c2a64796635c1046ce4a29adc5373e9 to your computer and use it in GitHub Desktop.
An 8x32 variant of the leaping dots thing for u/tomchaps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// by <[email protected]> | |
// License: Creative Commons CC0 | |
#include <Arduino.h> | |
#include <FastLED.h> | |
#include <colorutils.h> | |
enum XY_matrix_config { | |
SERPENTINE = 1, | |
ROWMAJOR = 2, | |
FLIPMAJOR = 4, | |
FLIPMINOR = 8 | |
}; | |
#define BRIGHTNESS 32 | |
#define LED_PIN 2 | |
#define COLOR_ORDER GRB | |
#define CHIPSET WS2812B | |
#define kMatrixWidth 8 | |
#define kMatrixHeight 32 | |
#define XY_MATRIX (SERPENTINE | ROWMAJOR | FLIPMINOR) | |
#define MS_GOAL 10 // to try maintain 1000 / 10ms == 100 FPS | |
#define NUM_LEAPERS 8 | |
#define GRAVITY 10 | |
#define SETTLED_THRESHOLD 48 | |
#define WALL_FRICTION 248 // 255 is no friction | |
#define DRAG 240 // 255 is no wind resistance | |
#define SERIAL_UI 1 | |
#define NUM_LEDS ((kMatrixWidth) * (kMatrixHeight)) | |
CRGB leds[NUM_LEDS + 1]; // 1 extra for XY() to use when out-of-bounds | |
uint16_t XY(uint8_t x, uint8_t y) { | |
uint8_t major, minor, sz_major, sz_minor; | |
if (x >= kMatrixWidth || y >= kMatrixHeight) | |
return NUM_LEDS; | |
if (XY_MATRIX & ROWMAJOR) | |
major = x, minor = y, sz_major = kMatrixWidth, sz_minor = kMatrixHeight; | |
else | |
major = y, minor = x, sz_major = kMatrixHeight, sz_minor = kMatrixWidth; | |
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; | |
} | |
/////////////////////////////////////////////////////////////////////// | |
CRGBPalette16 currentPalette = RainbowColors_p; | |
uint32_t last_millis = 0; | |
typedef struct { | |
int16_t x, y, xd, yd; | |
// uint8_t state; | |
} Leaper; | |
Leaper leapers[NUM_LEAPERS]; | |
extern "C" { | |
void restart_leaper(Leaper * lpr); | |
void move_leaper(Leaper * lpr); | |
} | |
void setup() { | |
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS); | |
FastLED.setCorrection(UncorrectedColor); | |
FastLED.setTemperature(UncorrectedTemperature); | |
FastLED.setDither(DISABLE_DITHER); | |
FastLED.setBrightness(BRIGHTNESS); | |
pinMode(LED_BUILTIN, OUTPUT); | |
if (SERIAL_UI) { | |
Serial.begin(250000); | |
} | |
for (uint8_t lpr = 0; lpr < NUM_LEAPERS; lpr++) { | |
restart_leaper(&leapers[lpr]); | |
leapers[lpr].x = random8() * kMatrixWidth; | |
leapers[lpr].y = random8() * kMatrixHeight; | |
} | |
} | |
void loop() { | |
FastLED.clear(); | |
// fadeToBlackBy(leds, NUM_LEDS, 64); | |
for (uint8_t lpr = 0; lpr < NUM_LEAPERS; lpr++) { | |
move_leaper(&leapers[lpr]); | |
CRGB rgb = ColorFromPalette(currentPalette, lpr * (255 / NUM_LEAPERS), 255, LINEARBLEND); | |
if (kMatrixWidth > 1) | |
wu_pixel(leapers[lpr].x, leapers[lpr].y, &rgb); | |
else | |
wu_pixel1d(leapers[lpr].y, &rgb); | |
} | |
// cap the frame rate and indicate idle time via the built-in LED | |
uint32_t frame_time = millis() - last_millis; | |
int8_t pause = MS_GOAL - frame_time; | |
if (pause < 0 && SERIAL_UI) { Serial.print(-pause); Serial.println("ms late"); } | |
digitalWrite(LED_BUILTIN, HIGH); | |
if (pause > 0) delay(pause); | |
digitalWrite(LED_BUILTIN, LOW); | |
last_millis = millis(); | |
FastLED.show(); | |
} | |
void restart_leaper(Leaper * lpr) { | |
// leap up and to the side with some random component | |
lpr->xd = random8() + 32; | |
lpr->yd = random8() + 512; | |
// for variety, sometimes go 50% faster | |
if (random8() < 12) { | |
lpr->xd += lpr->xd >> 1; | |
lpr->yd += lpr->yd >> 1; | |
} | |
// leap towards the centre of the screen | |
if (lpr->x > (kMatrixWidth / 2 * 256)) { | |
lpr->xd = -lpr->xd; | |
} | |
} | |
void move_leaper(Leaper * lpr) { | |
// add the X & Y velocities to the position | |
lpr->x += lpr->xd; | |
lpr->y += lpr->yd; | |
// bounce off the floor and ceiling? | |
if (lpr->y < 0 || lpr->y >= ((kMatrixHeight - 1) << 8)) { | |
lpr->xd = ((int32_t) lpr->xd * WALL_FRICTION) >> 8; | |
lpr->yd = ((int32_t) -lpr->yd * WALL_FRICTION) >> 8; | |
if (lpr->y < 0) lpr->y = -lpr->y; | |
// settled on the floor? | |
if (lpr->y <= SETTLED_THRESHOLD && abs(lpr->yd) <= SETTLED_THRESHOLD) { | |
restart_leaper(lpr); | |
} | |
} | |
// bounce off the sides of the screen? | |
if (lpr->x <= 0 || lpr->x >= (kMatrixWidth - 1) << 8) { | |
lpr->xd = ((int32_t) -lpr->xd * WALL_FRICTION) >> 8; | |
lpr->yd = ((int32_t) lpr->yd * WALL_FRICTION) >> 8; | |
if (lpr->x <= 0) { | |
lpr->x = -lpr->x; | |
} else { | |
lpr->x = ((2 * kMatrixWidth - 1) << 8) - lpr->x; | |
} | |
} | |
// gravity | |
lpr->yd -= GRAVITY; | |
// viscosity, done badly | |
// uint32_t speed2 = lpr->xd * lpr->xd + lpr->yd * lpr->yd; | |
lpr->xd = ((int32_t) lpr->xd * DRAG) >> 8; | |
lpr->yd = ((int32_t) lpr->yd * DRAG) >> 8; | |
} | |
// x and y are 24.8 fixed point | |
// Not Ray Wu. ;) The idea came from Xiaolin Wu. | |
void wu_pixel(uint32_t x, uint32_t y, CRGB * col) { | |
// extract the fractional parts and derive their inverses | |
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; | |
// calculate the intensities for each affected pixel | |
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) | |
uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy), | |
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)}; | |
// multiply the intensities by the colour, and saturating-add them to the pixels | |
for (uint8_t i = 0; i < 4; i++) { | |
uint16_t xy = XY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1)); | |
leds[xy].r = qadd8(leds[xy].r, col->r * wu[i] >> 8); | |
leds[xy].g = qadd8(leds[xy].g, col->g * wu[i] >> 8); | |
leds[xy].b = qadd8(leds[xy].b, col->b * wu[i] >> 8); | |
} | |
} | |
// note that this will write to NUM_LEDS + 1 | |
void wu_pixel1d(uint32_t y, CRGB * col) { | |
// extract the fractional parts and derive their inverses | |
uint8_t yy = y & 0xff, iy = 255 - yy; | |
y = y >> 8; | |
uint8_t wu[2] = {iy, yy}; | |
// multiply the intensities by the colour, and saturating-add them to the pixels | |
for (uint8_t i = 0; i < 2; i++) { | |
leds[y].r = qadd8(leds[y].r, col->r * wu[i] >> 8); | |
leds[y].g = qadd8(leds[y].g, col->g * wu[i] >> 8); | |
leds[y].b = qadd8(leds[y].b, col->b * wu[i] >> 8); | |
y++; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment