Mercurial > hg > soniczoomios
view grid.mm @ 37:8ed7522deaaa
Interpolation.
author | Robert Tubb <rt300@eecs.qmul.ac.uk> |
---|---|
date | Tue, 09 Apr 2013 17:14:31 +0100 |
parents | 790939017078 |
children | 0dfe9e0c01aa |
line wrap: on
line source
// // grid.cpp // oscSenderExample // // Created by Robert Tubb on 03/10/2012. // // This is the grid view, i.e. the viewable representation of hilbert surface #include "grid.h" #include <sstream> extern PresetManager presetManager; extern EventLogger eventLogger; extern Hilbert hilbert; //-------------------------------------------------------------- Grid::Grid(): maxValue(pow(32.0,7.0)-1), minValue(30), paramsPerDim(5), paramBitDepth(7){ interpLevel = 4; } //-------------------------------------------------------------- //-------------------------------------------------------------- Grid::~Grid(){ } void Grid::init(){ interpolateMode = INTERPOLATE_GRID; maxZoom = false; minZoom = false; pixSize.setCoord(ofGetWidth(), ofGetHeight()); //set scale and position to random scale = 15500.0; smallestGridSpacing = 3; //pixels snapDist = TwoVector(9,9); snapped = false; 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); hilbert.init(paramBitDepth, paramsPerDim); // 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] = 12; params[i] = 12; } TwoVector coord = calculateCoordFromParams(params); setCoord(coord); viewWasChanged(); calculateInterpolateLevel(); } template <typename T> int sgn(T val) { return (T(0) < val) - (val < T(0)); } //-------------------------------------------------------------- int Grid::calculateInterpolateLevel(){ if(interpolateMode == INTERPOLATE_GRID){ // calculate according to smallest gridlines // exactly same algorithm as for drawing lines // i.e. kinda duplicated code. float markpow = pow(paramsPerDim,2.0); for(int p = 0; p < paramBitDepth; p++){ double gridSize = pow(markpow,p); double divsr = size.x / gridSize; // return the spacing if( ofGetWidth()/divsr >= smallestGridSpacing){ interpLevel = p; return p; } } interpLevel = 0; return 0; }else{ interpLevel = 0; return 0; // ??? } } //-------------------------------------------------------------- void Grid::draw(){ if(interpolateMode == NO_INTERPOLATION){ drawGridLines(); drawPresets(); }else if(interpolateMode == INTERPOLATE_GRID){ // calculate according to smallest gridlines drawGridLines(); }else if(interpolateMode == INTERPOLATE_PRESET){ drawPresets(); return; // ??? } // draw centre cross hairs drawCrossHairs(); // draw the undo history trail, given viewing area eventLogger.drawTrail(topLeft, topLeft + size); } //-------------------------------------------------------------- void Grid::drawGridLines(){ // draw lines // TODO too friggin long pixSize.setCoord(ofGetWidth(), ofGetHeight()); // incase of rotation? 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 = 1 << paramsPerDim; // power that denotes next grid markings e.g. 32 int lineWidth = 0; int colCycle = 0; // loop thru powers of (base?) smallest to biggest // to determine which should be shown as grids while (!done){ gridSize = pow(markpow,power); colCycle = power % paramBitDepth; divsr = size.x / gridSize; // if (divisor i.e. number of lines is less than 1000 if( ofGetWidth()/divsr >= smallestGridSpacing){ // calculate transparency float visCont = log10(divsr*2); // 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){ // maximum only one line. worth checking so that bigger lines still show up? done = true; } */ power++; if(power > paramBitDepth) done = true; } //cout << "draw done" << "\n"; //displayInfo(); ////////-------//////// /* 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::drawCrossHairs(){ // snapping TwoVector pos; if(snapped){ pos = coordToPixel(snapCentre); }else{ pos = coordToPixel(centre); } ofSetColor(255, 0, 0); ofNoFill(); ofLine(pos.x-20, pos.y, pos.x+20, pos.y); ofLine(pos.x, pos.y-20, pos.x, pos.y+20); ofEllipse(pos.x, pos.y, 20, 20); ofFill(); } //----------------------------------------------------------------------- TwoVector Grid::coordToPixel(TwoVector coord){ TwoVector pix; pix.x = (coord.x - topLeft.x)/scale; pix.y = (coord.y - topLeft.y)/scale; return pix; } //----------------------------------------------------------------------- void Grid::drawPresets(){ presetManager.drawPresetsInRange(topLeft, topLeft + size); // draw snapped preset info if(snapped && closestPreset != NULL){ ofDrawBitmapString( closestPreset->displayTextDescription(), 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(){ // ? } //-------------------------------------------------------------- void Grid::move(TwoVector moveP){ // numspacing, pixelspacing stay the same // convert pixels to surf units TwoVector moveS; moveS = moveP * scale; topLeft = topLeft - moveS; // - because moving to the right means taking away from offset centre = centre - moveS; viewWasChanged(); eventLogger.logEvent(SCROLL, centre, scale); } //-------------------------------------------------------------- void Grid::zoom(float factor){ if(snapped)centre = (centre + snapCentre)*0.5; // clunky 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; viewWasChanged(); calculateInterpolateLevel(); eventLogger.logEvent(ZOOM, centre, scale); } //-------------------------------------------------------------- void Grid::shiftCentreToSnapped(){ // TODO just in case we're freezing something // snapping actually change centre centre = snapCentre; } //-------------------------------------------------------------- void Grid::snapCheck(){ // check environs for presets. vector<Preset *> closePresets = presetManager.getPresetsInRange(centre - snapDist*scale, centre + snapDist*scale); if(closePresets.size() > 0){ snapped = true; // find closest double dist, mindist = maxValue; closestPreset = closePresets[0]; for(vector<Preset *>::iterator piter = closePresets.begin(); piter < closePresets.end(); piter++){ dist = (*piter)->coordinates.distanceTo(centre); if(dist < mindist){ mindist = dist; closestPreset = *piter; } } snapCentre = closestPreset->coordinates; eventLogger.logEvent(SNAPPED_TO_PRESET, getCoord(),closestPreset->creationTime ); //cout << "SNAPPED CHECK\n"; }else{ snapped = false; closestPreset = NULL; snapCentre = centre; } } //-------------------------------------------------------------- void Grid::setMaxZoom(){ // got to smallest (white) if(snapped)centre = snapCentre; size.x = maxValue*2.0; scale = size.x/pixSize.x; size.y = scale * pixSize.y; maxZoom = true; minZoom = false; viewWasChanged(); } //-------------------------------------------------------------- void Grid::setMinZoom(){ // go to entire space (orange) if(snapped)centre = snapCentre; size.x = minValue*2.0; scale = size.x/pixSize.x; size.y = scale * pixSize.y; minZoom = true; maxZoom = false; viewWasChanged(); } //-------------------------------------------------------------- void Grid::viewWasChanged(){ snapCheck(); checkLimits(); // calculate new params? vector<int> params; if(snapped){ params = calculateParamsFromCoord(snapCentre); }else{ if(interpolateMode == NO_INTERPOLATION){ params = calculateParamsFromCoord(centre); }else if(interpolateMode == INTERPOLATE_GRID){ params = calculateInterpolatedParamsFromCoord(centre); } } for(int i = 0;i<2*paramsPerDim;i++){ midiCC[i] = params[i]; } } //-------------------------------------------------------------- void Grid::checkLimits(){ // check for maximum zoom level 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 read point crosshairs if(snapped){ return snapCentre; } 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); // snapping? viewWasChanged(); } //-------------------------------------------------------------- //-------------------------------------------------------------- //-------------------------------------------------------------- //-------------------------------------------------------------- //-------------------------------------------------------------- #pragma mark const utils 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]; } TwoVector result; result.x = hilbert.calculateIndexFromParams(ccValueX); result.y = hilbert.calculateIndexFromParams(ccValueY); return result; } //-------------------------------------------------------------- vector<int> Grid::calculateParamsFromCoord(TwoVector coord){ if (coord.x > maxValue || coord.y > maxValue){ cout << "calculateParams Error: centre double value is too large" << "\n"; vector<int> empty; return empty; } if (coord.x < 0 || coord.y < 0){ cout << "calculateParams Error: centre double value is negative" << "\n"; vector<int> empty; return empty; } // X vector<int> resultX; resultX = hilbert.calculateParamsFromIndex(coord.x); // Y vector<int> resultY; resultY = hilbert.calculateParamsFromIndex(coord.y); // concatenate vector<int> result = resultX; result.insert( result.end(), resultY.begin(), resultY.end() ); return result; } //-------------------------------------------------------------- vector<int> Grid::calculateInterpolatedParamsFromCoord(TwoVector coord){ vector<int> result; // round by masking according to interpLevel long long x = coord.x; long long y = coord.y; cout << interpLevel << "\n"; int roundDigits = (paramsPerDim*interpLevel); // knock off last digits x = x >> roundDigits; x = x << roundDigits; float frac = (coord.x - x)/(1 << roundDigits); long long xlower = x; long long xupper = x + (1 << roundDigits); vector<int> pupper = hilbert.calculateParamsFromIndex(xupper); vector<int> plower = hilbert.calculateParamsFromIndex(xlower); result = interpVector(pupper,plower,frac); // now Y y = y >> roundDigits; y = y << roundDigits; frac = (coord.y - y)/(1 << roundDigits); long long ylower = y; long long yupper = y + (1 << roundDigits); pupper = hilbert.calculateParamsFromIndex(yupper); plower = hilbert.calculateParamsFromIndex(ylower); vector<int> resultY = interpVector(pupper,plower,frac); // stickem together result.insert( result.end(), resultY.begin(), resultY.end() ); return result; } //-------------------------------------------------------------- vector<int> Grid::interpVector(vector<int> upper, vector<int> lower, float frac){ vector<int> result; if(upper.size() != lower.size()){ cout << "Error: interpVector takes vectors of same length" << "\n"; return result; } if(frac > 1){ cout << "Error: bad fraction for vector interpolation" << "\n"; return result; } int N = upper.size(); for(int i=0; i<N; i++){ result.push_back(frac*upper[i] + (1-frac)*lower[i]); } return result; } //-------------------------------------------------------------- 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; } //-------------------------------------------------------------- //-------------------------------------------------------------- //--------------------------------------------------------------