rt300@4: // rt300@4: // grid.cpp rt300@4: // oscSenderExample rt300@4: // rt300@4: // Created by Robert Tubb on 03/10/2012. rt300@4: // rt300@37: // This is the grid view, i.e. the viewable representation of hilbert surface rt300@4: rt300@4: #include "grid.h" rt300@4: rt300@4: rt300@4: #include rt300@4: rt300@47: Grid theGridView; // global because it saves PAIN rt300@47: rt300@4: extern PresetManager presetManager; rt300@4: extern EventLogger eventLogger; rt300@34: extern Hilbert hilbert; rt300@4: //-------------------------------------------------------------- rt300@43: Grid::Grid(): minValue(30), paramsPerDim(5), paramBitDepth(7), hilbert(){ rt300@4: rt300@37: interpLevel = 4; rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: //-------------------------------------------------------------- rt300@4: Grid::~Grid(){ rt300@4: rt300@4: } rt300@4: void Grid::init(){ rt300@44: presetWasTapped = false; rt300@43: maxValue = pow(2.0,paramsPerDim*paramBitDepth)-1; rt300@38: interpolateMode = NO_INTERPOLATION; rt300@37: rt300@4: maxZoom = false; rt300@4: minZoom = false; rt300@4: rt300@43: midiCC = vector(paramsPerDim*2,0); rt300@43: rt300@4: pixSize.setCoord(ofGetWidth(), ofGetHeight()); rt300@4: rt300@39: //set scale to random rt300@39: double scaleLevel = (ofRandomf() + 1.00001)*0.5*paramBitDepth; rt300@39: scale = pow(2.0, scaleLevel*paramBitDepth); rt300@35: rt300@37: smallestGridSpacing = 3; //pixels rt300@37: rt300@8: snapDist = TwoVector(9,9); rt300@5: snapped = false; rt300@4: rt300@4: size.setCoord(pixSize.x*scale, pixSize.y*scale); rt300@4: centre.setCoord(maxValue/2 , maxValue/2); rt300@4: topLeft.setCoord(centre.x - size.x/2, centre.y - size.y/2); rt300@34: rt300@43: // do this now rt300@43: hilbert.changeCurve(paramBitDepth, paramsPerDim); rt300@4: rt300@4: // set a starting param value, find coord that does it. rt300@4: vector params(paramsPerDim*2); rt300@4: for(int i = 0;i<2*paramsPerDim;i++){ rt300@4: midiCC[i] = 12; rt300@4: params[i] = 12; rt300@4: } rt300@35: rt300@35: rt300@4: TwoVector coord = calculateCoordFromParams(params); rt300@4: setCoord(coord); rt300@4: rt300@4: viewWasChanged(); rt300@37: calculateInterpolateLevel(); rt300@39: rt300@39: // log the starting zoom level rt300@43: eventLogger.logEvent(ZOOM,getCoord(), scale); rt300@43: rt300@43: rt300@43: ///////// TEST HILBERT rt300@43: rt300@43: rt300@43: /* rt300@43: Hilbert hilbert1(7,1); rt300@43: rt300@43: unsigned long long inCoord = 100; rt300@43: vector checkParam; rt300@43: checkParam = hilbert1.calculateParamsFromIndex(inCoord); rt300@43: cout << "PARAMS: "; rt300@43: for(int i=0; i<4;i++){ rt300@43: cout << checkParam[i] << " "; rt300@43: } rt300@43: cout << '\n'; rt300@43: unsigned long long outCoord = hilbert1.calculateIndexFromParams(checkParam); rt300@43: cout << "UNIT TEST COORD = " << outCoord << "\n"; rt300@43: */ rt300@39: rt300@4: } rt300@39: //-------------------------------------------------------------- rt300@4: template int sgn(T val) { rt300@4: return (T(0) < val) - (val < T(0)); rt300@4: } rt300@43: //-------------------------------------------------------------- rt300@43: void Grid::changeNumberOfParamsPerDim(){ rt300@43: // experimental func rt300@43: // try using different gray code, etc etc rt300@43: paramsPerDim = 4; rt300@43: rt300@43: } rt300@37: //-------------------------------------------------------------- rt300@38: double Grid::calculateInterpolateLevel(){ rt300@37: if(interpolateMode == INTERPOLATE_GRID){ rt300@38: rt300@38: // log_32 of scale times the pixel size we want to be the "integral" interp rt300@38: int N = 1 << paramsPerDim; rt300@38: interpLevel = log(scale*N*smallestGridSpacing)/log(N); rt300@38: rt300@38: return interpLevel; rt300@37: }else{ rt300@37: interpLevel = 0; rt300@37: return 0; // ??? rt300@37: } rt300@37: } rt300@37: //-------------------------------------------------------------- rt300@39: TwoVector Grid::getCoordForPresetSave(){ // bad functions get bad names rt300@39: rt300@39: // rt300@39: TwoVector coord = calculateCoordFromParams(getParams()); rt300@39: return coord; rt300@39: rt300@39: } rt300@39: //-------------------------------------------------------------- rt300@39: void Grid::setScale(double scaleLevel){ rt300@39: rt300@39: // random rt300@39: //double p = scaleLevel*paramBitDepth*paramsPerDim*0.5; rt300@39: rt300@39: rt300@39: if(scaleLevel > paramBitDepth || scaleLevel < 0){ rt300@39: cout << "Error: Bad scale level: " << scaleLevel << "\n"; rt300@43: scaleLevel = round(paramBitDepth*0.5); rt300@39: } rt300@39: rt300@39: scale = pow(2.0, scaleLevel*paramsPerDim); rt300@39: rt300@39: // update view size using centre rt300@39: // and scale... rt300@39: size.x = ofGetWidth()*scale; // zooming in, size gets SMALLER (view less) rt300@39: size.y = ofGetHeight()*scale; rt300@39: rt300@39: rt300@39: rt300@39: viewWasChanged(); rt300@39: calculateInterpolateLevel(); rt300@43: eventLogger.logEvent(ZOOM, getCoord(), scale); rt300@39: } rt300@39: //-------------------------------------------------------------- rt300@38: void Grid::setInterpolation(interpolateModeType mode){ rt300@38: interpolateMode = mode; rt300@38: viewWasChanged(); rt300@38: calculateInterpolateLevel(); rt300@38: eventLogger.clearTrail(); rt300@38: } rt300@38: //-------------------------------------------------------------- rt300@37: void Grid::draw(){ rt300@37: if(interpolateMode == NO_INTERPOLATION){ rt300@37: drawGridLines(); rt300@37: drawPresets(); rt300@37: }else if(interpolateMode == INTERPOLATE_GRID){ rt300@37: // calculate according to smallest gridlines rt300@37: drawGridLines(); rt300@37: rt300@37: }else if(interpolateMode == INTERPOLATE_PRESET){ rt300@37: drawPresets(); rt300@37: return; // ??? rt300@37: } rt300@37: rt300@37: rt300@37: rt300@37: // draw centre cross hairs rt300@37: drawCrossHairs(); rt300@37: rt300@37: rt300@37: // draw the undo history trail, given viewing area rt300@37: eventLogger.drawTrail(topLeft, topLeft + size); rt300@37: } rt300@4: //-------------------------------------------------------------- rt300@37: void Grid::drawGridLines(){ // draw lines rt300@4: // TODO too friggin long rt300@4: rt300@16: pixSize.setCoord(ofGetWidth(), ofGetHeight()); // incase of rotation? rt300@37: rt300@4: int power = 0; rt300@4: rt300@4: // these get big! rt300@4: double divsr; rt300@4: double yl,xl; rt300@4: double gridSize = 0; rt300@4: rt300@4: double firstLineXPos, lastLineXPos,firstLineYPos, lastLineYPos, remleft, xoffset, yoffset = 0.0; rt300@4: double xstart, xfinish, ystart,yfinish; rt300@4: // these don't rt300@4: int xpos, ypos = 0; rt300@4: rt300@4: float alpha; rt300@4: bool done = false; rt300@37: float markpow = 1 << paramsPerDim; // power that denotes next grid markings e.g. 32 rt300@4: int lineWidth = 0; rt300@4: int colCycle = 0; rt300@4: rt300@4: rt300@37: // loop thru powers of (base?) smallest to biggest rt300@37: // to determine which should be shown as grids rt300@4: while (!done){ rt300@4: gridSize = pow(markpow,power); rt300@4: rt300@37: colCycle = power % paramBitDepth; rt300@4: rt300@4: divsr = size.x / gridSize; rt300@4: rt300@4: // if (divisor i.e. number of lines is less than 1000 rt300@37: if( ofGetWidth()/divsr >= smallestGridSpacing){ rt300@4: rt300@4: // calculate transparency rt300@37: float visCont = log10(divsr*2); // 0 if only 1 line, 3 if 1000 lines rt300@4: alpha = 90*(3 - visCont); rt300@4: ofSetLineWidth(lineWidth); rt300@4: rt300@4: // cycle colors for different scales rt300@4: if(colCycle == 0){ rt300@4: ofSetColor(255,255,255,alpha); rt300@4: }else if(colCycle == 1){ rt300@44: ofSetColor(75,0,130,alpha); rt300@4: }else if(colCycle == 2){ rt300@4: ofSetColor(0,0,255,alpha); rt300@4: }else if(colCycle == 3){ rt300@4: ofSetColor(0,255,0,alpha); rt300@4: }else if(colCycle == 4){ rt300@44: ofSetColor(255,255,0,alpha); rt300@4: }else if(colCycle == 5){ rt300@44: ofSetColor(255,127,0,alpha); rt300@4: }else if(colCycle == 6){ rt300@44: ofSetColor(255,0,0,alpha); rt300@4: }else if(colCycle == 7){ rt300@44: ofSetColor(255,255,255,255); rt300@4: }else{ rt300@44: ofSetColor(255,255,255,255); rt300@43: cout << "colour cycle > 7\n"; rt300@4: } rt300@4: rt300@4: rt300@4: // draw level numbers associated with each color. This would not be true if markpow wasnt 2^d rt300@4: ////////-------///////// rt300@4: /* rt300@4: temp << "THIS LEVEL = " << (int)pow(2.0,power) << " "; rt300@4: string s = temp.str(); rt300@4: ofDrawBitmapString( s, 10, line ); rt300@4: line+=30; rt300@4: temp.str(""); rt300@4: *////////-------///////// rt300@4: rt300@4: // draw presets at this level rt300@4: // fill in smallest squares if they contain a preset rt300@4: // how to do this efficiently? rt300@4: rt300@4: rt300@4: if(topLeft.x < 0.0){ rt300@4: firstLineXPos = 0.0; rt300@4: xoffset = firstLineXPos - topLeft.x; rt300@4: xstart = xoffset/scale; rt300@4: }else{ rt300@4: firstLineXPos = topLeft.x; rt300@4: // kinda float version of % operator rt300@4: remleft = ceil(firstLineXPos/gridSize); rt300@4: xoffset = (remleft * gridSize) - topLeft.x; rt300@4: xstart = 0; rt300@4: } rt300@4: if(topLeft.x + size.x > maxValue){ rt300@4: lastLineXPos = maxValue+1 - topLeft.x; rt300@4: rt300@4: rt300@4: }else{ rt300@4: lastLineXPos = size.x; rt300@4: rt300@4: } rt300@4: xfinish = lastLineXPos/scale; rt300@4: rt300@4: //////////------------------------ rt300@4: // same for y rt300@4: rt300@4: if(topLeft.y < 0.0){ rt300@4: firstLineYPos = 0.0; rt300@4: yoffset = firstLineYPos - topLeft.y; rt300@4: ystart = yoffset/scale; rt300@4: }else{ rt300@4: firstLineYPos = topLeft.y; rt300@4: // kinda float version of % operator rt300@4: remleft = ceil(firstLineYPos/gridSize); rt300@4: yoffset = (remleft * gridSize) - topLeft.y; rt300@4: ystart = 0; rt300@4: } rt300@4: if(topLeft.y + size.y > maxValue){ rt300@4: lastLineYPos = maxValue+1 - topLeft.y; rt300@4: rt300@4: }else{ rt300@4: lastLineYPos = size.y; rt300@4: rt300@4: } rt300@4: yfinish = lastLineYPos/scale; rt300@4: // ------------------------------------------- rt300@4: // now draw rt300@4: for(xl = xoffset; xl <= (lastLineXPos); xl+= gridSize){ rt300@4: rt300@4: xpos = xl/scale; rt300@4: ofLine(xpos, ystart, xpos, yfinish); rt300@4: rt300@4: } rt300@4: rt300@4: for(yl = yoffset; yl <= (lastLineYPos); yl+= gridSize){ rt300@4: rt300@4: ypos = yl/scale; rt300@4: ofLine(xstart, ypos, xfinish, ypos); rt300@4: } rt300@4: rt300@4: rt300@37: } rt300@37: /*else if (divsr < 1){ rt300@37: // maximum only one line. worth checking so that bigger lines still show up? rt300@4: done = true; rt300@4: } rt300@37: */ rt300@4: power++; rt300@37: if(power > paramBitDepth) rt300@37: done = true; rt300@37: rt300@4: } rt300@4: //cout << "draw done" << "\n"; rt300@4: //displayInfo(); rt300@4: rt300@33: rt300@33: rt300@4: ////////-------//////// rt300@4: /* rt300@4: ostringstream temp; rt300@4: temp << "Centre x = " << centre.x << "\n y = " << centre.y << " "; rt300@4: string s = temp.str(); rt300@4: ofDrawBitmapString( s, pixSize.x/2+10, pixSize.y/2+10 ); rt300@4: */ rt300@4: } rt300@5: //----------------------------------------------------------------------- rt300@5: void Grid::drawCrossHairs(){ rt300@5: // snapping rt300@5: TwoVector pos; rt300@5: if(snapped){ rt300@5: pos = coordToPixel(snapCentre); rt300@5: }else{ rt300@5: pos = coordToPixel(centre); rt300@5: rt300@5: } rt300@5: ofSetColor(255, 0, 0); rt300@5: ofNoFill(); rt300@5: ofLine(pos.x-20, pos.y, pos.x+20, pos.y); rt300@5: ofLine(pos.x, pos.y-20, pos.x, pos.y+20); rt300@5: ofEllipse(pos.x, pos.y, 20, 20); rt300@5: ofFill(); rt300@5: } rt300@5: //----------------------------------------------------------------------- rt300@4: TwoVector Grid::coordToPixel(TwoVector coord){ rt300@4: TwoVector pix; rt300@4: pix.x = (coord.x - topLeft.x)/scale; rt300@4: pix.y = (coord.y - topLeft.y)/scale; rt300@4: return pix; rt300@4: rt300@4: } rt300@44: //----------------------------------------------------------------------- rt300@44: TwoVector Grid::pixelToCoord(TwoVector pixel){ rt300@4: rt300@44: return pixel*scale + topLeft; rt300@44: rt300@44: } rt300@4: //----------------------------------------------------------------------- rt300@4: void Grid::drawPresets(){ rt300@5: presetManager.drawPresetsInRange(topLeft, topLeft + size); rt300@7: // draw snapped preset info rt300@44: if(tappedPreset && tappedPreset != NULL){ rt300@44: TwoVector presetPos = coordToPixel(tappedPreset->coordinates); rt300@44: ofDrawBitmapString( tappedPreset->displayTextDescription(), presetPos.x, presetPos.y ); rt300@44: }else if( (snapped) && closestPreset != NULL){ rt300@44: ofDrawBitmapString( closestPreset->displayTextDescription(), pixSize.x/2 +10, pixSize.y/2+10 ); rt300@7: } rt300@44: rt300@33: rt300@4: } rt300@4: rt300@4: //----------------------------------------------------------------------- rt300@4: void Grid::displayInfo(){ rt300@4: rt300@4: // display some "useful information" rt300@4: rt300@4: ofSetColor(255,255,255,255); rt300@4: rt300@4: ostringstream temp; rt300@4: int line = 10; // text output pos rt300@4: rt300@4: rt300@4: ////////-------///////// rt300@4: temp << "scale = " << scale << " "; rt300@4: string s = temp.str(); rt300@4: ofDrawBitmapString( s, 10, line ); rt300@4: line+=30; rt300@4: temp.str(""); rt300@4: ////////-------///////// rt300@4: temp << "Top Left = " << topLeft.x << "," << topLeft.y << " "; rt300@4: s = temp.str(); rt300@4: ofDrawBitmapString( s, 10, line ); rt300@4: line+=60; rt300@4: temp.str(""); rt300@4: rt300@4: ////////-------///////// rt300@4: temp << "View Size = " << size.x << "," << size.y << " "; rt300@4: s = temp.str(); rt300@4: ofDrawBitmapString( s, 10, line ); rt300@4: line+=60; rt300@4: temp.str(""); rt300@4: rt300@4: ////////-------///////// rt300@4: rt300@43: for(int i=0;i<2*paramsPerDim;i++){ // TODO 5bit specific rt300@4: temp << midiCC[i] << " "; rt300@4: s = temp.str(); rt300@4: ofDrawBitmapString( s, 10, line ); rt300@4: line+=20; rt300@4: temp.str(""); rt300@4: } rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: void Grid::update(){ // ? rt300@46: if(automatedMovementInProgress){ rt300@46: continueInterpolatedAnimation(); rt300@46: } rt300@4: rt300@4: } rt300@44: //-------------------------------------------------------------- rt300@44: void Grid::tap(TwoVector pixel){ rt300@44: static Preset * lastTappedPreset; rt300@44: rt300@44: // search nearby for preset rt300@44: TwoVector tapCoord = pixelToCoord(pixel); rt300@44: rt300@44: vector closePresets = presetManager.getPresetsInRange(tapCoord - snapDist*2*scale, tapCoord + snapDist*2*scale); rt300@44: if(closePresets.size() > 0){ rt300@44: tappedPreset = getClosestPresetOf(closePresets); rt300@44: //interpolateTo(coordinates); rt300@44: cout << "tapped preset: " << tappedPreset->name << "\n"; rt300@44: presetWasTapped = true; rt300@44: rt300@44: if(lastTappedPreset == tappedPreset){ rt300@44: cout << "DOUBLE tapped preset: " << tappedPreset->name << "\n"; rt300@44: rt300@44: //TODO interpolate to this! exciting! rt300@46: rt300@46: startInterpolatedAnimation(tappedPreset->coordinates); rt300@46: eventLogger.logEvent(PRESET_DOUBLE_TAPPED, tappedPreset->coordinates, scale); rt300@44: rt300@44: rt300@44: } rt300@44: rt300@44: lastTappedPreset = tappedPreset; rt300@44: }else{ rt300@44: // nothing ? rt300@44: } rt300@44: } rt300@46: //----------------- rt300@5: rt300@46: void Grid::startInterpolatedAnimation(TwoVector endCoordinate){ rt300@46: automatedMovementInProgress = true; rt300@46: rt300@46: framesRemaining = PRESET_INTERP_TIME; rt300@46: rt300@46: startInterpParams = calculateParamsFromCoord(centre); rt300@46: endInterpParams = calculateParamsFromCoord(endCoordinate); rt300@46: endInterpCoord = endCoordinate; rt300@46: } rt300@46: //----------------- rt300@46: void Grid::continueInterpolatedAnimation(){ rt300@46: rt300@46: framesRemaining--; rt300@46: if(framesRemaining <= 0){ rt300@46: //finished rt300@46: // set position to preset rt300@46: centre = endInterpCoord; rt300@46: rt300@46: //will do snapping and checking rt300@46: viewWasChanged(); rt300@46: // update all the sliders and shit rt300@46: rt300@46: ((testApp *)ofGetAppPtr())->setAllGUISliders(getParams()); rt300@46: // set flag in testapp rt300@46: automatedMovementInProgress = false; rt300@46: return; rt300@46: } rt300@46: // calculate next step rt300@46: TwoVector moveVector = (endInterpCoord - centre)*(1/(float)framesRemaining); rt300@46: rt300@46: centre = centre + moveVector; rt300@46: topLeft.x = centre.x - size.x/2; rt300@46: topLeft.y = centre.y - size.y/2; rt300@46: rt300@46: float frac = (float)framesRemaining/(float)PRESET_INTERP_TIME; rt300@46: rt300@46: vector currentParams = interpVector( startInterpParams,endInterpParams, frac); rt300@46: rt300@46: ((testApp *)ofGetAppPtr())->setAllGUISliders(currentParams); rt300@46: rt300@46: eventLogger.logEvent(SCROLL,getCoord(),getScale()); rt300@46: rt300@46: } rt300@4: //-------------------------------------------------------------- rt300@33: rt300@4: void Grid::move(TwoVector moveP){ rt300@4: // numspacing, pixelspacing stay the same rt300@4: rt300@4: // convert pixels to surf units rt300@4: TwoVector moveS; rt300@4: moveS = moveP * scale; rt300@4: rt300@4: topLeft = topLeft - moveS; // - because moving to the right means taking away from offset rt300@4: centre = centre - moveS; rt300@4: rt300@4: viewWasChanged(); rt300@43: eventLogger.logEvent(SCROLL, getCoord(), scale); rt300@4: rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@46: // move the grid to a point (coordinate rather than pixel value) without triggering calculations rt300@46: void Grid::animateTo(TwoVector point){ rt300@46: rt300@46: centre = point; rt300@46: topLeft.x = centre.x - size.x/2; rt300@46: topLeft.y = centre.y - size.y/2; rt300@46: rt300@46: eventLogger.logEvent(SCROLL, getCoord(), scale); // TODO not *really* a user scroll... rt300@46: rt300@46: } rt300@46: //-------------------------------------------------------------- rt300@4: void Grid::zoom(float factor){ rt300@5: if(snapped)centre = (centre + snapCentre)*0.5; // clunky rt300@5: rt300@4: if(maxZoom && factor > 1.0){ rt300@4: return; rt300@4: rt300@4: } rt300@4: if(factor < 1.0){ rt300@4: maxZoom = false; rt300@4: } rt300@4: if(minZoom && factor < 1.0){ rt300@4: return; rt300@4: rt300@4: } rt300@4: if(factor > 1.0){ rt300@4: minZoom = false; rt300@4: } rt300@4: scale = scale*factor; // simple eh? rt300@4: rt300@4: // update view size using centre rt300@4: // and scale... rt300@4: size.x = size.x*factor; // zooming in, size gets SMALLER (view less) rt300@4: size.y = size.y*factor; rt300@4: rt300@4: rt300@4: rt300@4: viewWasChanged(); rt300@37: calculateInterpolateLevel(); rt300@43: eventLogger.logEvent(ZOOM, getCoord(), scale); rt300@4: rt300@5: } rt300@5: //-------------------------------------------------------------- rt300@20: void Grid::shiftCentreToSnapped(){ rt300@20: // TODO just in case we're freezing something rt300@20: // snapping actually change centre rt300@20: centre = snapCentre; rt300@46: topLeft.x = centre.x - size.x/2; rt300@46: topLeft.y = centre.y - size.y/2; rt300@20: } rt300@20: //-------------------------------------------------------------- rt300@44: Preset * Grid::getClosestPresetOf(vector somepresets){ rt300@44: double dist, mindist = maxValue; rt300@44: Preset * cPreset = somepresets[0]; rt300@44: rt300@44: for(vector::iterator piter = somepresets.begin(); piter < somepresets.end(); piter++){ rt300@44: dist = (*piter)->coordinates.distanceTo(centre); rt300@44: rt300@44: if(dist < mindist){ rt300@44: mindist = dist; rt300@44: cPreset = (*piter); rt300@44: } rt300@44: } rt300@44: return cPreset; rt300@44: } rt300@44: //-------------------------------------------------------------- rt300@5: void Grid::snapCheck(){ rt300@47: rt300@47: lEvent *closestEvalPt; // needs to be here because .h wont see it for some reason rt300@38: rt300@5: // check environs for presets. rt300@44: rt300@5: vector closePresets = presetManager.getPresetsInRange(centre - snapDist*scale, centre + snapDist*scale); rt300@5: if(closePresets.size() > 0){ rt300@45: if(interpolateMode == INTERPOLATE_GRID){ rt300@45: snapped = false; rt300@45: closestPreset = NULL; rt300@45: snapCentre = centre; rt300@45: return; rt300@45: } // no presets visible rt300@45: rt300@5: snapped = true; rt300@44: presetWasTapped = false; rt300@44: closestPreset = getClosestPresetOf(closePresets); rt300@5: snapCentre = closestPreset->coordinates; rt300@22: eventLogger.logEvent(SNAPPED_TO_PRESET, getCoord(),closestPreset->creationTime ); rt300@44: rt300@43: rt300@5: }else{ rt300@45: // look for evaluation points rt300@44: // do an evaluation snap check . duplicate code >:[ rt300@44: rt300@44: vector closeEvalPoints = eventLogger.getEvaluationPointsInRange(centre - snapDist*scale, centre + snapDist*scale); rt300@44: if(closeEvalPoints.size() > 0){ rt300@44: snapped = true; rt300@44: rt300@44: double dist, mindist = maxValue; rt300@44: closestEvalPt = closeEvalPoints[0]; rt300@44: rt300@44: for(vector::iterator eiter = closeEvalPoints.begin(); eiter < closeEvalPoints.end(); eiter++){ rt300@44: TwoVector coord = TwoVector((*eiter)->val1,(*eiter)->val2); rt300@44: dist = coord.distanceTo(centre); rt300@44: rt300@44: if(dist < mindist){ rt300@44: mindist = dist; rt300@44: closestEvalPt = (*eiter); rt300@44: snapCentre = coord; rt300@44: } rt300@44: rt300@44: } rt300@44: rt300@44: eventLogger.logEvent(SNAPPED_TO_EVALPT, getCoord() ); rt300@44: rt300@44: }else{ rt300@44: snapped = false; rt300@44: closestPreset = NULL; rt300@44: closestEvalPt = NULL; rt300@44: tappedPreset = NULL; rt300@44: snapCentre = centre; rt300@44: } rt300@44: rt300@44: rt300@44: rt300@5: } rt300@5: rt300@4: rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@5: void Grid::setMaxZoom(){ // got to smallest (white) rt300@5: if(snapped)centre = snapCentre; rt300@5: size.x = maxValue*2.0; rt300@5: scale = size.x/pixSize.x; rt300@5: size.y = scale * pixSize.y; rt300@7: maxZoom = true; rt300@7: minZoom = false; rt300@5: viewWasChanged(); rt300@5: } rt300@5: //-------------------------------------------------------------- rt300@5: void Grid::setMinZoom(){ // go to entire space (orange) rt300@5: if(snapped)centre = snapCentre; rt300@5: size.x = minValue*2.0; rt300@5: scale = size.x/pixSize.x; rt300@5: size.y = scale * pixSize.y; rt300@7: minZoom = true; rt300@7: maxZoom = false; rt300@5: viewWasChanged(); rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: void Grid::viewWasChanged(){ rt300@5: snapCheck(); rt300@4: checkLimits(); rt300@5: // calculate new params? rt300@5: vector params; rt300@5: if(snapped){ rt300@5: params = calculateParamsFromCoord(snapCentre); rt300@5: }else{ rt300@37: if(interpolateMode == NO_INTERPOLATION){ rt300@37: params = calculateParamsFromCoord(centre); rt300@37: }else if(interpolateMode == INTERPOLATE_GRID){ rt300@37: params = calculateInterpolatedParamsFromCoord(centre); rt300@37: } rt300@5: } rt300@4: for(int i = 0;i<2*paramsPerDim;i++){ rt300@4: midiCC[i] = params[i]; rt300@4: } rt300@4: rt300@4: } rt300@4: rt300@4: //-------------------------------------------------------------- rt300@4: void Grid::checkLimits(){ rt300@4: // check for maximum zoom level rt300@5: rt300@4: rt300@4: if(size.x > maxValue*2.0){ rt300@39: rt300@4: maxZoom = true; rt300@4: size.x = maxValue*2.0; rt300@4: // need to also set y size back to rt300@4: } rt300@4: if(size.y > maxValue*2.0){ rt300@39: rt300@4: maxZoom = true; rt300@4: size.y = maxValue*2.0; rt300@4: } rt300@4: rt300@4: if(size.x < minValue){ rt300@39: rt300@4: minZoom = true; rt300@4: size.x = minValue; rt300@4: // need to also set y size back to rt300@4: } rt300@4: if(size.y < minValue){ rt300@4: minZoom = true; rt300@39: rt300@4: size.y = minValue; rt300@4: } rt300@4: rt300@4: scale = size.x/pixSize.x; rt300@4: size.y = scale * pixSize.y; rt300@4: rt300@4: topLeft.x = centre.x - size.x/2; rt300@4: topLeft.y = centre.y - size.y/2; rt300@4: // check for negatives rt300@4: rt300@4: // allow centre to be at limits rt300@4: if((topLeft.x + size.x*0.5) < 0.0){ rt300@4: topLeft.x = 0.0 - size.x*0.5; rt300@4: centre.x = 0.0; rt300@4: } rt300@4: rt300@4: if(topLeft.y + size.y*0.5 < 0.0) { rt300@4: topLeft.y = 0.0 - size.y*0.5; rt300@4: centre.y = 0.0; rt300@4: } rt300@4: rt300@4: // does topleft refer to lines or view? rt300@4: rt300@4: // check max rt300@4: if(topLeft.x + size.x/2 > maxValue){ rt300@4: topLeft.x = maxValue - size.x/2; rt300@4: centre.x = maxValue; rt300@4: } rt300@4: if(topLeft.y + size.y/2 > maxValue) { rt300@4: topLeft.y = maxValue - size.y/2; rt300@4: centre.y = maxValue; rt300@4: } rt300@4: rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: void Grid::checkConsistencies(){ rt300@4: // debug function to check all the parameters are consistent maybe rt300@4: rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: void Grid::setCoord(TwoVector coord){ rt300@4: rt300@4: centre = coord; rt300@5: rt300@4: viewWasChanged(); rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: TwoVector Grid::getCoord(){ rt300@5: // return read point crosshairs rt300@5: if(snapped){ rt300@5: return snapCentre; rt300@5: } rt300@4: return centre; rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: vector Grid::getParams(){ rt300@4: // return a vector with midiCCs in rt300@43: // if less than 10, fill rest with zeros rt300@43: rt300@43: vector params(10,0); rt300@4: // rt300@4: for(int i = 0;i<2*paramsPerDim;i++){ rt300@4: params[i] = midiCC[i]; rt300@4: } rt300@43: for(int i = 2*paramsPerDim;i<10;i++){ rt300@43: params[i] = 0; rt300@43: } rt300@4: return params; rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: void Grid::setParams(vector params){ rt300@4: // input midiCCs, and go to the appropriate coordinate rt300@4: rt300@4: for(int i = 0;i<2*paramsPerDim;i++){ rt300@43: midiCC[i] = params[i]; rt300@4: } rt300@4: TwoVector coord = calculateCoordFromParams(params); rt300@4: setCoord(coord); rt300@4: rt300@20: // snapping? rt300@4: viewWasChanged(); rt300@4: rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@5: //-------------------------------------------------------------- rt300@5: //-------------------------------------------------------------- rt300@5: //-------------------------------------------------------------- rt300@5: //-------------------------------------------------------------- rt300@5: #pragma mark const utils rt300@5: rt300@37: TwoVector Grid::calculateCoordFromParams(vector params){ rt300@4: rt300@4: vector ccValueX(paramsPerDim); rt300@4: vector ccValueY(paramsPerDim); rt300@4: for(int i=0;i Grid::calculateParamsFromCoord(TwoVector coord){ rt300@35: rt300@4: if (coord.x > maxValue || coord.y > maxValue){ rt300@4: cout << "calculateParams Error: centre double value is too large" << "\n"; rt300@4: vector empty; rt300@4: return empty; rt300@4: } rt300@35: if (coord.x < 0 || coord.y < 0){ rt300@35: cout << "calculateParams Error: centre double value is negative" << "\n"; rt300@35: vector empty; rt300@35: return empty; rt300@4: } rt300@4: rt300@35: // X rt300@35: vector resultX; rt300@35: resultX = hilbert.calculateParamsFromIndex(coord.x); rt300@35: // Y rt300@4: vector resultY; rt300@35: resultY = hilbert.calculateParamsFromIndex(coord.y); rt300@4: rt300@4: // concatenate rt300@35: vector result = resultX; rt300@35: rt300@4: result.insert( result.end(), resultY.begin(), resultY.end() ); rt300@4: return result; rt300@4: } rt300@37: //-------------------------------------------------------------- rt300@37: vector Grid::calculateInterpolatedParamsFromCoord(TwoVector coord){ rt300@4: rt300@37: vector result; rt300@37: rt300@38: int interpLevelBigGrid = ceil(interpLevel); rt300@38: int interpLevelSmallGrid = floor(interpLevel); rt300@38: rt300@38: if(interpLevelBigGrid >= paramBitDepth){ rt300@38: // maximum is bit-1 so don't do interp at all rt300@38: vector pSmallGrid = interpHilbert(interpLevelSmallGrid, coord); rt300@38: return pSmallGrid; rt300@38: } rt300@38: double interpFrac = interpLevel - interpLevelSmallGrid; rt300@38: rt300@38: vector pBigGrid = interpHilbert(interpLevelBigGrid, coord); rt300@38: vector pSmallGrid = interpHilbert(interpLevelSmallGrid, coord); rt300@38: // now we need to interpolate twixt these two aswell! rt300@38: rt300@38: result = interpVector(pBigGrid, pSmallGrid, interpFrac); rt300@38: rt300@38: return result; rt300@38: } rt300@38: //-------------------------------------------------------------- rt300@38: vector Grid::interpHilbert(int bitlevel, TwoVector coord){ rt300@38: vector result; rt300@37: long long x = coord.x; rt300@37: long long y = coord.y; rt300@38: rt300@38: int roundDigits = (paramsPerDim*bitlevel); rt300@37: // knock off last digits rt300@37: x = x >> roundDigits; rt300@37: x = x << roundDigits; rt300@37: float frac = (coord.x - x)/(1 << roundDigits); rt300@37: long long xlower = x; rt300@37: long long xupper = x + (1 << roundDigits); rt300@38: rt300@37: vector pupper = hilbert.calculateParamsFromIndex(xupper); rt300@37: vector plower = hilbert.calculateParamsFromIndex(xlower); rt300@37: rt300@37: result = interpVector(pupper,plower,frac); rt300@38: // now Y rt300@37: y = y >> roundDigits; rt300@37: y = y << roundDigits; rt300@37: frac = (coord.y - y)/(1 << roundDigits); rt300@37: long long ylower = y; rt300@37: long long yupper = y + (1 << roundDigits); rt300@37: rt300@37: pupper = hilbert.calculateParamsFromIndex(yupper); rt300@37: plower = hilbert.calculateParamsFromIndex(ylower); rt300@37: rt300@37: vector resultY = interpVector(pupper,plower,frac); rt300@38: // stickem together rt300@37: result.insert( result.end(), resultY.begin(), resultY.end() ); rt300@38: rt300@37: return result; rt300@37: } rt300@37: //-------------------------------------------------------------- rt300@37: vector Grid::interpVector(vector upper, vector lower, float frac){ rt300@37: vector result; rt300@37: if(upper.size() != lower.size()){ rt300@37: cout << "Error: interpVector takes vectors of same length" << "\n"; rt300@37: return result; rt300@37: } rt300@37: if(frac > 1){ rt300@37: cout << "Error: bad fraction for vector interpolation" << "\n"; rt300@37: return result; rt300@37: } rt300@37: int N = upper.size(); rt300@37: for(int i=0; i Grid::walkDiff(vector left, vector right){ rt300@4: // horrible rt300@4: vector result(2,0); rt300@4: rt300@4: int size = left.size(); rt300@4: for(int i = 0; i < size; i++){ rt300@4: if(left[i] && !right[i]){ // 1 - 0 rt300@4: // dim rt300@4: result[0] = i; rt300@4: result[1] = 1; rt300@4: }else if(!left[i] && right[i]){ // 0 - 1 rt300@4: result[0] = i; rt300@4: result[1] = -1; rt300@4: }else{ // equal do nothing rt300@4: rt300@4: rt300@4: } rt300@4: } rt300@4: rt300@4: return result; rt300@4: } rt300@4: //-------------------------------------------------------------- rt300@4: //-------------------------------------------------------------- rt300@38: //-------------------------------------------------------------- rt300@38: rt300@38: rt300@38: //-------------------------------------------------------------- rt300@38: //-------------------------------------------------------------- rt300@38: //-------------------------------------------------------------- rt300@38: //-------------------------------------------------------------- rt300@38: //-------------------------------------------------------------- rt300@43: #pragma mark The OLD algorithm rt300@38: rt300@38: TwoVector Grid::calculateCoordFromParamsOld(vector params) const{ rt300@38: rt300@38: vector ccValueX(paramsPerDim); rt300@38: vector ccValueY(paramsPerDim); rt300@38: for(int i=0;i > codesX = midiToGray(ccValueX); rt300@38: vector > codesY = midiToGray(ccValueY); rt300@38: rt300@38: vector base32X = codesToBase32(codesX); rt300@38: vector base32Y = codesToBase32(codesY); rt300@38: rt300@38: TwoVector result; rt300@38: result.x = base32toCoord(base32X); rt300@38: result.y = base32toCoord(base32Y); rt300@38: return result; rt300@38: } rt300@38: rt300@38: //-------------------------------------------------------------- rt300@38: vector Grid::calculateParamsFromCoordOld(TwoVector coord) const{ rt300@38: // some arrays in reverse order of power from normal numbers! 1,2,4, rt300@38: rt300@38: // centre to base 32 rt300@38: if (coord.x > maxValue || coord.y > maxValue){ rt300@38: cout << "calculateParams Error: centre double value is too large" << "\n"; rt300@38: vector empty; rt300@38: return empty; rt300@38: } rt300@38: rt300@38: //-------------------------- rt300@38: // X rt300@38: vector base32x = coordTobase32(coord.x); // 7 numbers from 0 to 31 rt300@38: vector > grayCodesX; rt300@38: rt300@38: int size = base32x.size(); rt300@38: for(int i=0;i result; rt300@38: result = grayToMidi(grayCodesX); rt300@38: //-------------------------- rt300@38: // AND FOR Y rt300@38: vector base32y = coordTobase32(coord.y); rt300@38: vector > grayCodesY; rt300@38: rt300@38: size = base32y.size(); rt300@38: for(int i=0;i resultY; rt300@38: resultY = grayToMidi(grayCodesY); rt300@38: rt300@38: // concatenate rt300@38: result.insert( result.end(), resultY.begin(), resultY.end() ); rt300@38: return result; rt300@38: } rt300@38: //------------------------------------------------------------------- rt300@38: rt300@38: // for 1 dimension!!! i.e. call this twice rt300@38: vector Grid::grayToMidi(vector > grayCodes) const{ rt300@38: rt300@38: // gray to midi CC values rt300@38: // loop thru the scales to build up a CC number per dimension rt300@38: int midiCCresult[paramsPerDim]; // TODO dims specific rt300@38: for(int i=0;i >::iterator piter = grayCodes.begin(); rt300@38: for(;piter < grayCodes.end();piter++){ // each lesser power of 2 rt300@38: midP = 0; // reset rt300@38: vector::iterator diter = (*piter).begin(); rt300@38: for(; diter <= (*piter).end();diter++){ // each one is different dimension rt300@38: int ig = int(*diter); // convert binary to int rt300@38: //cout << "ig: " << ig; rt300@38: midiCCresult[midP] += ig*bin; // mult by power of two rt300@38: midP++; rt300@38: } rt300@38: bin = bin >> 1; // next power of 2 down rt300@38: rt300@38: } rt300@38: rt300@38: // put in vector rt300@38: vector result; rt300@38: for(int i=0;i Grid::intToGray(int num, int dimToTravel) const{ rt300@38: rt300@38: // dimToTravel - this is the dimension that we want the walk to traverse, i.e. the last non zero digit of the code rt300@38: // so swap it for 3 to produce correct orientation rt300@38: rt300@38: // just use look up table... until it gets huuuge. rt300@38: vector grayCode = vcode[num]; rt300@38: grayCode[3] = vcode[num][dimToTravel]; rt300@38: grayCode[dimToTravel] = vcode[num][3]; rt300@38: return grayCode; rt300@38: } rt300@38: //-------------------------------------------------------------- rt300@38: /* rt300@38: breaks down float into a base 32 number (2^D) rt300@38: each of these is converted to gray code rt300@38: this is then converted to 10 parameters, where each 32-digit becomes separate power of 2 rt300@38: so "zoom" grid 32-patches are converted to 2-scales. 1,2,4,8,16,32,64,(128?) biggest number is 32^7 rt300@38: */ rt300@38: vector Grid::coordTobase32(double value) const{ rt300@38: //double base = 32.0; rt300@38: if(value < 0.0){ rt300@38: cout << "coordTobase32 error: input value is negative\n"; rt300@38: value = 0.0; rt300@38: }else if(value > maxValue){ rt300@38: cout << "coordTobase32 error: input value too big!\n"; rt300@38: } rt300@38: double rem, divdr = 0.0; rt300@38: int digit32; rt300@38: rt300@38: // what power of 32 to start at? rt300@38: int maxpow = 7; // midi cc specific rt300@38: vector result; rt300@38: rt300@38: rem = value; rt300@38: for(;maxpow >=0;maxpow--){ rt300@38: // repeatedly get digit and remainder. This is exactly what we're doing in draw !?... could put all this in one place? rt300@38: divdr = pow((double)codeLength,(double)maxpow); rt300@38: digit32 = floor(rem/divdr); rt300@38: rem = rem - digit32*divdr; rt300@38: result.push_back(digit32); // array, biggest index is smallest power rt300@38: } rt300@38: // at this point rem should be fractional... use for interp? rt300@38: rt300@38: return result; rt300@38: } rt300@38: //-------------------------------------------------------------- rt300@38: rt300@38: vector Grid::codesToBase32(vector > inCodes) const{ rt300@38: vector result; rt300@38: for(int i=0;i incode) const{ rt300@38: // look for match in table rt300@38: rt300@38: int s = vcode.size(); rt300@38: rt300@38: for(int i=0; i base32Digs) const{ rt300@38: // build up the big float from a base 32 number rt300@38: double result = 0.0; rt300@38: rt300@38: // what power of 32 to start at? rt300@38: int mpow = base32Digs.size() - 1; // should be 7... rt300@38: rt300@38: for(int p=0;p<=mpow;p++){ // biggest index is smallest power rt300@38: result += ((double)base32Digs[p]) * pow(32.0, (double)mpow-p); rt300@38: rt300@38: } rt300@38: return result; rt300@38: rt300@38: } rt300@38: //-------------------------------------------------------------- rt300@38: // for 1 dimension - takes in 5 cc params and outputs 7 codes rt300@38: vector > Grid::midiToGray(vector ccValue) const{ rt300@38: int pow2 = 1 << (paramBitDepth-1); rt300@38: rt300@38: vector aCode(paramsPerDim); rt300@38: vector > theXCodes(paramBitDepth,aCode); rt300@38: rt300@38: // build up binary gray code representations from the bits of the midi ccs rt300@38: rt300@38: vector > theCodes(paramBitDepth, vector(paramsPerDim)); rt300@38: rt300@38: // x rt300@38: for(int p=0;p> 1; rt300@38: rt300@38: } rt300@38: return theCodes; rt300@38: } rt300@38: rt300@38: //-------------------------------------------------------------- rt300@38: void Grid::makeCode(){ rt300@38: rt300@38: ////////////////////////////////~~~,,,,,,,,......... rt300@38: ////////// gray code generation rt300@38: ////// rt300@38: /// rt300@38: // TODO 5bit specific rt300@38: // transition sequence.... what a palaver! only need to do once though. rt300@38: rt300@38: // max MRL rt300@38: 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}; rt300@38: // balanced rt300@38: int transB[] = {1,2,3,4,5,1,5,2,3,5,2,4,2,3,4,1,4,3,2,3,1,5,3,4,1,5,2,5,3,4,1,3}; rt300@38: for(int i = 0; i()); rt300@38: rt300@38: for(int j=0; j >::iterator cit; rt300@38: //vector::iterator bit; rt300@38: vector::iterator bit; rt300@38: int i = 0; rt300@38: for(cit=vcode.begin(); cit!=vcode.end() ; cit++){ // fill vector rt300@38: i++; rt300@38: cout << i << " = "; rt300@38: bit = (*cit).begin(); rt300@38: for(bit=(*cit).begin(); bit!=(*cit).end() ; bit++){ rt300@38: rt300@38: cout << *bit; rt300@38: } rt300@38: cout << "\n"; rt300@38: rt300@38: } rt300@38: rt300@38: // codeToInt unit test rt300@38: vector aCode = vcode[12]; rt300@38: int result = grayToInt(aCode); rt300@38: cout << "GRAY TO INT : " << result << "\n"; rt300@38: rt300@38: cout << "-------------------------------\n"; rt300@38: rt300@38: // base32toFloat unit test rt300@38: rt300@38: vector test32 = coordTobase32(869437.0); rt300@38: double cresult = base32toCoord(test32); rt300@38: cout << "base32toCoord : " << cresult << "\n"; rt300@38: rt300@38: cout << "-------------------------------\n"; rt300@38: rt300@38: rt300@38: rt300@38: // midiToGray unit test rt300@38: rt300@38: vector > incodes(paramBitDepth, vector(paramsPerDim)); rt300@38: for(int i=0;i<7;i++){ rt300@38: incodes[i] = vcode[i+5]; rt300@38: } rt300@38: rt300@38: vector midiTest; rt300@38: midiTest = grayToMidi(incodes); rt300@38: vector > outcodes = midiToGray(midiTest); rt300@38: rt300@38: vector< vector >::iterator cit1; rt300@38: //vector::iterator bit; rt300@38: vector::iterator bit1; rt300@38: cout << "midiToGray Unit test\n"; rt300@38: for(int i=0;i checkParam; rt300@38: checkParam = calculateParamsFromCoord(inCoord); rt300@38: for(int i=0; i<2*paramsPerDim;i++){ rt300@38: cout << checkParam[i] << "_"; rt300@38: } rt300@38: TwoVector outCoord = calculateCoordFromParams(checkParam); rt300@38: cout << "Unit TEst coord = " << outCoord.x << "," << outCoord.y << "\n"; rt300@38: */ rt300@38: rt300@38: } rt300@38: //-------------------------------------------------------------- rt300@38: //-------------------------------------------------------------- rt300@38: //--------------------------------------------------------------