Add some more patterns

This commit is contained in:
mrcodetastic 2024-08-07 19:16:13 +01:00
parent 27a319157d
commit ad3337b3a3
7 changed files with 447 additions and 232 deletions

View file

@ -0,0 +1,235 @@
#ifndef FireWork_H
#define FireWork_H
/****************************************************************
* Fireworks Class
****************************************************************/
// Ripped from: https://github.com/lmirel/MorphingClockRemix
const int FIREWORKS = 4; // Number of fireworks
const int FIREWORK_PARTICLES = 32; // Number of particles per firework
const float GRAVITY = 0.03f;
const float baselineSpeed = -1.2f;
const float maxSpeed = -2.0f;
class Firework
{
public:
float x[FIREWORK_PARTICLES];
float y[FIREWORK_PARTICLES];
char lx[FIREWORK_PARTICLES], ly[FIREWORK_PARTICLES];
float xSpeed[FIREWORK_PARTICLES];
float ySpeed[FIREWORK_PARTICLES];
char red;
char blue;
char green;
char alpha;
int framesUntilLaunch;
char particleSize;
bool hasExploded;
Firework(); // Constructor declaration
void initialise();
void move();
void explode();
};
// Constructor implementation
Firework::Firework()
{
initialise();
for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
{
lx[loop] = 0;
ly[loop] = VPANEL_H + 1; // Push the particle location down off the bottom of the screen
}
}
void Firework::initialise()
{
// Pick an initial x location and random x/y speeds
float xLoc = (rand() % VPANEL_W);
float xSpeedVal = baselineSpeed + (rand() % (int)maxSpeed);
float ySpeedVal = baselineSpeed + (rand() % (int)maxSpeed);
// Set initial x/y location and speeds
for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
{
x[loop] = xLoc;
y[loop] = VPANEL_H + 1; // Push the particle location down off the bottom of the screen
xSpeed[loop] = xSpeedVal;
ySpeed[loop] = ySpeedVal;
//don't reset these otherwise particles won't be removed
//lx[loop] = 0;
//ly[loop] = LAYER_HEIGHT + 1; // Push the particle location down off the bottom of the screen
}
// Assign a random colour and full alpha (i.e. particle is completely opaque)
red = (rand() % 255);/// (float)RAND_MAX);
green = (rand() % 255); /// (float)RAND_MAX);
blue = (rand() % 255); /// (float)RAND_MAX);
alpha = 50;//max particle frames
// Firework will launch after a random amount of frames between 0 and 400
framesUntilLaunch = ((int)rand() % (VPANEL_H));
// Size of the particle (as thrown to glPointSize) - range is 1.0f to 4.0f
particleSize = 1.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
// Flag to keep trackof whether the firework has exploded or not
hasExploded = false;
//cout << "Initialised a firework." << endl;
}
void Firework::move()
{
for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
{
// Once the firework is ready to launch start moving the particles
if (framesUntilLaunch <= 0)
{
//draw black on last known position
//tl->drawPixel (x[loop], y[loop], cc_blk);
lx[loop] = x[loop];
ly[loop] = y[loop];
//
x[loop] += xSpeed[loop];
y[loop] += ySpeed[loop];
ySpeed[loop] += GRAVITY;
}
}
framesUntilLaunch--;
// Once a fireworks speed turns positive (i.e. at top of arc) - blow it up!
if (ySpeed[0] > 0.0f)
{
for (int loop2 = 0; loop2 < FIREWORK_PARTICLES; loop2++)
{
// Set a random x and y speed beteen -4 and + 4
xSpeed[loop2] = -2 + (rand() / (float)RAND_MAX) * 4;
ySpeed[loop2] = -2 + (rand() / (float)RAND_MAX) * 4;
}
//cout << "Boom!" << endl;
hasExploded = true;
}
}
void Firework::explode()
{
for (int loop = 0; loop < FIREWORK_PARTICLES; loop++)
{
// Dampen the horizontal speed by 1% per frame
xSpeed[loop] *= 0.99;
//draw black on last known position (NO LONGER USED tl->dim used instead)
//tl->drawPixel (x[loop], y[loop], cc_blk);
lx[loop] = x[loop];
ly[loop] = y[loop];
//
// Move the particle
x[loop] += xSpeed[loop];
y[loop] += ySpeed[loop];
// Apply gravity to the particle's speed
ySpeed[loop] += GRAVITY;
}
// Fade out the particles (alpha is stored per firework, not per particle)
if (alpha > 0)
{
alpha -= 1;
}
else // Once the alpha hits zero reset the firework
{
initialise();
}
}
/*********************** */
class PatternFirework : public Drawable {
public:
PatternFirework() {
name = (char *)"PatternFirework";
}
void start();
unsigned int drawFrame();
void stop();
private:
// Create our array of fireworks
Firework fw[FIREWORKS];
};
void PatternFirework::start() { }
void PatternFirework::stop() { }
unsigned int PatternFirework::drawFrame()
{
effects.DimAll(250);
CRGB cc_frw;
//display.fillScreen (0);
// Draw fireworks
//cout << "Firework count is: " << Firework::fireworkCount << endl;
for (int loop = 0; loop < FIREWORKS; loop++)
{
for (int particleLoop = 0; particleLoop < FIREWORK_PARTICLES; particleLoop++)
{
// Set colour to yellow on way up, then whatever colour firework should be when exploded
if (fw[loop].hasExploded == false)
{
//glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
cc_frw = CRGB (255, 255, 0);
}
else
{
//glColor4f(fw[loop].red, fw[loop].green, fw[loop].blue, fw[loop].alpha);
//glVertex2f(fw[loop].x[particleLoop], fw[loop].y[particleLoop]);
cc_frw = CRGB (fw[loop].red, fw[loop].green, fw[loop].blue);
}
// Draw the point
//glVertex2f(fw[loop].x[particleLoop], fw[loop].y[particleLoop]);
effects.setPixel (fw[loop].x[particleLoop], fw[loop].y[particleLoop], cc_frw);
// effects.setPixel (fw[loop].lx[particleLoop], fw[loop].ly[particleLoop], 0);
}
// Move the firework appropriately depending on its explosion state
if (fw[loop].hasExploded == false)
{
fw[loop].move();
}
else
{
fw[loop].explode();
}
//
//delay (10);
} // end loop
effects.ShowFrame();
return 20;
} // end drawframe
#endif

View file

@ -1,5 +1,6 @@
// Codetastic 2024
// ChatGPT was used to create this.
// It sucks.
#ifndef PatternTheMatrix_H
#define PatternTheMatrix_H

View file

@ -0,0 +1,93 @@
#ifndef PatternRain_H
#define PatternRain_H
// Codetastic 2024
struct rainDrop {
uint8_t x;
uint8_t y;
CRGB colour;
};
#define MAX_RAINDROPS 128
class PatternRain : public Drawable {
public:
PatternRain()
{
name = (char *)"PatternRain";
}
void start() {
buffer = (uint16_t *) malloc(((VPANEL_W*VPANEL_H)+1)*sizeof(uint16_t)); // always alloc an extra amount for XY
}
void stop() {
free(buffer);
}
unsigned int drawFrame()
{
rain(32, 255, 224, 240, CRGB::Green);
effects.ShowFrame();
return 45; // 1000/45 frames per secton
}
private:
struct rainDrop rainDrops[MAX_RAINDROPS];
int rainDropPos = 0;
uint16_t* buffer = NULL; // buffer of number
void rain(byte backgroundDepth, byte maxBrightness, byte spawnFreq, byte tailLength, CRGB rainColor)
{
CRGBPalette16 rain_p( CRGB::Black, rainColor );
// Dim routine
for (int16_t i = 0; i < VPANEL_W; i++) {
for (int16_t j = 0; j < VPANEL_H; j++) {
uint16_t xy = XY16(i, j);
effects.leds[xy].nscale8(tailLength);
}
}
// Genrate a new raindrop if the randomness says we should
if (random(255) < spawnFreq) {
// Find a spare raindrop slot
for (int d = 0; d < MAX_RAINDROPS; d++) {
// This raindrop is done with, it has... dropped
if (rainDrops[d].y >= VPANEL_H ) // not currently in use
{
rainDrops[d].colour = ColorFromPalette(rain_p, random(backgroundDepth, maxBrightness));
rainDrops[d].x = random(VPANEL_W-1);
rainDrops[d].y = 0;
break; // exit until next time.
}
}
} // end random spawn
// Iterate through all the rainDrops, draw the drop pixel on the layer
for (int d = 0; d < MAX_RAINDROPS; d++) {
effects.setPixel(rainDrops[d].x, rainDrops[d].y++, rainDrops[d].colour);
}
}
};
#endif

View file

@ -1,103 +0,0 @@
// Codetastic 2024
// ChatGPT was used to create this.
#ifndef PatternSphereSpin_H
#define PatternSphereSpin_H
#include <vector>
#include <cmath>
struct Point3D {
float x, y, z;
};
struct Point2D {
int x, y;
};
class PatternSphereSpin : public Drawable {
private:
std::vector<Point3D> points;
float angleX = 0.0f;
float angleY = 0.0f;
float distance = 3.0f;
Point2D project(Point3D point, float distance) {
Point2D projected;
projected.x = static_cast<int>((point.x / (distance - point.z)) * VPANEL_W / 2 + VPANEL_W / 2);
projected.y = static_cast<int>((point.y / (distance - point.z)) * VPANEL_H / 2 + VPANEL_H / 2);
return projected;
}
std::vector<Point3D> generateSpherePoints(int numPoints) {
std::vector<Point3D> points;
for (int i = 0; i < numPoints; ++i) {
float theta = 2 * PI * (i / static_cast<float>(numPoints));
for (int j = 0; j < numPoints / 2; ++j) {
float phi = PI * (j / static_cast<float>(numPoints / 2));
points.push_back({ cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi) });
}
}
return points;
}
void rotatePoints(std::vector<Point3D>& points, float angleX, float angleY) {
for (auto& point : points) {
// Rotate around the X axis
float y = point.y * cos(angleX) - point.z * sin(angleX);
float z = point.y * sin(angleX) + point.z * cos(angleX);
point.y = y;
point.z = z;
// Rotate around the Y axis
float x = point.x * cos(angleY) + point.z * sin(angleY);
z = -point.x * sin(angleY) + point.z * cos(angleY);
point.x = x;
point.z = z;
}
}
public:
PatternSphereSpin() {
name = (char *)"Sphere Spin";
}
void start()
{
points = generateSpherePoints(30);
angleX = 0.0f;
angleY = 0.0f;
distance = 3.0f;
};
unsigned int drawFrame() {
effects.ClearFrame();
// Rotate points
rotatePoints(points, angleX, angleY);
// Project and draw lines between points
for (size_t i = 0; i < points.size(); ++i) {
Point2D p1 = project(points[i], distance);
for (size_t j = i + 1; j < points.size(); ++j) {
Point2D p2 = project(points[j], distance);
effects.drawLine(p1.x, p1.y, p2.x, p2.y, CRGB(255,255,255));
}
}
// Update angles for rotation
angleX += 0.05f;
angleY += 0.05f;
effects.ShowFrame();
return 0;
}
};
#endif

View file

@ -1,77 +1,133 @@
// Codetastic 2024
// ChatGPT was used to create this.
#ifndef PatternStarfield_H
#define PatternStarfield_H
#include <vector>
struct Star
{
Star() {
x = y = z = 0;
colour = CRGB::White;
}
#define STAR_COUNT 128
// Star structure
struct Star {
float x, y, z;
float x;
float y;
float z;
CRGB colour;
};
// Based on https://github.com/sinoia/oled-starfield/blob/master/src/starfield.cpp
class PatternStarfield : public Drawable {
private:
std::vector<Star> stars;
float speed = 0.5f;
const int starCount = 100; // number of stars in the star field
const int maxDepth = 32; // maximum distance away for a star
public:
PatternStarfield() {
name = (char *)"Starfield";
// the star field - starCount stars represented as x, y and z co-ordinates
// https://www.cplusplus.com/doc/tutorial/dynamic/
Star * stars;
//CRGBPalette16 currentPalette;
unsigned int drawFrame() { // aka drawStars
// Dim routine
for (int16_t i = 0; i < VPANEL_W; i++) {
for (int16_t j = 0; j < VPANEL_H; j++) {
uint16_t xy = XY16(i, j);
effects.leds[xy].nscale8(250);
}
}
void start()
int origin_x = VPANEL_W / 2;
int origin_y = VPANEL_H / 2;
// Iterate through the stars reducing the z co-ordinate in order to move the
// star closer.
for (int i = 0; i < starCount; ++i) {
stars[i].z -= 0.1;
// if the star has moved past the screen (z < 0) reposition it far away
// with random x and y positions.
if (stars[i].z <= 0)
{
stars.resize(STAR_COUNT);
for (int i = 0; i < STAR_COUNT; ++i) {
stars[i] = { static_cast<float>(rand() % VPANEL_W - VPANEL_W / 2),
static_cast<float>(rand() % VPANEL_H - VPANEL_H / 2),
static_cast<float>(rand() % VPANEL_W),
effects.ColorFromCurrentPalette(rand()*255)
};
} // random positions
};
unsigned int drawFrame() {
effects.DimAll(250);
// Update star positions
for (auto& star : stars) {
star.z -= speed;
if (star.z <= 0) {
star.z = VPANEL_W;
star.x = static_cast<float>(rand() % VPANEL_W - VPANEL_W / 2);
star.y = static_cast<float>(rand() % VPANEL_H - VPANEL_H / 2);
}
stars[i].x = getRandom(-25, 25);
stars[i].y = getRandom(-25, 25);
stars[i].z = maxDepth;
}
// draw position
for (const auto& star : stars) {
float k = 128.0f / star.z;
int x = static_cast<int>(star.x * k + VPANEL_W / 2);
int y = static_cast<int>(star.y * k + VPANEL_H / 2);
// Convert the 3D coordinates to 2D using perspective projection.
float k = VPANEL_W / stars[i].z;
int x = static_cast<int>(stars[i].x * k + origin_x);
int y = static_cast<int>(stars[i].y * k + origin_y);
if (x >= 0 && x < VPANEL_W && y >= 0 && y < VPANEL_H) {
// Draw the star (if it is visible in the screen).
// Distant stars are smaller than closer stars.
if ((0 <= x and x < VPANEL_H)
and (0 <= y and y < VPANEL_H)) {
// TODO: Get brighter as we get closer to edges?
effects.setPixel(x, y, star.colour);
CRGB tmp = stars[i].colour;
//CRGB tmp = CRGB::White;
byte scale = 255 -(stars[i].z*7);
tmp.nscale8(scale);
effects.setPixel(x,y, CRGB(tmp.r,tmp.g,tmp.b));
}
else
{
stars[i].z = -1; // set to -1 so it gets re-popualted
}
}
effects.ShowFrame();
return 0;
return 5;
}
int getRandom(int lower, int upper) {
/* Generate and return a random number between lower and upper bound */
return lower + static_cast<int>(rand() % (upper - lower + 1));
}
/*
CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlendType blendType = LINEARBLEND) {
return ColorFromPalette(currentPalette, index, brightness, blendType);
}
*/
public:
PatternStarfield()
{
name = (char *)"PatternStarfield";
}
void start() {
//currentPalette = RainbowColors_p;
//currentPalette = CloudColors_p;
// Allocate memory
stars = new Star[starCount];
// Initialise the star field with random stars
for (int i = 0; i < starCount; i++) {
stars[i].x = getRandom(-25, 25);
stars[i].y = getRandom(-25, 25);
stars[i].z = getRandom(0, maxDepth);
//stars[i].colour = ColorFromCurrentPalette(random(0, 128));
stars[i].colour = effects.ColorFromCurrentPalette(random(0, 128));
}
} // end start
void stop() {
delete[] stars;
delete stars;
}
};
#endif

View file

@ -1,66 +0,0 @@
// Codetastic 2024
// ChatGPT was used to create this.
#ifndef PatternTunnel_H
#define PatternTunnel_H
class PatternTunnel : public Drawable {
private:
uint8_t circlePositions[5] = {0, 6, 12, 18, 24}; // Initial positions of circles
public:
PatternTunnel() {
name = (char *)"Tunnel";
}
// Function to draw a circle on the matrix
void drawCircle(int centerX, int centerY, int radius, CRGB color) {
for (int y = -radius; y <= radius; y++) {
for (int x = -radius; x <= radius; x++) {
if (x*x + y*y <= radius*radius) {
int drawX = centerX + x;
int drawY = centerY + y;
if (drawX >= 0 && drawX < VPANEL_W && drawY >= 0 && drawY < VPANEL_H) {
effects.leds[XY(drawX, drawY)] = color;
}
}
}
}
}
void start()
{
};
unsigned int drawFrame() {
effects.DimAll(250);
// Draw circles
for (int i = 0; i < 5; i++) {
int radius = circlePositions[i] % 32;
CRGB color = CHSV(map(radius, 0, 31, 0, 255), 255, 255);
drawCircle(VPANEL_W / 2, VPANEL_H / 2, radius, color);
// Move circles forward
circlePositions[i]++;
// Reset the position if the circle is out of bounds
if (circlePositions[i] >= 32) {
circlePositions[i] = 0;
}
}
effects.ShowFrame();
return 0;
}
};
#endif

View file

@ -43,17 +43,17 @@
#include "PatternSpiral.hpp"
#include "PatternSpiro.hpp"
#include "PatternWave.hpp"
#include "PatternTheMatrix.hpp"
#include "PatternRain.hpp"
#include "PatternJuliaSetFractal.hpp"
//#include "PatternTunnel.hpp" // fail
//#include "PatternSphereSpin.hpp" // fail
#include "PatternRain.hpp"
#include "PatternFireworks.hpp"
class Patterns {
private:
PatternStarfield starfield;
// PatternSphereSpin sspin;
PatternAttract attract;
PatternBounce bounce;
PatternCube cube;
@ -74,16 +74,15 @@ class Patterns {
PatternSpiral spiral;
PatternSpiro spiro;
PatternWave wave;
PatternTheMatrix matrix;
// PatternTunnel tunnel;
PatternJuliaSet juliaSet;
PatternRain rain;
PatternFirework fireworks;
std::vector<Drawable*> availablePatterns = {
// &tunnel,
&juliaSet,
&matrix,
&starfield,
// &sspin,
&attract,
&bounce,
&cube,
@ -102,10 +101,10 @@ class Patterns {
&plasma,
&radar,
&simpnoise,
//&snake, // a bit crap
//&spiral, // a bit crap
&spiro,
&wave,
&rain,
&fireworks
};
int currentIndex = 0;