8x8x8 Led Cube driven by 4x PCF8575 for the columns and 1x PCF8574 for the layers

led_cube.cpp

Committer:
Bas
Date:
2012-11-01
Revision:
1:e08a4d27f534
Parent:
0:920c5ed65a45

File content as of revision 1:e08a4d27f534:

#include "led_cube.h"

LED_CUBE::LED_CUBE(I2C* _interface)
    : row(_interface, 0x07,PCF8574_TYPE),
      colums_0_1(_interface, 0x00),
      colums_2_3(_interface, 0x01),
      colums_4_5(_interface, 0x02),
      colums_6_7(_interface, 0x03)
{
    _interface->frequency(400000); //400KHz
    duty.attach_us(this, &LED_CUBE::UpdateRow,1250);// each row = 1/8 duty
}

LED_CUBE::~LED_CUBE() {}

void LED_CUBE::UpdateRow()
{
    static int rownr = 0;

    if (rownr < 0 || rownr > 7) {
        rownr = 0;
    }
    row.WriteByte(0xFF); // switch off all rows before updating next colums to avoid ghost data from previous colums
    colums_0_1.WriteBytes(~cubeData[8 * rownr + 0],~cubeData[8 * rownr + 2]); //update the columns
    colums_2_3.WriteBytes(~cubeData[8 * rownr + 1],~cubeData[8 * rownr + 3]);
    colums_4_5.WriteBytes(~cubeData[8 * rownr + 6],~cubeData[8 * rownr + 4]);
    colums_6_7.WriteBytes(~cubeData[8 * rownr + 7],~cubeData[8 * rownr + 5]);
    row.WriteByte(~(1<<rownr)); // now display the row
    rownr++;
}

//clear everything
void LED_CUBE::clear_cube(void)
{
    row.WriteByte(0xFF);
    colums_0_1.WriteBytes(0xFF,0xFF);
    colums_2_3.WriteBytes(0xFF,0xFF);
    colums_4_5.WriteBytes(0xFF,0xFF);
    colums_6_7.WriteBytes(0xFF,0xFF);
    clearVoxels();
}

//set a voxel in the cube array
void LED_CUBE::setVoxel(unsigned char x, unsigned char y, unsigned char z)
{
    if (inrange(x, y, z))
        cubeData[z*8 + x] |= 0x01<<y;
}

//clear a single voxel in the cube array
void LED_CUBE::clearVoxel(unsigned char x, unsigned char y, unsigned char z)
{
    if (inrange(x, y, z))
        cubeData[z*8 + x] &= ~(0x01<<y);
}

//return the value of a single voxel in the cube array
unsigned char LED_CUBE::getVoxel(unsigned char x, unsigned char y, unsigned char z)
{
    if (inrange(x, y, z)) {
        if(cubeData[z*8 + x] & 0x01<<y) {
            return 1;
        } else {
            return 0;
        }
    } else {
        return 0;
    }
}

void LED_CUBE::copyColumn(unsigned char x, unsigned char y, unsigned char col)
{
    for(unsigned char z = 0; z < 8; z++) {
        if(col&(0x01<<z)) {
            setVoxel(x, y, z);
        } else {
            clearVoxel(x, y, z);
        }
    }
}

// In some effect we want to just take bool and write it to a voxel
// this function calls the apropriate voxel manipulation function.
void LED_CUBE::alterVoxel(unsigned char x, unsigned char y, unsigned char z, unsigned char state)
{
    if (state == 1) {
        setVoxel(x,y,z);
    } else {
        clearVoxel(x,y,z);
    }
}

// Flip the state of a voxel.
// If the voxel is 1, its turned into a 0, and vice versa.
void LED_CUBE::flipVoxel(unsigned char x, unsigned char y, unsigned char z)
{
    if (inrange(x, y, z))
        cubeData[z*8 + x] ^= (1 << y);
}

void LED_CUBE::setPlane_x (int x)
{
    int z;

    if (x>=0 && x<8) {
        for (z=0; z<8; z++)
            cubeData[z*8 + x] = 0xFF;
    }
}

void LED_CUBE::clearPlane_x (int x)
{
    int z;

    if (x>=0 && x<8) {
        for (z=0; z<8; z++)
            cubeData[z*8 + x] = 0x00;
    }
}

void LED_CUBE::setPlane_y (int y)
{
    int z;
    int x;

    if (y>=0 && y<8) {
        for (z=0; z<8; z++) {
            for (x=0; x<8; x++) {
                cubeData[z*8 + x] |= (1 << y);
            }
        }
    }
}

void LED_CUBE::clearPlane_y (int y)
{
    int z;
    int x;

    if (y>=0 && y<8) {
        for (z=0; z<8; z++) {
            for (x=0; x<8; x++) {
                cubeData[z*8 + x] &= ~(1 << y);
            }
        }
    }
}
// Sets all voxels along a X/Y plane at a given point
// on axis Z
void LED_CUBE::setPlane_z (int z)
{
    int i;

    if (z>=0 && z<8) {
        for (i=0; i<8; i++)
            cubeData[z*8 + i] = 0xFF;
    }
}

// Clears voxels in the same manner as above
void LED_CUBE::clearPlane_z (int z)
{
    int i;

    if (z>=0 && z<8) {
        for (i=0; i<8; i++)
            cubeData[z*8 + i] = 0x00;
    }
}

void LED_CUBE::setPlane (char axis, unsigned char i)
{
    switch (axis) {
        case AXIS_X:
            setPlane_x(i);
            break;
        case AXIS_Y:
            setPlane_y(i);
            break;
        case AXIS_Z:
            setPlane_z(i);
            break;
    }
}

void LED_CUBE::clearPlane (char axis, unsigned char i)
{
    switch (axis) {
        case AXIS_X:
            clearPlane_x(i);
            break;
        case AXIS_Y:
            clearPlane_y(i);
            break;
        case AXIS_Z:
            clearPlane_z(i);
            break;
    }
}
void LED_CUBE::setSlope_XY(int x1, int y1, int x2, int y2)
{
    float slope, offset;
    int x,y,z;

    if (x1>x2) {
        int tmp;
        tmp = x2;
        x2 = x1;
        x1 = tmp;
        tmp = y2;
        y2 = y1;
        y1 = tmp;
    }

    slope=float(y2-y1)/float(x2-x1);
    offset=-x1*slope +y1;

    if ((x2-x1)>=7) {
        for(x=0; x<8; x++) {
            y = floor((x*slope + offset)+0.5);
            for(z=0; z<8; z++) {
                setVoxel(x,y,z);
            }
        }
    } else if(x1==x2) {
        for(y=0; y<8; y++) {
            for(z=0; z<8; z++) {
                setVoxel(x1,y,z);
            }
        }
    } else {
        for(y=0; y<8; y++) {
            x = floor(((y-offset)/slope)+0.5);
            for(z=0; z<8; z++) {
                setVoxel(x,y,z);
            }

        }
    }
}

void LED_CUBE::setSlope_XZ(int x1, int z1, int x2, int z2)
{
    float slope, offset;
    int x,z;

    if (x1>x2) {
        int tmp;
        tmp = x2;
        x2 = x1;
        x1 = tmp;
        tmp = z2;
        z2 = z1;
        z1 = tmp;
    }

    slope=float(z2-z1)/float(x2-x1);
    offset=-x1*slope +z1;

    if ((x2-x1)>=7) {
        for(x=0; x<8; x++) {
            z = floor((x*slope + offset)+0.5);
            cubeData[z*8 + x] = 0xFF;

        }
    } else if(x1==x2) {
        for(z=0; z<8; z++) {
            cubeData[z*8 + x1] = 0xFF;

        }
    } else {
        for(z=0; z<8; z++) {
            x = floor(((z-offset)/slope)+0.5);
            cubeData[z*8 + x] = 0xFF;

        }

    }
}


void LED_CUBE::setSlope_YZ(int y1, int z1, int y2, int z2)
{
    float slope, offset;
    int x,y,z;

    if (y1>y2) {
        int tmp;
        tmp = y2;
        y2 = y1;
        y1 = tmp;
        tmp = z2;
        z2 = z1;
        z1 = tmp;
    }

    slope=float(z2-z1)/float(y2-y1);
    offset=-y1*slope +z1;

    if ((y2-y1)>=7) {
        for(y=0; y<8; y++) {
            z = floor((y*slope + offset)+0.5);
            for(x=0; x<8; x++) {
                setVoxel(x,y,z);
            }
        }
    } else if(y1==y2) {
        for(z=0; z<8; z++) {
            for(x=0; x<8; x++) {
                setVoxel(x,y1,z);
            }
        }
    } else {
        for(z=0; z<8; z++) {
            y = floor(((z-offset)/slope)+0.5);
            for(x=0; x<8; x++) {
                setVoxel(x,y,z);
            }
        }

    }
}

//clear all the voxels in the array
void LED_CUBE::clearVoxels(void)
{
    memset(cubeData, 0, 64*sizeof(unsigned char));
}

// Fill a value into all 64 byts of the cube buffer
// Mostly used for clearing. fill(0xr0)
// or setting all on. fill(0xff)
void LED_CUBE::fill (unsigned char pattern)
{
    memset(cubeData, pattern, 64*sizeof(unsigned char));
}

// Draw a box with all walls drawn and all voxels inside set
void LED_CUBE::box_filled(int x1, int y1, int z1, int x2, int y2, int z2)
{
    int ix;
    int iz;

    argorder(x1, x2, &x1, &x2);
    argorder(y1, y2, &y1, &y2);
    argorder(z1, z2, &z1, &z2);
    for (iz=z1; iz<=z2; iz++) {
        for (ix=x1; ix<=x2; ix++) {
            cubeData[iz*8 + ix] |= byteline(y1,y2);
        }
    }
}

// Draw a hollow box with side walls.
void LED_CUBE::box_walls(int x1, int y1, int z1, int x2, int y2, int z2)
{
    int ix;
    int iz;

    argorder(x1, x2, &x1, &x2);
    argorder(y1, y2, &y1, &y2);
    argorder(z1, z2, &z1, &z2);
    for (iz=z1; iz<=z2; iz++) {
        for (ix=x1; ix<=x2; ix++) {
            if (ix == x1 || ix == x2 || iz == z1 || iz == z2) {
                cubeData[iz*8 + ix] = byteline(y1,y2);
            } else {
                cubeData[iz*8 + ix] |= ((0x01 << y1) | (0x01 << y2));
            }
        }
    }
}

// Draw a wireframe box. This only draws the corners and edges,
// no walls.
void LED_CUBE::box_wireframe(int x1, int y1, int z1, int x2, int y2, int z2)
{
    int ix;
    int iz;

    argorder(x1, x2, &x1, &x2);
    argorder(y1, y2, &y1, &y2);
    argorder(z1, z2, &z1, &z2);
    // Lines along Y axis
    cubeData[z1*8 + x1] |= byteline(y1,y2);
    cubeData[z1*8 + x2] |= byteline(y1,y2);
    cubeData[z2*8 + x1] |= byteline(y1,y2);
    cubeData[z2*8 + x2] |= byteline(y1,y2);
    // Lines along X axis
    for (ix=x1; ix<=x2; ix++) {
        setVoxel(ix,y1,z1);
        setVoxel(ix,y1,z2);
        setVoxel(ix,y2,z1);
        setVoxel(ix,y2,z2);
    }
    // Lines along Z axis
    for (iz=z1; iz<=z2; iz++) {
        setVoxel(x1,y1,iz);
        setVoxel(x1,y2,iz);
        setVoxel(x2,y1,iz);
        setVoxel(x2,y2,iz);
    }
}

// Draw a line between any coordinates in 3d space.
// Uses integer values for input, so dont expect smooth animations.
void LED_CUBE::line(int x1, int y1, int z1, int x2, int y2, int z2)
{
    float xy;
    // how many voxels do we move on the y axis for each step on the x axis
    float xz;
    // how many voxels do we move on the y axis for each step on the x axis
    unsigned char x,y,z;
    // We always want to draw the line from x=0 to x=7.
    // If x1 is bigget than x2, we need to flip all the values.
    if (x1>x2) {
        int tmp;
        tmp = x2;
        x2 = x1;
        x1 = tmp;
        tmp = y2;
        y2 = y1;
        y1 = tmp;
        tmp = z2;
        z2 = z1;
        z1 = tmp;
    }
    if (y1>y2) {
        xy = (float)(y1-y2)/(float)(x2-x1);
    } else {
        xy = (float)(y2-y1)/(float)(x2-x1);
    }
    if (z1>z2) {
        xz = (float)(z1-z2)/(float)(x2-x1);
    } else {
        xz = (float)(z2-z1)/(float)(x2-x1);
    }
    // For each step of x, y increments by:
    for (x = x1; x<=x2; x++) {
        y = (xy*(x-x1))+y1;
        z = (xz*(x-x1))+z1;
        setVoxel(x,y,z);
    }
}
void LED_CUBE::circle(int xr, int yr, int zr, int d ,char axis)
{
    if (d%2) { // odd diameter, place center on half pixels
// later
    } else { // use midpoint circle algorithm
        int r = d/2;
        int f = 1 - r;
        int ddF_x = 1;
        int ddF_y = -2 * r;
        int x = 0;
        int y = r;

        setVoxel(xr, yr+r, zr);
        setVoxel(xr, yr-r, zr);
        setVoxel(xr+r, yr, zr);
        setVoxel(xr-r, yr, zr);
        while (x<y) {
            if (f >= 0) {
                y--;
                ddF_y += 2;
                f += ddF_y;
            }
            x++;
            ddF_x += 2;
            f += ddF_x;

            setVoxel(xr + x, yr + y, zr);
            setVoxel(xr - x, yr + y, zr);
            setVoxel(xr + x, yr - y, zr);
            setVoxel(xr - x, yr - y, zr);
            setVoxel(xr + y, yr + x, zr);
            setVoxel(xr - y, yr + x, zr);
            setVoxel(xr + y, yr - x, zr);
            setVoxel(xr - y, yr - x, zr);
        }
    }
}

void LED_CUBE::CopyBlock(int x1, int y1, int z1, int x2, int y2, int z2, int xc, int yc, int zc)
{
    int ix;
    int iz;
    int nx;
    int nz=0;
    unsigned char tmp_data[64];

    argorder(x1, x2, &x1, &x2);
    argorder(y1, y2, &y1, &y2);
    argorder(z1, z2, &z1, &z2);

    for (iz=z1; iz<=z2; iz++) {
        nx=0;
        for (ix=x1; ix<=x2; ix++) {
            //copy block data into tmp array and mask.
            tmp_data[iz*8 + ix] = cubeData[iz*8 + ix] & byteline(y1,y2);
            //shift the tmp data to the new y location
            if (yc >= y1) {
                tmp_data[iz*8 + ix] = tmp_data[iz*8 + ix] << (yc-y1);
            } else {
                tmp_data[iz*8 + ix] = tmp_data[iz*8 + ix] >> (y1-yc);
            }
            //alter the cubedata on the new x and z location with the tmp data. use old y location and copy to the new location
            cubeData[(zc+nz)*8 + xc+nx] = (cubeData[(zc+nz)*8 + xc+nx] & (~byteline(yc,yc+y2-y1))) | tmp_data[iz*8 + ix];
            nx++;
        }
        nz++;
    }
}

void LED_CUBE::MoveBlock(int x1, int y1, int z1, int x2, int y2, int z2, int xm, int ym, int zm)
{
    int ix;
    int iz;
    int nx;
    int nz=0;
    unsigned char tmp_data[64];

    argorder(x1, x2, &x1, &x2);
    argorder(y1, y2, &y1, &y2);
    argorder(z1, z2, &z1, &z2);

    for (iz=z1; iz<=z2; iz++) {
        for (ix=x1; ix<=x2; ix++) {
            //copy block data into tmp array and mask.
            tmp_data[iz*8 + ix] = cubeData[iz*8 + ix] & byteline(y1,y2);
            //clear the original voxels
            cubeData[iz*8 + ix] &= ~byteline(y1,y2);
            //shift the tmp data to the new y location
            if (ym >= y1) {
                tmp_data[iz*8 + ix] = tmp_data[iz*8 + ix] << (ym-y1);
            } else {
                tmp_data[iz*8 + ix] = tmp_data[iz*8 + ix] >> (y1-ym);
            }
        }
    }

    for (iz=z1; iz<=z2; iz++) {
        nx=0;
        for (ix=x1; ix<=x2; ix++) {

            //alter the cubedata on the new x and z location with the tmp data. use old y location and copy to the new location
            cubeData[(zm+nz)*8 + xm+nx] = (cubeData[(zm+nz)*8 + xm+nx] & (~byteline(ym,ym+y2-y1))) | tmp_data[iz*8 + ix];
            nx++;
        }
        nz++;
    }
}

char LED_CUBE::byteline (int start, int end)
{
    char line=0;
    int bitnr;

    for (bitnr=0; bitnr<8; bitnr++) {
        if (bitnr>=start && bitnr<=end) {
            line |= (1<<bitnr);
        }
    }
    return line;
}

// This function validates that we are drawing inside the cube.
unsigned char LED_CUBE::inrange(int x, int y, int z)
{
    if (x >= 0 && x < 8 && y >= 0 && y < 8 && z >= 0 && z < 8) {
        return 0x01;
    } else {
        // One of the coordinates was outside the cube.
        return 0x00;
    }
}

void LED_CUBE::argorder(int ix1, int ix2, int *ox1, int *ox2)
{
    if (ix1>ix2) {
        int tmp;
        tmp = ix1;
        ix1= ix2;
        ix2 = tmp;
    }
    *ox1 = ix1;
    *ox2 = ix2;
}

double LED_CUBE::map(double in, double inMin, double inMax, double outMin, double outMax)
{
    double out;
    out = (in-inMin)/(inMax-inMin)*(outMax-outMin) + outMin;
    return out;
}