Advertisement
grist

Steve's Skull

Nov 17th, 2013
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.04 KB | None | 0 0
  1. /*
  2.  
  3.     Steve's Skull.
  4.    
  5.     Using Jez's Perlin Simplex noise generator functions modified slightly
  6.    
  7.     12 Nov 2013 - Adding HSV option.
  8.  
  9.  
  10. */
  11.  
  12.  
  13. // Defines and constants
  14. #define F_CPU 8000000UL  //  The target chip will be running at 8Mhz
  15. // Update this to match LED driver resolution
  16. #define PWM_RESOLUTION 8 // bits
  17. #define COLOURS 3 // 3 colours, red, green and blue
  18. #define FRAME_DELAY 5 // milliseconds to display each frame for
  19. #define OFFSET_LIMIT 16384  // noise pattern stops changing after this, so reset if we reach it
  20. #define CIG_DRAG_INTERVAL 60000 // milliseconds between drags on the cigarette
  21. #define CIG_MIN 1 // minimum cig brightness, set to more than 0 to let it smoulder
  22. #define CIG_MAX 255 // maximum cig brightness
  23. // Map colour names to array positions
  24. #define RED 0
  25. #define GREEN 1
  26. #define BLUE 2
  27. #define CIG_PIN 6 // the cigarette
  28. #define COLOUR_MODE_PIN 7 // a switch to allow the user to change between RGB and HSV colour modes
  29. // LED pins (RED, GREEN, BLUE)
  30. int led_pins[COLOURS] = {11, 10, 9};
  31.  
  32. // Simplex noise parameters:
  33. // obtained empirically and used in scaling returned values to match the PWM_RESOLUTION
  34. const double NOISE_LIMIT = 0.312768; // the noise generator returns values between -/+ this (on the Arduino)
  35. // These are used to adjust the hue and brightness distributions. Simplex produces a normalish distribution between +/- noise limit
  36. // but sharply peaked around the mean of zero. These parameters are used to expand that peak wider to produce
  37. // a smooth distribution that's closer to linear.
  38. const double HUE_WIDTH = 12.0;
  39. const double BRI_WIDTH = 8.0;
  40.  
  41. // used to store the current position in the noise space. Global so it's persistent between calls
  42. double offset = 0;
  43. // Increment range, smaller is a slower walk through the noise space,
  44. // which means smaller colour differences between frames and a slower colour progression.
  45. double time_inc;
  46. // calulate the highest allowable value from the bit resolution. Used in scaling
  47. // Simplex noise returned values to the limits of the PWM_RESOLUTION available.
  48. int LED_MAX_LEVEL = (1 << PWM_RESOLUTION) - 1;
  49. // some working containers used by the Perlin noise generator
  50. // These are modified in multiple subs. Easier to make them global than keep passing them around
  51. int i, j, k, A[] = {0, 0, 0};
  52. double u, v, w, s;
  53. const double onethird = 0.333333333;
  54. const double onesixth = 0.166666667;
  55. // Not sure what these represent.
  56. int T[] = {0x15, 0x38, 0x32, 0x2c, 0x0d, 0x13, 0x07, 0x2a}; // {21, 56, 50, 44, 13, 19, 7, 42)
  57. // To hold the values from the Simplex generator
  58. int colours[COLOURS];
  59.  
  60.  
  61. void setup() {
  62.    pinMode(COLOUR_MODE_PIN, OUTPUT);
  63.    digitalWrite(COLOUR_MODE_PIN, HIGH); // enable internal pullup
  64.    for (int i=0;i<COLOURS;i++) {
  65.        pinMode(led_pins[i], OUTPUT);
  66.    }
  67. }
  68.  
  69. void loop() {
  70.   unsigned long ciggie_time = 0;
  71.   float cig_brightness = 0;
  72.   float fade_direction = 1; // start by going up
  73.   int i;
  74.   for (;;) { // forever
  75.     generateSimplexNoisePattern(offset);
  76.     analogWrite(led_pins[RED], colours[RED]);
  77.     analogWrite(led_pins[GREEN], colours[GREEN]);
  78.     analogWrite(led_pins[BLUE], colours[BLUE]);
  79.     if (offset > OFFSET_LIMIT) {
  80.       offset = 0;
  81.     } else {
  82.       offset += time_inc;
  83.     }
  84.     // Cigarette stuff
  85.     if (millis() > ciggie_time) {
  86.         cig_brightness += fade_direction;
  87.         analogWrite(CIG_PIN, int(cig_brightness));
  88.         if (cig_brightness == CIG_MAX) {
  89.             fade_direction = -0.5;
  90.         } else if (cig_brightness == CIG_MIN) {
  91.             fade_direction = 1;
  92.             ciggie_time = millis() + CIG_DRAG_INTERVAL;
  93.         }
  94.  
  95.      }
  96.      
  97.      delay(5);
  98.   }
  99. }
  100.  
  101.  
  102. void generateSimplexNoisePattern( double yoffset) {
  103.     // Calculate simplex noise for LEDs that are nodes:
  104.    
  105.     if (digitalRead(COLOUR_MODE_PIN) == HIGH) { // RGB
  106.        
  107.         time_inc = 0.0005;
  108.         // Store raw values from simplex function
  109.         double r = SimplexNoise(yoffset,0, 0);
  110.         double g = SimplexNoise(yoffset,1, 0);
  111.         double b = SimplexNoise(yoffset,2, 0);
  112.         // Convert values from raw noise to scaled r,g,b and feed to LEDs.
  113.         // Raw noise is +/-NOISE_LIMIT.
  114.         // Result converted to int here to save a step, some detail is lost, but not enough to worry about.
  115.         // Difference is about +/- 2 with a 12 bit range (0-4095) or 0.05%
  116.         colours[RED] = (r + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
  117.         colours[GREEN] = (g + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
  118.         colours[BLUE] = (b + NOISE_LIMIT) * LED_MAX_LEVEL/(NOISE_LIMIT*2);
  119.     } else { // HSV
  120.         int saturation = LED_MAX_LEVEL; // could play with this too
  121.         double h = SimplexNoise(yoffset,0, 0);
  122.         double v = SimplexNoise(yoffset,2, 0);
  123.         time_inc  = 0.0001;
  124.         // Convert values from raw noise to scaled r,g,b and feed to LEDs.
  125.         // Raw noise is +/-NOISE_LIMIT.
  126.         // Result converted to int here to save a step, some detail is lost, but not enough to worry about.
  127.         // Difference is about +/- 2 with a 12 bit range (0-4095) or 0.05%
  128.        
  129.         // Conversion for hue shifts a simplex distribution with mean about zero and distribution over +/- NOISE_LIMIT
  130.         // to a flatter distribution between 0..1 with smooth wrap around.
  131.         // This isn't flat, it's bimodal around 0 and 1, but increasing the HUE_WIDTH factor here
  132.         // increases the width of those humps. If there isn't enough green coming through, then raise this.
  133.         h = constrain(h * NOISE_LIMIT * HUE_WIDTH, -0.999, 0.999);  // Expand from +/- NOISE_LIMIT to +/- 1:
  134.         if ( h < 0 ) {          // Convert -1..1 range with (roughly) normal mean about zero to flatter distribution across 0..1 range.
  135.           h = 1 + h;
  136.         }
  137.         int hue = h * LED_MAX_LEVEL;                  // Expand 0..1 range to 0..MAX.
  138.    
  139.         // Conversion for brightness shifts a simplex distribution with mean about zero and distribution over +/- NOISE_LIMIT
  140.         // to a flatter distribution between 0..1 with no wrap around. This distribution is not really flat, just
  141.         // a widened simplex (roughly normal) chopped off at min and min.
  142.         // 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).
  143.         // Decreasing it results in more time spent at half brightness.
  144.         v = v * NOISE_LIMIT * BRI_WIDTH;
  145.         v = 0.5 + v;
  146.         int brightness = constrain(v * LED_MAX_LEVEL, 0, LED_MAX_LEVEL);
  147.         // Convert the HSV values to RGB and set the colours
  148.         setRGB(hue, saturation, brightness);
  149.     }
  150. }
  151.  
  152. /*****************************************************************************/
  153. // Simplex noise code:
  154. // From an original algorithm by Ken Perlin.
  155. // 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
  156. double SimplexNoise(double x, double y, double z) {
  157.   // Skew input space to relative coordinate in simplex cell
  158.   s = (x + y + z) * onethird;
  159.   i = fastfloor(x+s);
  160.   j = fastfloor(y+s);
  161.   k = fastfloor(z+s);
  162.    
  163.   // Unskew cell origin back to (x, y , z) space
  164.   s = (i + j + k) * onesixth;
  165.   u = x - i + s;
  166.   v = y - j + s;
  167.   w = z - k + s;;
  168.    
  169.   A[0] = A[1] = A[2] = 0;
  170.    
  171.   // For 3D case, the simplex shape is a slightly irregular tetrahedron.
  172.   // Determine which simplex we're in
  173.   int hi = u >= w ? u >= v ? 0 : 1 : v >= w ? 1 : 2;
  174.   int lo = u < w ? u < v ? 0 : 1 : v < w ? 1 : 2;
  175.    
  176.   return k_fn(hi) + k_fn(3 - hi - lo) + k_fn(lo) + k_fn(0);
  177. }
  178.  
  179.  
  180. int fastfloor(double n) {
  181.   return n > 0 ? (int) n : (int) n - 1;
  182. }
  183.  
  184.  
  185. double k_fn(int a) {
  186.   s = (A[0] + A[1] + A[2]) * onesixth;
  187.   double x = u - A[0] + s;
  188.   double y = v - A[1] + s;
  189.   double z = w - A[2] + s;
  190.   double t = 0.6f - x * x - y * y - z * z;
  191.   int h = shuffle(i + A[0], j + A[1], k + A[2]);
  192.   A[a]++;
  193.   if (t < 0) return 0;
  194.   int b5 = h >> 5 & 1;
  195.   int b4 = h >> 4 & 1;
  196.   int b3 = h >> 3 & 1;
  197.   int b2 = h >> 2 & 1;
  198.   int b = h & 3;
  199.   double p = b == 1 ? x : b == 2 ? y : z;
  200.   double q = b == 1 ? y : b == 2 ? z : x;
  201.   double r = b == 1 ? z : b == 2 ? x : y;
  202.   p = b5 == b3 ? -p : p;
  203.   q = b5 == b4 ? -q: q;
  204.   r = b5 != (b4^b3) ? -r : r;
  205.   t *= t;
  206.   return 8 * t * t * (p + (b == 0 ? q + r : b2 == 0 ? q : r));
  207. }
  208.  
  209.  
  210. int shuffle(int i, int j, int k) {
  211.   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);
  212. }
  213.  
  214.  
  215. int b(int i, int j, int k, int B) {
  216.   return T[b(i, B) << 2 | b(j, B) << 1 | b(k, B)];
  217. }
  218.  
  219.  
  220. int b(int N, int B) {
  221.   return N >> B & 1;
  222. }
  223.  
  224. // HSV to RGB code from Kasper Kamperman, adapted by Jez Weston.
  225. // http://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/
  226. void setRGB(int hue, int sat, int bri) {
  227.   // Convert hue, saturation and brightness ( HSB/HSV ) to RGB
  228.  
  229.   int r, g, b;
  230.   long base, divisions;
  231.  
  232.   if (sat == 0) { // Acromatic color (gray). Hue doesn't matter.
  233.     colours[0]=bri;
  234.     colours[1]=bri;
  235.     colours[2]=bri;  
  236.   }
  237.   else {
  238.     base = ( (long)(LED_MAX_LEVEL - sat) * (long)bri )>>PWM_RESOLUTION;
  239.  
  240.     // Divide the HSV space into divisions
  241.     divisions = 43;
  242.    
  243.     switch( int(hue/divisions) ) {
  244.     case 0:
  245.       r = bri;
  246.       g = (((bri-base)*hue)/divisions)+base;
  247.       b = base;
  248.     break;
  249.     case 1:
  250.       r = (((bri-base)*(divisions-(hue%divisions)))/divisions)+base;
  251.       g = bri;
  252.       b = base;
  253.     break;
  254.     case 2:
  255.       r = base;
  256.       g = bri;
  257.       b = (((bri-base)*(hue%divisions))/divisions)+base;
  258.     break;
  259.     case 3:
  260.       r = base;
  261.       g = (((bri-base)*(divisions-(hue%divisions)))/divisions)+base;
  262.       b = bri;
  263.     break;
  264.     case 4:
  265.       r = (((bri-base)*(hue%divisions))/divisions)+base;
  266.       g = base;
  267.       b = bri;
  268.     break;
  269.     case 5:
  270.       r = bri;
  271.       g = base;
  272.       b = (((bri-base)*(divisions-(hue%divisions)))/divisions)+base;
  273.     break;
  274.     }
  275.  
  276.   colours[RED]=r;
  277.   colours[GREEN]=g;
  278.   colours[BLUE]=b;
  279.   }
  280. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement