view grid.cpp @ 49:178642d134a7 tip

xtra files
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Wed, 01 May 2013 17:34:33 +0100
parents 23efe1f0cd8a
children
line wrap: on
line source
//
//  grid.cpp
//  oscSenderExample
//
//  Created by Robert Tubb on 03/10/2012.
//
//
#include "ofMain.h"
#include "grid.h"
#include "eventLogger.h"

#include <sstream>

//extern PresetManager presetManager;
extern EventLogger eventLogger;
//--------------------------------------------------------------
Grid::Grid(){

}
//--------------------------------------------------------------
//--------------------------------------------------------------
Grid::~Grid(){

}
void Grid::init(){
    maxValue = pow(32.0,7.0)-1;
    minValue = 60; // number of 1-size divisions at smallest scale
    maxZoom = false;
    minZoom = false;
    paramsPerDim = 5;
    paramBitDepth = 7;
    codeLength = pow(2.0,paramsPerDim);
    
    pixSize.setCoord(ofGetWidth(), ofGetHeight());
 
    //set scale and position to mid way
    scale = pow(32.0,6.0);
    

    size.setCoord(pixSize.x*scale, pixSize.y*scale);
    centre.setCoord(maxValue/2 , maxValue/2);
    topLeft.setCoord(centre.x - size.x/2, centre.y - size.y/2);
    
    
    makeCode();

    // set a starting param value, find coord that does it.
    vector<int> params(paramsPerDim*2);
    for(int i = 0;i<2*paramsPerDim;i++){
        midiCC[i] = 64;
        params[i] = 64;
    }
    TwoVector coord = calculateCoordFromParams(params);
    setCoord(coord);
    
    viewWasChanged();
    
    
    
}

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

void Grid::makeCode(){
 
    ////////////////////////////////~~~,,,,,,,,.........
    ////////// gray code generation
    //////
    ///
    // TODO 5bit specific
    // transition sequence.... what a palaver! only need to do once though.
    int trans[] = {0,1,2,3,0,4,2,1,0,3,2,1,0,4,2,3,0,1,2,3,0,4,2,1,0,3,2,1,0,4,2,3};

    
    int code[codeLength][paramsPerDim]; // start with normal array
    
   
    
    for(int j=0; j<paramsPerDim; j++){
        code[0][j] = 0;
    }
    
    for(int i=0; i < codeLength-1; i++){  // TODO went off end??
        transSeq.push_back(trans[i]); // member vector
        for(int j=0; j<paramsPerDim; j++){
            
            if (j == abs(trans[i])){
                code[i+1][j] = !code[i][j];
            }else{
                code[i+1][j] = code[i][j]; 
            }
        }

    }
    
    for(int i=0; i < codeLength; i++){ // fill vector
        
        vcode.push_back(vector<bool>());
        
        for(int j=0; j<paramsPerDim; j++){
            vcode[i].push_back(code[i][j]);
        }
    }
    
    // now try with iterators to print...
    vector< vector<bool> >::iterator cit;
    //vector<bool>::iterator bit;
    vector<bool>::iterator bit;
    int i = 0;
    for(cit=vcode.begin(); cit!=vcode.end() ; cit++){ // fill vector
        i++;
        cout << i << " = ";
        bit = (*cit).begin();
        for(bit=(*cit).begin(); bit!=(*cit).end() ; bit++){
            
            cout  << *bit;
        }
        cout << "\n";

    }
    /*
    // codeToInt unit test
    vector<bool> aCode = vcode[12];
    int result = grayToInt(aCode);
    cout << "GRAY TO INT : " << result << "\n";

    cout << "-------------------------------\n";
    
    // base32toFloat unit test

    vector<int> test32 = coordTobase32(869437.0);
    double cresult = base32toCoord(test32);
    cout << "base32toCoord : " << cresult << "\n";
     
     cout << "-------------------------------\n";
    
    
    
    // midiToGray unit test

    vector<vector<bool> > incodes(paramBitDepth, vector<bool>(paramsPerDim));
    for(int i=0;i<7;i++){
        incodes[i] = vcode[i+5];
    }

    vector<int> midiTest;
    midiTest = grayToMidi(incodes);
    vector<vector<bool> > outcodes = midiToGray(midiTest);
    
    vector< vector<bool> >::iterator cit1;
    //vector<bool>::iterator bit;
    vector<bool>::iterator bit1;
    cout << "midiToGray Unit test\n";
    for(int i=0;i<paramBitDepth;i++){ // fill vector

        for(int j=0;j<paramsPerDim;j++){
            
            cout  << (incodes[i][j] == outcodes[i][j]) << ",";
        }
        cout << "\n";
        
    }
     cout << "-------------------------------\n";

    
    // whole process unit test(?)
    
    TwoVector inCoord(888999777,777666555);
    vector<int> checkParam;
    checkParam = calculateParamsFromCoord(inCoord);
    for(int i=0; i<2*paramsPerDim;i++){
        cout << checkParam[i] << "_";
    }
    TwoVector outCoord = calculateCoordFromParams(checkParam);
    cout << "Unit TEst coord = " << outCoord.x << "," << outCoord.y << "\n";
*/
    
}
//--------------------------------------------------------------
void Grid::draw(){  // draw lines
    // TODO too friggin long
    
    //scale++;
    // nice loopy thing
    int power = 0;
   
    // these get big!
    double  divsr;
    double yl,xl;
    double gridSize = 0;

    double firstLineXPos, lastLineXPos,firstLineYPos, lastLineYPos, remleft, xoffset, yoffset = 0.0;
    double xstart, xfinish, ystart,yfinish;
    // these don't
    int xpos, ypos = 0;
    
    float alpha;
    bool done = false;
    float markpow = 32.0;  // power that denotes next grid markings
    int lineWidth = 0;
    int colCycle = 0;
    
    // get all the preset points within this view
    //presetManager.getPresetsInRange(topLeft, topLeft+size);
 
    // loop thru powers of (base?) to determine which should be shown as grids
    while (!done){
        gridSize = pow(markpow,power);
 
        colCycle = power % 7;

        divsr = size.x / gridSize;

        // if (divisor i.e. number of lines is less than 1000
        if( divsr >= 1 && divsr < 1000){

            // calculate transparency
            float visCont = log10(divsr); // 0 if only 1 line, 3 if 1000 lines
            alpha = 90*(3 - visCont);
            ofSetLineWidth(lineWidth);
            
            // cycle colors for different scales
            if(colCycle == 0){
                ofSetColor(255,255,255,alpha);
            }else if(colCycle == 1){
                ofSetColor(255,0,0,alpha);
            }else if(colCycle == 2){
                ofSetColor(0,0,255,alpha);
            }else if(colCycle == 3){
                ofSetColor(0,255,0,alpha);
            }else if(colCycle == 4){
                ofSetColor(255,0,255,alpha);
            }else if(colCycle == 5){
                ofSetColor(0,255,255,alpha);
            }else if(colCycle == 6){
                ofSetColor(255,255,0,alpha);
            }else if(colCycle == 7){
                ofSetColor(255,255,255,alpha);
            }else{
                cout << "err colour messed up\n";
            }
            
            
            // draw level numbers associated with each color. This would not be true if markpow wasnt 2^d
            ////////-------/////////
            /*
            temp << "THIS LEVEL = " << (int)pow(2.0,power) << "   ";
            string s = temp.str();
            ofDrawBitmapString( s, 10, line );
            line+=30;
            temp.str("");
            *////////-------/////////
            
            // draw presets at this level
            // fill in smallest squares if they contain a preset
            // how to do this efficiently?
            
            
            if(topLeft.x < 0.0){
                firstLineXPos = 0.0;
                xoffset = firstLineXPos - topLeft.x;
                xstart = xoffset/scale;
            }else{
                firstLineXPos = topLeft.x;
                // kinda float version of % operator
                remleft = ceil(firstLineXPos/gridSize);
                xoffset = (remleft * gridSize) - topLeft.x;
                xstart = 0;
            }
            if(topLeft.x + size.x > maxValue){
                lastLineXPos = maxValue+1 - topLeft.x;
                
                
            }else{
                lastLineXPos = size.x;
                
            }
            xfinish = lastLineXPos/scale;
            
            //////////------------------------
            // same for y
            
            if(topLeft.y < 0.0){
                firstLineYPos = 0.0;
                yoffset = firstLineYPos - topLeft.y;
                ystart = yoffset/scale;
            }else{
                firstLineYPos = topLeft.y;
                // kinda float version of % operator
                remleft = ceil(firstLineYPos/gridSize);
                yoffset = (remleft * gridSize) - topLeft.y;
                ystart = 0;
            }
            if(topLeft.y + size.y > maxValue){
                lastLineYPos = maxValue+1 - topLeft.y;
               
            }else{
                lastLineYPos = size.y;
                
            }
            yfinish = lastLineYPos/scale;
            // -------------------------------------------
                // now draw
            for(xl = xoffset; xl <= (lastLineXPos); xl+= gridSize){

                xpos = xl/scale;
                ofLine(xpos, ystart, xpos, yfinish);
            
            }

            for(yl = yoffset; yl <= (lastLineYPos); yl+= gridSize){
                
                ypos = yl/scale;
                ofLine(xstart, ypos, xfinish, ypos);
            }
            
            
        }else if (divsr < 1){
            // ignore...
            done = true;
        }
        
        power++;
    }
    //cout << "draw done" << "\n";
    //displayInfo();

    ////////-------/////////
    
    ////////-------/////////
    
    // draw centre cross hairs
    ofSetColor(255, 0, 0);
    ofNoFill();
    ofLine(pixSize.x/2-20, pixSize.y/2, pixSize.x/2+20, pixSize.y/2);
	ofLine(pixSize.x/2, pixSize.y/2-20, pixSize.x/2, pixSize.y/2+20);
    ofEllipse(pixSize.x/2, pixSize.y/2, 20, 20);
    ofFill();
    ////////-------////////
    /*
    ostringstream temp;
    temp << "Centre x = " << centre.x << "\n      y = " << centre.y << "   ";
    string s = temp.str();
    ofDrawBitmapString( s, pixSize.x/2+10, pixSize.y/2+10 );
    */
}
//-----------------------------------------------------------------------
void Grid::displayInfo(){
    
    // display some "useful information"
    
    ofSetColor(255,255,255,255);
    
    ostringstream temp;
    int line = 10; // text output pos
    
    
    ////////-------/////////
    temp << "scale = " << scale << "   ";
    string s = temp.str();
	ofDrawBitmapString( s, 10, line );
    line+=30;
    temp.str("");
    ////////-------/////////
    temp << "Top Left = " << topLeft.x << "," << topLeft.y << "   ";
    s = temp.str();
    ofDrawBitmapString( s, 10, line );
    line+=60;
    temp.str("");
    
    ////////-------/////////
    temp << "View Size = " << size.x << "," << size.y << "   ";
    s = temp.str();
    ofDrawBitmapString( s, 10, line );
    line+=60;
    temp.str("");
    
    ////////-------/////////
    
    for(int i=0;i<10;i++){ // TODO 5bit specific
        temp << midiCC[i] << " ";
        s = temp.str();
        ofDrawBitmapString( s, 10, line );
        line+=20;
        temp.str("");
    }
}
 //--------------------------------------------------------------
void Grid::update(){  // ?
    // "update" bit of a crap name - all we do here is calculate the dimension params from the x,y position

    vector<int> params = calculateParamsFromCoord(centre);
    for(int i = 0;i<2*paramsPerDim;i++){
        midiCC[i] = params[i];
    }

}
//--------------------------------------------------------------
void Grid::move(int moveX, int moveY){
    // numspacing, pixelspacing stay the same
    
    // convert pixels to surf units
    TwoVector moveS;
    moveS.setCoord(moveX * scale, moveY * scale);
    
    topLeft = topLeft - moveS;  // - because moving to the right means taking away from offset
    centre = centre - moveS;
    eventLogger.logEvent(-1, centre, scale);
    viewWasChanged();
}
//--------------------------------------------------------------
void Grid::zoom(float factor){
    if(maxZoom && factor > 1.0){
        return;
        
    }
    if(factor < 1.0){
        maxZoom = false;
    }
    if(minZoom && factor < 1.0){
        return;
        
    }
    if(factor > 1.0){
        minZoom = false;
    }
    scale = scale*factor; // simple eh?

    // update view size using centre
    // and scale...
    size.x = size.x*factor; // zooming in, size gets SMALLER (view less)
    size.y = size.y*factor;

    
    eventLogger.logEvent(-2, centre, scale);
    viewWasChanged();

}
//--------------------------------------------------------------
void Grid::viewWasChanged(){
    checkLimits();
    // and calculate new params?
    

}

//--------------------------------------------------------------
void Grid::checkLimits(){
    // check for maximum zoom level
    // TODO: DODGY

    if(size.x > maxValue*2.0){
        cout << "maxZoom\n";
        maxZoom = true;
        size.x = maxValue*2.0;
        // need to also set y size back to
    }
    if(size.y > maxValue*2.0){
        cout << "maxZoom\n";
        maxZoom = true;
        size.y = maxValue*2.0;
    }
    
    if(size.x < minValue){
        cout << "min Zoom\n";
        minZoom = true;
        size.x = minValue;
        // need to also set y size back to
    }
    if(size.y < minValue){
        minZoom = true;
        cout << "min Zoom\n";
        size.y = minValue;
    }
    
    scale = size.x/pixSize.x;
    size.y = scale * pixSize.y;
    
    topLeft.x = centre.x - size.x/2;
    topLeft.y = centre.y - size.y/2;
    // check for negatives
    
    // allow centre to be at limits
    if((topLeft.x + size.x*0.5) < 0.0){
        cout << "left Wall\n";
        topLeft.x = 0.0 - size.x*0.5;
        centre.x = 0.0;
    }

    if(topLeft.y + size.y*0.5 < 0.0) {
        cout << "top Wall\n";
        topLeft.y = 0.0 - size.y*0.5;
        centre.y = 0.0;
    }
    
   // does topleft refer to lines or view?
    
        // check max 
    if(topLeft.x + size.x/2 > maxValue){
        cout << "right Wall\n";
        topLeft.x = maxValue - size.x/2;
        centre.x = maxValue;
    }
    if(topLeft.y + size.y/2 > maxValue) {
        cout << "bottom Wall\n";
        topLeft.y = maxValue - size.y/2;
        centre.y = maxValue;
    }

}
//--------------------------------------------------------------
void Grid::checkConsistencies(){
    // debug function to check all the parameters are consistent maybe

}
//--------------------------------------------------------------
void Grid::setCoord(TwoVector coord){
    
    centre = coord;
    viewWasChanged();
}
//--------------------------------------------------------------
TwoVector Grid::getCoord(){
    
    return centre;
}
//--------------------------------------------------------------
vector<int> Grid::getParams(){
    // return a vector with midiCCs in
    // should we store params somewhere and use this as a low computation get ?
    vector<int> params(2*paramsPerDim,0);
    //
    for(int i = 0;i<2*paramsPerDim;i++){
        params[i] = midiCC[i];
    }
    return params;
}
//--------------------------------------------------------------
void Grid::setParams(vector<int> params){
    // input midiCCs, and go to the appropriate coordinate

    for(int i = 0;i<2*paramsPerDim;i++){
        midiCC[i] = 64;
    }
    TwoVector coord = calculateCoordFromParams(params);
    setCoord(coord);
    
    viewWasChanged();

}
//--------------------------------------------------------------
TwoVector Grid::calculateCoordFromParams(vector<int> params){

    vector<int> ccValueX(paramsPerDim);
    vector<int> ccValueY(paramsPerDim);
    for(int i=0;i<paramsPerDim;i++){
        ccValueX[i] = params[i];
        ccValueY[i] = params[i+paramsPerDim];
    }
    vector<vector <bool> > codesX = midiToGray(ccValueX);
    vector<vector <bool> > codesY = midiToGray(ccValueY);

    vector<int> base32X = codesToBase32(codesX);
    vector<int> base32Y = codesToBase32(codesY);
    
    TwoVector result;
    result.x = base32toCoord(base32X);
    result.y = base32toCoord(base32Y);
    return result;
}

//--------------------------------------------------------------
vector<int> Grid::calculateParamsFromCoord(TwoVector coord){
    // some arrays in reverse order of power from normal numbers!   1,2,4,
    
    // centre to base 32
    if (coord.x > maxValue || coord.y > maxValue){
        cout << "calculateParams Error: centre double value is too large" << "\n";
        vector<int> empty;
        return empty;
    }

    //--------------------------
    // X
    vector<int> base32x = coordTobase32(coord.x); // 7 numbers from 0 to 31
    vector<vector <bool> > grayCodesX;
    
    int size = base32x.size();
    for(int i=0;i<size;i++){
        grayCodesX.push_back(intToGray(base32x[i]));
    }
    
    vector<int> result;
    result = grayToMidi(grayCodesX);
    //--------------------------
    // AND FOR Y
    vector<int> base32y = coordTobase32(coord.y);
    vector<vector <bool> > grayCodesY;
    
    size = base32y.size();
    for(int i=0;i<size;i++){
        grayCodesY.push_back(intToGray(base32y[i]));
    }

    vector<int> resultY;
    resultY = grayToMidi(grayCodesY);
    
    // concatenate
    result.insert( result.end(), resultY.begin(), resultY.end() );
    return result;
}
//-------------------------------------------------------------------

// for 1 dimension!!! i.e. call this twice
vector<int> Grid::grayToMidi(vector<vector <bool> > grayCodes){

    // gray to midi CC values
    // loop thru the scales to build up a CC number per dimension
    int midiCCresult[paramsPerDim]; // TODO dims specific
    for(int i=0;i<paramsPerDim;i++){
        midiCCresult[i] = 0;
    }
    
    int bin = 1;
    bin = bin << grayCodes.size()-1;
    
    int midP = 0;
    
    vector<vector <bool> >::iterator piter = grayCodes.begin();
    for(;piter < grayCodes.end();piter++){ // each lesser power of 2
        midP = 0; // reset
        vector<bool>::iterator diter = (*piter).begin();
        for(; diter <= (*piter).end();diter++){ // each one is different dimension
            int ig = int(*diter); // convert binary to int
            //cout << "ig: " << ig;
            midiCCresult[midP] += ig*bin; // mult by power of two
            midP++;
        }
        bin = bin >> 1; // next power of 2 down
        
    }
    
    // put in vector
    vector<int> result;
    for(int i=0;i<paramsPerDim;i++){
        result.push_back(midiCCresult[i]);
    }
    
    return result;
    
    
}


//--------------------------------------------------------------

vector<bool> Grid::intToGray(int num, int dimToTravel){

    // dimToTravel - this is the dimension that we want the walk to traverse, i.e. the last non zero digit of the code
    // so swap it for 3 to produce correct orientation

    // just use look up table... until it gets huuuge.
    vector<bool> grayCode = vcode[num];
    grayCode[3] = vcode[num][dimToTravel];
    grayCode[dimToTravel] = vcode[num][3];
    return grayCode;
}
//--------------------------------------------------------------
vector<int> Grid::walkDiff(vector<bool> left, vector<bool> right){
    // horrible
    vector<int> result(2,0);

    int size = left.size();
    for(int i = 0; i < size; i++){
        if(left[i] && !right[i]){  // 1 - 0
            // dim
            result[0] = i;
            result[1] = 1;
        }else if(!left[i] && right[i]){ // 0 - 1
            result[0] = i;
            result[1] = -1;
        }else{ // equal do nothing
            
            
        }
    }
    
    return result;
}

//--------------------------------------------------------------
/*
breaks down float into a base 32 number (2^D)
each of these is converted to gray code
this is then converted to 10 parameters, where each 32-digit becomes separate power of 2
 so "zoom" grid 32-patches are converted to 2-scales. 1,2,4,8,16,32,64,(128?) biggest number is 32^7
*/
vector<int> Grid::coordTobase32(double value){
    //double base = 32.0;
    if(value < 0.0){
        cout << "coordTobase32 error: input value is negative\n";
        value = 0.0;
    }else if(value > maxValue){
        cout << "coordTobase32 error: input value too big!\n";
    }
    double rem, divdr = 0.0;
    int digit32;

    // what power of 32 to start at?
    int maxpow = 7; // midi cc specific
    vector<int> result; 

    rem = value;
    for(;maxpow >=0;maxpow--){
        // repeatedly get digit and remainder. This is exactly what we're doing in draw !?... could put all this in one place?
        divdr = pow((double)codeLength,(double)maxpow);
        digit32 = floor(rem/divdr);
        rem = rem - digit32*divdr; 
        result.push_back(digit32);  // array, biggest index is smallest power
    }
    // at this point rem should be fractional... use for interp?

    return result;
}
//--------------------------------------------------------------

// WHY HERE XCODE?
vector<int> Grid::codesToBase32(vector<vector<bool> > inCodes){
    vector<int> result;
    for(int i=0;i<paramBitDepth;i++){
        result.push_back(grayToInt(inCodes[i]));
    }
    return result;
}
//--------------------------------------------------------------

int Grid::grayToInt(vector<bool> incode){
    // look for match in table
    
    int s = vcode.size();
    
    for(int i=0; i<s;i++){ // fill vector
        if(vcode[i] == incode){
            return i;
        }
        
    }
    cout << "grayToInt error: no matching code found!";
    return -1;
}
//--------------------------------------------------------------
double Grid::base32toCoord(vector<int> base32Digs){
    // build up the big float from a base 32 number
    double result = 0.0;
   
    // what power of 32 to start at?
    int mpow = base32Digs.size() - 1; // should be 7...
    
    for(int p=0;p<=mpow;p++){ // biggest index is smallest power
        result += ((double)base32Digs[p]) * pow(32.0, (double)mpow-p);
  
    }
    return result;
    
}
//--------------------------------------------------------------
// for 1 dimension - takes in 5 cc params and outputs  7 codes 
vector<vector <bool> > Grid::midiToGray(vector<int> ccValue){
    int pow2 = 1 << (paramBitDepth-1);

    vector<int> aCode(paramsPerDim);
    vector<vector<int> > theXCodes(paramBitDepth,aCode);
    
    // build up binary gray code representations from the bits of the midi ccs

    vector<vector<bool> > theCodes(paramBitDepth, vector<bool>(paramsPerDim));

    // x
    for(int p=0;p<paramBitDepth;p++){

        for(int i=0;i<paramsPerDim;i++){
            
            bool bit = (pow2 == (ccValue[i] & pow2));
            theCodes[p][i] = bit;
        }
        pow2 = pow2 >> 1;
        
    }
    return theCodes;
}
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------