Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Steve's Skull.
- Using Jez's Perlin Simplex noise generator functions modified slightly
- 12 Nov 2013 - Adding HSV option.
- */
- // Defines and constants
- #define F_CPU 8000000UL // The target chip will be running at 8Mhz
- // Update this to match LED driver resolution
- #define PWM_RESOLUTION 8 // bits
- #define COLOURS 3 // 3 colours, red, green and blue
- #define FRAME_DELAY 5 // milliseconds to display each frame for
- #define OFFSET_LIMIT 16384 // noise pattern stops changing after this, so reset if we reach it
- #define CIG_DRAG_INTERVAL 60000 // milliseconds between drags on the cigarette
- #define CIG_MIN 1 // minimum cig brightness, set to more than 0 to let it smoulder
- #define CIG_MAX 255 // maximum cig brightness
- // Map colour names to array positions
- #define RED 0
- #define GREEN 1
- #define BLUE 2
- #define CIG_PIN 6 // the cigarette
- #define COLOUR_MODE_PIN 7 // a switch to allow the user to change between RGB and HSV colour modes
- // LED pins (RED, GREEN, BLUE)
- int led_pins[COLOURS] = {11, 10, 9};
- // Simplex noise parameters:
- // obtained empirically and used in scaling returned values to match the PWM_RESOLUTION
- const double NOISE_LIMIT = 0.312768; // the noise generator returns values between -/+ this (on the Arduino)
- // These are used to adjust the hue and brightness distributions. Simplex produces a normalish distribution between +/- noise limit
- // but sharply peaked around the mean of zero. These parameters are used to expand that peak wider to produce
- // a smooth distribution that's closer to linear.
- const double HUE_WIDTH = 12.0;
- const double BRI_WIDTH = 8.0;
- // used to store the current position in the noise space. Global so it's persistent between calls
- double offset = 0;
- // Increment range, smaller is a slower walk through the noise space,
- // which means smaller colour differences between frames and a slower colour progression.
- double time_inc;
- // calulate the highest allowable value from the bit resolution. Used in scaling
- // Simplex noise returned values to the limits of the PWM_RESOLUTION available.
- int LED_MAX_LEVEL = (1 << PWM_RESOLUTION) - 1;
- // some working containers used by the Perlin noise generator
- // These are modified in multiple subs. Easier to make them global than keep passing them around
- int i, j, k, A[] = {0, 0, 0};
- double u, v, w, s;
- const double onethird = 0.333333333;
- const double onesixth = 0.166666667;
- // Not sure what these represent.
- int T[] = {0x15, 0x38, 0x32, 0x2c, 0x0d, 0x13, 0x07, 0x2a}; // {21, 56, 50, 44, 13, 19, 7, 42)
- // To hold the values from the Simplex generator
- int colours[COLOURS];
- void setup() {
- pinMode(COLOUR_MODE_PIN, OUTPUT);
- digitalWrite(COLOUR_MODE_PIN, HIGH); // enable internal pullup
- for (int i=0;i<COLOURS;i++) {
- pinMode(led_pins[i], OUTPUT);
- }
- }
- void loop() {
- unsigned long ciggie_time = 0;
- float cig_brightness = 0;
- float fade_direction = 1; // start by going up
- int i;
- for (;;) { // forever
- generateSimplexNoisePattern(offset);
- analogWrite(led_pins[RED], colours[RED]);
- analogWrite(led_pins[GREEN], colours[GREEN]);
- analogWrite(led_pins[BLUE], colours[BLUE]);
- if (offset > OFFSET_LIMIT) {
- offset = 0;
- } else {
- offset += time_inc;
- }
- // Cigarette stuff
- if (millis() > ciggie_time) {
- cig_brightness += fade_direction;
- analogWrite(CIG_PIN, int(cig_brightness));
- if (cig_brightness == CIG_MAX) {
- fade_direction = -0.5;
- } else if (cig_brightness == CIG_MIN) {
- fade_direction = 1;
- ciggie_time = millis() + CIG_DRAG_INTERVAL;
- }
- }
- delay(5);
- }
- }
- void generateSimplexNoisePattern( double yoffset) {
- // Calculate simplex noise for LEDs that are nodes:
- if (digitalRead(COLOUR_MODE_PIN) == HIGH) { // RGB
- time_inc = 0.0005;
- // Store raw values from simplex function
- double r = SimplexNoise(yoffset,0, 0);
- double g = SimplexNoise(yoffset,1, 0);
- double b = SimplexNoise(yoffset,2, 0);
- // Convert values from raw noise to scaled r,g,b and feed to LEDs.
- // Raw noise is +/-NOISE_LIMIT.
- // Result converted to int here to save a step, some detail is lost, but not enough to worry about.
- // Difference is about +/- 2 with a 12 bit range (0-4095) or 0.05%
- colours[RED] = (r + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
- colours[GREEN] = (g + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
- colours[BLUE] = (b + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
- } else { // HSV
- int saturation = LED_MAX_LEVEL; // could play with this too
- double h = SimplexNoise(yoffset,0, 0);
- double v = SimplexNoise(yoffset,2, 0);
- time_inc = 0.0001;
- // Convert values from raw noise to scaled r,g,b and feed to LEDs.
- // Raw noise is +/-NOISE_LIMIT.
- // Result converted to int here to save a step, some detail is lost, but not enough to worry about.
- // Difference is about +/- 2 with a 12 bit range (0-4095) or 0.05%
- // Conversion for hue shifts a simplex distribution with mean about zero and distribution over +/- NOISE_LIMIT
- // to a flatter distribution between 0..1 with smooth wrap around.
- // This isn't flat, it's bimodal around 0 and 1, but increasing the HUE_WIDTH factor here
- // increases the width of those humps. If there isn't enough green coming through, then raise this.
- h = constrain(h * NOISE_LIMIT * HUE_WIDTH, -0.999, 0.999); // Expand from +/- NOISE_LIMIT to +/- 1:
- if ( h < 0 ) { // Convert -1..1 range with (roughly) normal mean about zero to flatter distribution across 0..1 range.
- h = 1 + h;
- }
- int hue = h * LED_MAX_LEVEL; // Expand 0..1 range to 0..MAX.
- // Conversion for brightness shifts a simplex distribution with mean about zero and distribution over +/- NOISE_LIMIT
- // to a flatter distribution between 0..1 with no wrap around. This distribution is not really flat, just
- // a widened simplex (roughly normal) chopped off at min and min.
- // BRI_WIDTH here adjusts the variability of the brightness. Increasing it results in more time spent at max or min (in this case, off or full brightness).
- // Decreasing it results in more time spent at half brightness.
- v = v * NOISE_LIMIT * BRI_WIDTH;
- v = 0.5 + v;
- int brightness = constrain(v * LED_MAX_LEVEL, 0, LED_MAX_LEVEL);
- // Convert the HSV values to RGB and set the colours
- setRGB(hue, saturation, brightness);
- }
- }
- /*****************************************************************************/
- // Simplex noise code:
- // From an original algorithm by Ken Perlin.
- // Returns a value in the range of about [-0.313 .. 0.347] <-- this may not be true depending on your c/c++ implementation and target processor
- double SimplexNoise(double x, double y, double z) {
- // Skew input space to relative coordinate in simplex cell
- s = (x + y + z) * onethird;
- i = fastfloor(x+s);
- j = fastfloor(y+s);
- k = fastfloor(z+s);
- // Unskew cell origin back to (x, y , z) space
- s = (i + j + k) * onesixth;
- u = x - i + s;
- v = y - j + s;
- w = z - k + s;;
- A[0] = A[1] = A[2] = 0;
- // For 3D case, the simplex shape is a slightly irregular tetrahedron.
- // Determine which simplex we're in
- int hi = u >= w ? u >= v ? 0 : 1 : v >= w ? 1 : 2;
- int lo = u < w ? u < v ? 0 : 1 : v < w ? 1 : 2;
- return k_fn(hi) + k_fn(3 - hi - lo) + k_fn(lo) + k_fn(0);
- }
- int fastfloor(double n) {
- return n > 0 ? (int) n : (int) n - 1;
- }
- double k_fn(int a) {
- s = (A[0] + A[1] + A[2]) * onesixth;
- double x = u - A[0] + s;
- double y = v - A[1] + s;
- double z = w - A[2] + s;
- double t = 0.6f - x * x - y * y - z * z;
- int h = shuffle(i + A[0], j + A[1], k + A[2]);
- A[a]++;
- if (t < 0) return 0;
- int b5 = h >> 5 & 1;
- int b4 = h >> 4 & 1;
- int b3 = h >> 3 & 1;
- int b2 = h >> 2 & 1;
- int b = h & 3;
- double p = b == 1 ? x : b == 2 ? y : z;
- double q = b == 1 ? y : b == 2 ? z : x;
- double r = b == 1 ? z : b == 2 ? x : y;
- p = b5 == b3 ? -p : p;
- q = b5 == b4 ? -q: q;
- r = b5 != (b4^b3) ? -r : r;
- t *= t;
- return 8 * t * t * (p + (b == 0 ? q + r : b2 == 0 ? q : r));
- }
- int shuffle(int i, int j, int k) {
- return b(i, j, k, 0) + b(j, k, i, 1) + b(k, i, j, 2) + b(i, j, k, 3) + b(j, k, i, 4) + b(k, i, j, 5) + b(i, j, k, 6) + b(j, k, i, 7);
- }
- int b(int i, int j, int k, int B) {
- return T[b(i, B) << 2 | b(j, B) << 1 | b(k, B)];
- }
- int b(int N, int B) {
- return N >> B & 1;
- }
- // HSV to RGB code from Kasper Kamperman, adapted by Jez Weston.
- // http://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/
- void setRGB(int hue, int sat, int bri) {
- // Convert hue, saturation and brightness ( HSB/HSV ) to RGB
- int r, g, b;
- long base, divisions;
- if (sat == 0) { // Acromatic color (gray). Hue doesn't matter.
- colours[0]=bri;
- colours[1]=bri;
- colours[2]=bri;
- }
- else {
- base = ( (long)(LED_MAX_LEVEL - sat) * (long)bri )>>PWM_RESOLUTION;
- // Divide the HSV space into divisions
- divisions = 43;
- switch( int(hue/divisions) ) {
- case 0:
- r = bri;
- g = (((bri-base)*hue)/divisions)+base;
- b = base;
- break;
- case 1:
- r = (((bri-base)*(divisions-(hue%divisions)))/divisions)+base;
- g = bri;
- b = base;
- break;
- case 2:
- r = base;
- g = bri;
- b = (((bri-base)*(hue%divisions))/divisions)+base;
- break;
- case 3:
- r = base;
- g = (((bri-base)*(divisions-(hue%divisions)))/divisions)+base;
- b = bri;
- break;
- case 4:
- r = (((bri-base)*(hue%divisions))/divisions)+base;
- g = base;
- b = bri;
- break;
- case 5:
- r = bri;
- g = base;
- b = (((bri-base)*(divisions-(hue%divisions)))/divisions)+base;
- break;
- }
- colours[RED]=r;
- colours[GREEN]=g;
- colours[BLUE]=b;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement