/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVJitter.cpp
 * Description:
 *     Movie -- mv_Jitter
 *         A single LED moves quickly and randomly around the
 *         cube.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Jitter()
{
  unsigned long ts, t;
  byte x, y, z, nx, ny, nz;
  
  ts = millis() + DEF_MOVIE_TIME;

  x = random(0, 3);
  y = random(0, 3);
  z = random(0, 3);
  nx = x;
  ny = y;
  nz = z;

  displayFrame[POS(x, y, z)] = MAXBRIGHTNESSLEVEL;
  
  while (millis() < ts)
  {
    t = millis() + 80;
        
    do
    {
      // Work out the next LED to light, adjacent to the current LED.
      
      switch(random(0, 3))
      {
        case 0:
          switch(random(0, 3))
          {
            case 0:
              if (nx > 0)
                --nx;
               break;
            case 2:
              if (nx < 2)
                ++nx;
              break;
          }
          break;
    
        case 1:
          switch(random(0, 3))
          {
            case 0:
              if (ny > 0)
                --ny;
              break;
            case 2:
              if (ny < 2)
                ++ny;
              break;
          }
          break;
          
        default:
          switch(random(0, 3))
          {
            case 0:
              if (nz > 0)
                --nz;
              break;
            case 2:
              if (nz < 2)
                ++nz;
              break;
          }
          break;
      }
    } while ( x == nx && y == ny && z == nz);
    
    while (millis() < t)
      ;
      
    displayFrame[POS(x,y,z)] = 0;
    displayFrame[POS(nx,ny,nz)] = MAXBRIGHTNESSLEVEL;
    
    x = nx;
    y = ny;
    z = nz;
  }

  displayFrame[POS(nx,ny,nz)] = 0;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVGlitter.cpp
 * Description:
 *     Movie -- mv_Glitter
 *         Lights random LEDs for short periods of time creating
 *         a "glitter" effect.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistribtued, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Glitter()
{
  unsigned long ts;
  byte x;
  byte m[3];
  byte n[3] = {0, 0, 0};
  
  ts = millis() + DEF_MOVIE_TIME;

  while (millis() < ts)
  {
    for (x = 0 ; x < 3 ; x++)
    {
      if (n[x] == 0)
      {
        displayFrame[m[x]] = 0;
        m[x] = random(0, 27);
        n[x] = random(30, 50);
        displayFrame[m[x]] = MAXBRIGHTNESSLEVEL;
      }
      else
        --n[x];
    }
    delay(1);
  }
  
  for (x = 0 ; x < 3 ; x++)
    displayFrame[m[x]] = 0;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVFlashy.cpp
 * Description:
 *     Movie -- mv_Flashy
 *         Fades all LEDs in and out at random speeds.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistribtued, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Flashy()
{
  unsigned long ts;
  byte y;
  Light lights[27];
  
  for (y = 0 ; y < 27 ; y++)
  {
    lights[y].speed = random(50, 200);
    lights[y].up = true;
    lights[y].t = millis() + lights[y].speed;
  }
  
  ts = millis() + DEF_MOVIE_TIME;

  y = 0;
  while (millis() < ts)
  {
    if (millis() > lights[y].t)
    {
      displayFrame[y] += (lights[y].up ? 1 : -1);
      if (displayFrame[y] == 0 || displayFrame[y] == 7)
        lights[y].up = !lights[y].up;
        
      lights[y].t = millis() + lights[y].speed;
    }
    
    if (++y > 26)
      y = 0;
  }
  
  df_fadeOut();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVEdgeTumble.cpp
 * Description:
 *     Movie -- mv_EdgeTumble
 *         LEDs are lit along one edge, and then it "tumbles" from
 *         edge to edge randomly. The LED in the center is faded
 *         in and out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

const byte TUMBLESPEED = 25;
const byte TUMBLEDELAY = 150;
const byte FLASHERSPEED = 50;

int getN(byte, byte);

int m[7][2][2] = { 
    {{1, 0}, {2, 0}},
    {{1, 0}, {2, 1}},
    {{1, 1}, {2, 1}},
    {{1, 1}, {2, 2}},
    {{1, 1}, {1, 2}},
    {{0, 1}, {1, 2}},
    {{0, 1}, {0, 2}}
};

unsigned long ft = 0;
boolean flashUp = true;
boolean flashOn = true;

void delayAndFlash(int d)
{
  unsigned long t, ct;
  byte a;
  
  t = millis() + d;
  
  do
  {
    ct = millis();
    
    if (ct > ft)
    {
      a = displayFrame[13];
      if (flashUp)
      {
        if (flashOn)
          if (++a >= MAXBRIGHTNESSLEVEL)
            flashUp = false;
      }
      else
      {
        if (--a < 1)
          flashUp = true;
      }
      displayFrame[13] = a;
      
      ft = ct + FLASHERSPEED;
    }
  } while (ct < t);
}

void mv_EdgeTumble()
{
  unsigned long ts;
  byte a, b, f, l;
  int nx, ny, nz;
  int px, py, pz;
  byte fn, fd;
  Point p[3], v;
  
  ft = millis() + 1000;
  flashUp = (displayFrame[13] < MAXBRIGHTNESSLEVEL);
  flashOn = true;
    
  ts = millis() + (DEF_MOVIE_TIME * 2);
  l = 7;
  
  // Random start point.
  
  a = random(0, 8);
  p[0].Set((a & 1) * 2, (a & 2), (a & 4) / 2);
  
  // Random starting direction.
  
  nx = 0;
  ny = 0;
  nz = 0;
  switch(random(0, 3))
  {
    case 0:
      nx = (p[0].x == 0) ? 1 : -1;
      break;
      
    case 1:
      ny = (p[0].y == 0) ? 1 : -1;
      break;
      
    case 2:
      nz = (p[0].z == 0) ? 1 : -1;
      break;
  }
  
  // Draw initial line.
  
  for (a = 1 ; a < 3 ; a++)
    p[a].Set(p[0].x+(nx*a), p[0].y+(ny*a), p[0].z+(nz*a));
    
  displayFrame[p[0].Pos()] = 7;
  displayFrame[p[1].Pos()] = 7;
  displayFrame[p[2].Pos()] = 7;
  
  while (l > 0)
  {
    if (millis() > ts)
    {
      --l;
      flashOn = false;
    }

    px = 0;
    py = 0;
    pz = 0;

    fn = 1;
    fd = 1;
        
    switch(random(0, 2))
    {
      case 0:
        px = (p[0].x == 0) ? 1 : -1;

        if (nx != 0 || nz != 0)
          pz = (p[0].z == 0) ? 1 : -1;

        if (ny != 0)
          py = (p[0].y == 0) ? 1 : -1;
          
        if (ny != 0 || nz != 0)
        {
          fn = 5;
          fd=255;
        }
        break;
        
      case 1:
        py = (p[0].y == 0) ? 1 : -1;

        if (nx != 0)
          px = (p[0].x == 0) ? 1 : -1;
        
        if (ny != 0 || nz != 0)
          pz = (p[0].z == 0) ? 1 : -1;
          
        if (nz != 0)
        {
          fn = 5;
          fd=255;
        }
        break;
    }
       
    displayFrame[p[0].Pos()] = l;
    
    for (f = 0 ; f < 6 ; f++, fn += fd)
    {
      for (a = 1, b = 0 ; a < 3 ; a++, b++)
      {
        displayFrame[p[a].Pos()] = 0;
        
        if (px == 0)
        {
          p[a].y = p[0].y + (m[fn][b][0] * py);
          p[a].z = p[0].z + (m[fn][b][1] * pz);
        }
        else if (py == 0)
        {
          p[a].x = p[0].x + (m[fn][b][0] * px);
          p[a].z = p[0].z + (m[fn][b][1] * pz);
        }
        else
        {
          p[a].x = p[0].x + (m[fn][b][0] * px);
          p[a].y = p[0].y + (m[fn][b][1] * py);
        }
        
        displayFrame[p[a].Pos()] = l;
      }      
            
      delayAndFlash(TUMBLESPEED);
    }
   
    v = p[0];
    p[0] = p[2];
    p[2] = v; 

    nx = getN(p[0].x, p[2].x);
    ny = getN(p[0].y, p[2].y);
    nz = getN(p[0].z, p[2].z);
    
    delayAndFlash(TUMBLEDELAY);
  }
  
  df_fadeOut();
}

int getN(byte n1, byte n2)
{
  if (n1 == n2)
    return 0;
  else if (n1 < n2)
    return 1;
  else
    return -1;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVEdgeChase.cpp
 * Description:
 *     Movie -- mv_EdgeChase
 *         LEDs are lit along an edge from one corner to another
 *         at a random speed. After a random delay another edge
 *         is lit. If the lighting of an edge runs in to an already
 *         lit LED, then the display is cleared and it starts again.
 *
 *     Movie -- mv_EdgeChase2
 *         LEDs are lit along an edge from one corner to another,
 *         but as it moves along, each previous LED is dimmed
 *         until it goes out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_EdgeChase()
{
  unsigned long ts, t;
  byte a, b, x, y, z;
  int nx, ny, nz;
  boolean r = false;
  int d1, d2;
  
  ts = millis() + DEF_MOVIE_TIME;

  d1 = random(40, 120);
  d2 = random(20, 60);
  
  a = random(0, 8);
  
  x = (a & 1) * 2;
  y = (a & 2);
  z = (a & 4) / 2;
  
  a = 3;
  
  displayFrame[POS(x, y, z)] = MAXBRIGHTNESSLEVEL;
  
  while (millis() < ts)
  {
    t = millis() + d1;
        
    nx = 0;
    ny = 0;
    nz = 0;
    
    do
    {
      b = random(0, 3);
    } while (a == b);

    a = b;
    
    switch(a)
    {
      case 0:
        nx = (x == 0) ? 1 : -1;
        break;
        
      case 1:
        ny = (y == 0) ? 1 : -1;
        break;
        
      case 2:
        nz = (z == 0) ? 1 : -1;
        break;
    }
    
    while (millis() < t)
      ;
    
    if (r)
      df_clear();
    
    displayFrame[POS(x, y, z)] = 4;
    displayFrame[POS(x+nx,y+ny,z+nz)] = MAXBRIGHTNESSLEVEL;
    
    delay(d2);
    
    displayFrame[POS(x+nx,y+ny,z+nz)] = 4;
    r = (displayFrame[POS(x+nx+nx,y+ny+ny,z+nz+nz)] != 0);
    displayFrame[POS(x+nx+nx,y+ny+ny,z+nz+nz)] = MAXBRIGHTNESSLEVEL;
    
    delay(d2);
    
    displayFrame[POS(x+nx,y+ny,z+nz)] = 1;
    
    x = x + nx + nx;
    y = y + ny + ny;
    z = z + nz + nz;
  }

  df_clear();
}

void mv_EdgeChase2()
{
  unsigned long ts, t;
  byte a, b, d, i, j, k, x, y, z;
  int nx, ny, nz;
  byte ss, sl;
  
  ts = millis() + DEF_MOVIE_TIME;

  d = random(50, 200);
  
  a = random(0, 8);
  
  x = (a & 1) * 2;
  y = (a & 2);
  z = (a & 4) / 2;
  points[0].Set(x, y, z);
  ss = 0;
  sl = 1;
  
  a = 3;
  
  displayFrame[points[0].Pos()] = MAXBRIGHTNESSLEVEL;

  t = millis() + d;
          
  while (millis() < ts)
  {
    nx = 0;
    ny = 0;
    nz = 0;
    
    do
    {
      b = random(0, 3);
    } while (a == b);

    a = b;
    
    switch(a)
    {
      case 0:
        nx = (x == 0) ? 1 : -1;
        break;
        
      case 1:
        ny = (y == 0) ? 1 : -1;
        break;
        
      case 2:
        nz = (z == 0) ? 1 : -1;
        break;
    }
    
    for (i = 0 ; i < 2 ; i++)
    {
      while (millis() < t)
        ;
        
      j = ss;
      for (k = 0 ; k < sl ; k++)
      {
        if (displayFrame[points[j].Pos()] > 0)
          --displayFrame[points[j].Pos()];
          
        if (j == 0)
          j = 7;
        else
          --j;
      }
      
      if (++ss > 7)
        ss = 0;
      
      if (sl < 8)
        ++sl;
  
      x = x + nx;
      y = y + ny;
      z = z + nz;
  
      points[ss].Set(x, y, z);
      displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
      
      t = millis() + d;
    }
  }

  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVCounter.cpp
 * Description: 
 *     Movie -- mv_Counter
 *         LED on bottom plane moves around the edge of the
 *         plane. On each complete circuit, the LED on the
 *         plane above moves along one, and when that has done
 *         a complete circuit, the led on the top plane moves
 *         along one.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

struct Item
{
  public:
    byte x;
    byte d;
    
    Item() : x(0), d(0) {};
    
    boolean inc()
    {
      boolean o = false;
      
      switch(d)
      {
        case 0:
          if (++x == 2)
            d = 1;
          break;
          
        case 1:
          x += 9;
          if (x > 18)
            d = 2;
          break;
          
        case 2:
          if (--x == 18)
            d = 3;
          break;
          
        case 3:
          x -= 9;
          if (x < 9)
          {
            d = 0;
            o = true;
          }
          break;
      }
      
      return o;
    }
};

void mv_Counter()
{
  Item i[3];
  unsigned long ts, t;
  boolean o;
  int del;
  
  i[0].x = 0;
  i[1].x = 0;
  i[2].x = 0;
  
  displayFrame[i[0].x] = 7;
  displayFrame[i[1].x + 3] = 7;
  displayFrame[i[2].x + 6] = 7;
  
  delay(1000);
  
  del = random(20, 70);
  
  ts = millis() + (DEF_MOVIE_TIME * 2);

  while (millis() < ts)
  {
    t = millis() + del;
    
    displayFrame[i[0].x] = 0;
    o = i[0].inc();
    displayFrame[i[0].x] = 7;
    if (o)
    {
      displayFrame[i[1].x + 3] = 0;
      o = i[1].inc();
      displayFrame[i[1].x + 3] = 7;
      if (o)
      {
        displayFrame[i[2].x + 6] = 0;
        i[2].inc();
        displayFrame[i[2].x + 6] = 7;
      }
    }
    
    while (millis() < t)
      ;
  }  
    
  df_fadeOut();
    
  delay(500);
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVAllOn.cpp
 * Description:
 *     Movie -- mv_AllOn
 *         Turn all LEDs on for a period, then fade out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_AllOn()
{
  unsigned long ts;
  
  ts = millis() + (DEF_MOVIE_TIME / 10);

  df_allOn();
  
  while (millis() < ts)
    ;
    
  df_fadeOut();
    
  delay(500);
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: DisplayFrame.h
 * Description: Header file for DisplayFrame.cpp.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#ifndef DisplayFrame_h
#define DisplayFrame_h

#include "Arduino.h"

typedef byte (*POSTransFPtr)(byte, byte, byte);

byte POSTrans0(byte, byte, byte);
byte POSTrans1(byte, byte, byte);
byte POSTrans2(byte, byte, byte);
byte POSTrans3(byte, byte, byte);
byte POSTrans4(byte, byte, byte);
byte POSTrans5(byte, byte, byte);
byte POSTrans6(byte, byte, byte);
byte POSTrans7(byte, byte, byte);

POSTransFPtr GetPOSTrans(byte);

#define POS(x, y , z)  (((y)*9)+((x)*3)+(z))

struct DisplayParameters
{
  public:
    byte subFrame;
    byte subFrameBit;
    byte x;

    DisplayParameters() : subFrame(0), subFrameBit(1), x(0) {};
};

struct Point
{
  public:
    byte x;
    byte y;
    byte z;

    Point() : x(0), y(0), z(0) {};
    Point(byte a, byte b, byte c) : x(a), y(b), z(c) {};

    void Set(byte a, byte b, byte c)
    {
      x = a;
      y = b;
      z = c;
    }

    byte Pos()
    {
      return POS(x, y, z);
    }
};

class Light
{
  public:
    byte speed;
    boolean up;
    unsigned long t;
};

const byte MAXBRIGHTNESSLEVEL = 7;

extern byte displayFrame[27];
extern Point points[8];
//extern const byte brightnessSubFrames[8];

typedef boolean (*df_processAllFunc)(int *, byte);

void df_init();
void updateDisplay();
void df_allOn();
void df_clear();
boolean df_fadeOut(int = 50);
boolean df_processAll(df_processAllFunc, int *);

void df_DisplayMonoFrame(POSTransFPtr, unsigned long);

#endif
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: DisplayFrame.cpp
 * Description: Functions for updating the LED cube.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "DisplayFrame.h"
#include "DirectIO.h"
#include "ShiftReg_7495.h"
#include "Counter_4017.h"

const byte brightnessSubFrames[] = {B00000000, B00000001, B01001001, B01010101, B01011101, B01110111, B11110111, B11111111};

boolean dfpa_fadeOut(int *, byte);

byte displayFrame[27];

Point points[8]; // An array of points that animations can use.

volatile DisplayParameters dispParams;

ShiftReg_7495 DisplayZ(4, 10, 11, 12); // Parallel mode: Clock on pin 4, A on pin 10, B on pin 11 and C on pin 12. NOT ACTUALLY USING PIN 4 - PINS 10, 11 and 12 WILL DRIVE THE DISPLAY DIECTLY.
Counter_4017 DisplayXY(7, 6); // Clock pin 7, last element detect pin 6.

// Used for timing metrics in the updateDisplay function.

volatile boolean gotupt = false;
volatile unsigned int upt = 0;
volatile unsigned int upc = 0;

void df_init()
{
  df_clear();
  attachInterrupt(0, updateDisplay, RISING); // Interrupt 0 is on pin 2 (DISPLAYCLOCKPIN).
}

// Called by interrupt 0.
// Displays a 3 LED segment on each interrupt.

void updateDisplay()
{
  byte b, c, d;
  unsigned long st = 0, et = 0;

  // Timing metrics.
  
  if (gotupt == false)
  {
    st = micros(); // micros() still works in an interrupt - apparently.
  }
  
  DisplayZ.clear();    // Clear data lines.
  DisplayXY.clock();   // Trigger 4017 - move to the next row to display on.

  // Move to next row to display.
  
  dispParams.x += 3;
  if (dispParams.x > 26)
  {
    dispParams.x = 0;
  
    // Subframe for dimming LED.
    
    if (++dispParams.subFrame > MAXBRIGHTNESSLEVEL)
    {
      dispParams.subFrame = 0;
      dispParams.subFrameBit = 1;
    }
    else
      dispParams.subFrameBit <<= 2;
  }
  
  // Set bits to be displayed.
  
  c = dispParams.x;
  for (b = 0 ; b < 3 ; b++)
  {
    DisplayZ.setBit(b, (brightnessSubFrames[displayFrame[c]] & dispParams.subFrameBit) != 0);
    ++c;
  }
  
  /*
    This loop is a delay to allow the triggering of the 4017 (DisplayXY) to propagate.
    The 4017 can take up to 530ns from tigger to output at 5V, according to data sheet.

    Without this delay, we will see the new data being applied to the current data lines
    before the 4017 has acted. So you get a ghost of the next set of data on the current
    leds.
  */
  
  for (d = 0 ; d < 9 ; d++)
    digitalRead(0);

  // Display the bits (LEDs).
  
  DisplayZ.send();

  // Timing metrics.
  
  if (gotupt == false && st > 0)
  {
    et = micros();
    upt = et - st;
    st = 0;
    gotupt = true;
  }
  
  ++upc;
}

void df_allOn()
{
  for (byte i = 0 ; i < 27 ; i++)
    displayFrame[i] = MAXBRIGHTNESSLEVEL;
}

void df_clear()
{
  for (byte i = 0 ; i < 27 ; i++)
    displayFrame[i] = 0;
}

boolean df_fadeOut(int delayTime)
{
  boolean faded = false;
  int changed = 0;

  while (!faded)
  {  
    changed = 0;
    
    df_processAll(dfpa_fadeOut, &changed);
    faded = (changed == 0);
    delay(delayTime);    
  }
  
  return faded;
}

boolean dfpa_fadeOut(int *data, byte cell)
{
  if (displayFrame[cell] > 0)
  {
    displayFrame[cell] -= 1;
    *data += 1;
  }
  
  return true;
}

boolean df_processAll(df_processAllFunc func, int *data)
{
  byte x;
  boolean success = true;
  
  for (x = 0 ; x < 27 ; x++)
  {
    if (!func(data, x))
    {
      success = false;
      continue;
    }
  }
  
  return success;
}

POSTransFPtr GetPOSTrans(byte t)
{
  switch(t)
  {
    case 1:
      return POSTrans1;
      break;
    
    case 2:
      return POSTrans2;
      break;
      
    case 3:
      return POSTrans3;
      break;
    
    case 4:
      return POSTrans4;
      break;
      
    case 5:
      return POSTrans5;
      break;
      
    case 6:
      return POSTrans6;
      break;
    
    case 7:
      return POSTrans7;
      break;
      
    default:
      return POSTrans0;
      break;
  }
}

byte POSTrans0(byte x, byte y, byte z)
{
  return(POS(x, y, z));
}

byte POSTrans1(byte x, byte y, byte z)
{
  return(POS(2-x, y, z));
}

byte POSTrans2(byte x, byte y, byte z)
{
  return(POS(x, 2-y, z));
}

byte POSTrans3(byte x, byte y, byte z)
{
  return(POS(2-x, 2-y, z));
}

byte POSTrans4(byte x, byte y, byte z)
{
  return(POS(x, y, 2-z));
}

byte POSTrans5(byte x, byte y, byte z)
{
  return(POS(2-x, y, 2-z));
}

byte POSTrans6(byte x, byte y, byte z)
{
  return(POS(x, 2-y, 2-z));
}

byte POSTrans7(byte x, byte y, byte z)
{
  return(POS(2-x, 2-y, 2-z));
}

// Each bit in "box" represents an LED in the display; 0=Off, 1 = On.

void df_DisplayMonoFrame(POSTransFPtr trans, unsigned long box)
{
  register byte x = 0, y = 0, z = 0;
  
  for (int i = 0 ; i < 27 ; i++)
  {
    displayFrame[trans(x, y, z)] = (box & 1) == 1 ? MAXBRIGHTNESSLEVEL : 0;
    box >>= 1;
    if (++x > 2)
    {
      x = 0;
      if (++y > 2)
      {
        y = 0;
        ++z;
      }
    }
  }
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: _3x3x3_Mono.ino
 * Description: 3x3x3 mono LED cube using Arduino UNO.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "DirectIO.h"
#include "Counter_4017.h"
#include "ShiftReg_7495.h"

#include "DisplayFrame.h"
#include "Movies.h"

const int DISPLAYCLOCKPIN = 2; // Must be pin 2, for interrupt 0.
const int CLOCKGENPIN = 3; // Pin that will generate the interrupt pulse. Could use external pulse generator instead.

void setup()
{  
  pinMode(DISPLAYCLOCKPIN, INPUT);
  pinMode(CLOCKGENPIN, OUTPUT);
  
  // Set PWM on pin 3 to about 3.9KHz and start it.
  TCCR2B = (TCCR2B & B11111000) | B010; 
  analogWrite(CLOCKGENPIN, 127);

  // Seed the random number generator.  
  randomSeed(analogRead(0));

  // Initialise the displayFrame functions.
  df_init();
}

void loop() 
{
  static byte a = 255;
  byte b;
  
  do
  {
    b = random(0, 14);
  } while (b == a);
  a = b;

  switch(b)
  {
    case 0:
      mv_Boxy();
      break;
      
    case 1:    
      mv_EdgeChase2();
      break;
      
    case 2:    
      mv_Snake();
      break;
      
    case 3:    
      mv_Counter();
      break;
      
    case 4:    
      mv_EdgeTumble();
      break;
      
    case 5:    
      mv_Flashy();
      break;
      
    case 6:    
      mv_AllOn();
      break;
      
    case 7:    
      mv_Rain();
      break;
      
    case 8:    
      mv_EdgeChase();
      break;
      
    case 9:    
      mv_Jitter();
      break;
      
    case 10:    
      mv_Pulse();
      break;
      
    case 11:    
      mv_TwinkleAll();
      break;
      
    case 12:
      mv_Lightning();
      break;
      
    default:    
      mv_Glitter();
      break;
  }
}