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
|
#ifndef PatternStarfield_H
#define PatternStarfield_H
struct Star
{
Star() {
x = y = z = 0;
colour = CRGB::White;
}
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:
const int starCount = 100; // number of stars in the star field
const int maxDepth = 32; // maximum distance away for a star
// 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);
}
}
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[i].x = getRandom(-25, 25);
stars[i].y = getRandom(-25, 25);
stars[i].z = maxDepth;
}
// 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);
// 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)) {
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 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;
}
};
#endif
|