double LinearApproximation::approximate(const std::vector<double> &t_vals) const
{
  // Linear Approximation
  // A(x-x0) + B(y-y0) + C(z-z0) = 0
  validateVariableSize(t_vals);

//  print("Approximating: ", t_vals);

  std::vector<std::vector<double> > sortedPoints = sortByDistance(t_vals, m_values);

  if (!sortedPoints.empty() && distance(t_vals, sortedPoints[0]) == 0)
  {
    //return exact match
    return sortedPoints[0].back();
  }

  std::vector<std::vector<double> > optimalPoints = filterForProblemReduction(t_vals, sortedPoints);

//  print("Starting points: ", m_values);

//  print("Sorted Points: ", sortedPoints);
  std::vector<std::vector<double> > chosenPoints = filterForSimilarity(t_vals, optimalPoints);

//  print("Chosen points: ", chosenPoints);


  std::vector<std::vector<std::vector<double> > > coefficientMatrices = buildCoefficientMatrices(chosenPoints);

//  print("Coefficients: ", coefficientMatrices);


  std::vector<double> coefficients = solveDeterminates(coefficientMatrices);

//  print("Coefficients: ", coefficients);
  assert(coefficients.size() == m_numVars + 1);



  double approximation = 0;
  for (size_t i = 0; i < m_numVars; ++i)
  {
    // A(x-x0) + B(y-y0) + C(z-z0) ... 
    approximation += coefficients[i] * (t_vals[i] - chosenPoints[0][i]);
  }

  // the last variable is the one we are solving for 
  // z=(z0*C+(y0-y)*B+(x0-x)*A)/C
  if (coefficients[m_numVars] == 0)
  {
    throw std::runtime_error("Unabled to approximate, not enough data");
  }

  approximation = (approximation + chosenPoints[0][m_numVars] * coefficients[m_numVars]) / coefficients[m_numVars];

//  std::cout << "final approximation: " << approximation << std::endl;

  return approximation;
}
std::pair<double, double> LinearApproximation::nearestFurthestNeighborDistances(const std::vector<double> &t_vals) const
{
  std::vector<std::vector<double> > sorted = sortByDistance(t_vals, m_values);

  if (sorted.size() < 1)
  {
    throw std::range_error("no neighbors");
  }

  return std::make_pair(distance(t_vals, sorted.front()), distance(t_vals, sorted.back()));
}
void Prototype01::guiSystemEvent(ofxUIEventArgs &e){
    string name = e.widget->getName();
    
    bool bSamplePalette = false;
    
    if (name == "n clusters"){
        bSamplePalette = true;
    } else if (name.find('.') != string::npos){
        ofxUIToggle *t = (ofxUIToggle*)e.widget;
        if (t != NULL){
            if (t->getValue()){
                img.loadImage(getDataPath()+"images/"+name);
                colorExtractedFbo.allocate(img.getWidth(),img.getHeight());
                bSamplePalette = true;
            }
        }
    }
    
    if(bSamplePalette && img.isAllocated()){
        ofImage smallImg;
        smallImg = img;
        smallImg.resize(img.getWidth()*0.25, img.getHeight()*0.25);
        const int colorCount = nPaletteColors;
        const int sampleCount = smallImg.getHeight() * smallImg.getWidth();
        cv::Mat colorSamples( sampleCount, 1, CV_32FC3 );
        
        // get our pixels
        unsigned char * pixels = smallImg.getPixels();
        
        // clear our list of colors
        palette.clear();
        
        // build our matrix of samples
        cv::MatIterator_<cv::Vec3f> sampleIt = colorSamples.begin<cv::Vec3f>();
        for(int i=0; i<sampleCount; i++){
            int pos = i * 3;
            *sampleIt = cv::Vec3f( pixels[pos], pixels[pos+1], pixels[pos+2] );
            sampleIt++;
        }
        
        // call kmeans
        cv::Mat labels, clusters;
        cv::kmeans( colorSamples, colorCount, labels, cv::TermCriteria(), 2, cv::KMEANS_RANDOM_CENTERS, clusters ); //cv::TermCriteria::COUNT, 8, 0
        
        for( int i = 0; i < colorCount; ++i ){
            ofColor clusterColor = ofColor( clusters.at<cv::Vec3f>(i,0)[0], clusters.at<cv::Vec3f>(i,0)[1], clusters.at<cv::Vec3f>(i,0)[2] );
            palette.push_back(clusterColor);
        }
        
        sortByDistance(palette);
    }
    
    bApplyMedian = true;
}
void SingleBrush::selfUpdate(){
    ofPushStyle();
    ofSetColor(255);
    
    video.update();
    
    if (bClean){
        colorAdded.begin();
        ofClear(0,0);
        colorAdded.end();
        
        brush.clear();
        canvas.beginBoth();
        background->draw();
        canvas.endBoth();
        
        bColorSample = true;
        bClean = false;
    }
    
    if(bColorSample && video.isInitialized()){
        ofImage smallImg;
        smallImg.setFromPixels(video.getPixels(), video.getWidth(), video.getHeight(), OF_IMAGE_COLOR);
        smallImg.resize(video.getWidth()*0.25, video.getHeight()*0.25);
        const int colorCount = nPaletteColors;
        const int sampleCount = smallImg.getHeight() * smallImg.getWidth();
        cv::Mat colorSamples( sampleCount, 1, CV_32FC3 );
        
        // get our pixels
        unsigned char * pixels = smallImg.getPixels();
        
        // clear our list of colors
        palette.clear();
        
        // build our matrix of samples
        cv::MatIterator_<cv::Vec3f> sampleIt = colorSamples.begin<cv::Vec3f>();
        for(int i=0; i<sampleCount; i++){
            int pos = i * 3;
            *sampleIt = cv::Vec3f( pixels[pos], pixels[pos+1], pixels[pos+2] );
            sampleIt++;
        }
        
        // call kmeans
        cv::Mat labels, clusters;
        cv::kmeans( colorSamples, colorCount, labels, cv::TermCriteria(), 2, cv::KMEANS_RANDOM_CENTERS, clusters ); //cv::TermCriteria::COUNT, 8, 0
        
        for( int i = 0; i < colorCount; ++i ){
            ofColor clusterColor = ofColor( clusters.at<cv::Vec3f>(i,0)[0], clusters.at<cv::Vec3f>(i,0)[1], clusters.at<cv::Vec3f>(i,0)[2] );
            palette.push_back(clusterColor);
        }
        
        sortByDistance(palette);
        
        bColorSample = false;
    }
    
    //  UPDATE
    //
    brush.update();
    //  RENDER
    //
    {
        //  Drawing on Canvas
        //
        {
            if(brush.bDown){
                if(brush.bInk){
                    colorAdded.begin();
                    ofClear(0, 0);
                    brushTexture.bind();
                    brush.draw();
                    brushTexture.unbind();
                    colorAdded.end();
                }
                
                water.swap();
                water.dst->begin();
                water.src->draw(0,0);
                ofSetColor(waterAmount);
                brush.drawMask();
                water.dst->end();
            }
            
            int width = ofGetWidth();
            int height = ofGetHeight();
            
            canvas.swap();
            canvas.dst->begin();
            colorAddShader.begin();
            colorAddShader.getShader().setUniformTexture("backbuffer", *(canvas.src), 0);
            colorAddShader.getShader().setUniformTexture("colorAddedTexture", colorAdded, 1);
            glBegin(GL_QUADS);
            glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
            glTexCoord2f(width, 0); glVertex3f(width, 0, 0);
            glTexCoord2f(width, height); glVertex3f(width, height, 0);
            glTexCoord2f(0,height);  glVertex3f(0,height, 0);
            glEnd();
            colorAddShader.end();
            canvas.dst->end();
            
            //  Water Effect
            //
            water.swap();
            water.dst->begin();
            ofClear(0,255);
            absorveShader.begin();
            water.src->draw(0, 0);
            absorveShader.end();
            water.dst->end();
            
//            normals << *(water.dst);
            flow << *(water.dst);
            
            noise.begin();
            noiseShader.begin();
            noiseShader.getShader().setUniformTexture("normalsTexture", flow, 1);
            noiseShader.getShader().setUniformTexture("waterTexture", *(water.dst), 2);
            water.dst->draw(0,0);
            noiseShader.end();
            noise.end();
            
            canvas.swap();
            canvas.dst->begin();
            displaceShader.begin();
            displaceShader.getShader().setUniformTexture("normals", noise, 1);
            displaceShader.getShader().setUniformTexture("dampMap", *(water.dst), 2);
            canvas.src->draw(0, 0);
            displaceShader.end();
            canvas.dst->end();
        }
        
        
        
    }
    ofPopStyle();
}