コード例 #1
0
ファイル: gate.cpp プロジェクト: pontikos/flowWorkspace
/*
 * when the original gate vertices are at the threshold
 * it is likely that the gates were truncated in flowJo xml
 * currently what we can do is to extend it to the real data range to avoid losing
 * the data points that are below this theshold range
 * to cut data range)
 */
void polygonGate::extend(flowData & fdata,float extend_val){
	string x=param.xName();
	string y=param.yName();
	valarray<double> xdata(fdata.subset(x));
	valarray<double> ydata(fdata.subset(y));

	vector<coordinate> v=param.getVertices();
	/*
	 * get R_min
	 */
	double xMin=xdata.min();
	double yMin=ydata.min();
	for(unsigned i=0;i<v.size();i++)
	{
		if(v.at(i).x<=extend_val)
		{
			if(g_loglevel>=POPULATION_LEVEL)
				COUT <<"extending "<<x<<"from "<<v.at(i).x<<" to :"<<xMin<<endl;
			v.at(i).x=xMin;
		}
		if(v.at(i).y<=extend_val)
		{
			if(g_loglevel>=POPULATION_LEVEL)
				COUT <<"extending "<<y<<"from "<<v.at(i).y<<" to :"<<yMin<<endl;
			v.at(i).y=yMin;

		}
	}
	param.setVertices(v);
}
コード例 #2
0
//makePlot created a plot. We now update its data.
void MainWindow::refreshPlot(int *x, int *y, int len, uint8_t plot_index)
{
    //From array to QVector:
    QVector<double> xdata(len), ydata(len);
    qCopy(x, x+len, xdata.begin());
    qCopy(y, y+len, ydata.begin());

    ui->customPlot->graph(plot_index)->setData(xdata, ydata);

    //ToDo: Modify this code to take the max of all curves!

    //In Automatic mode we constantly adjust the axis:
    if(ui->radioButtonXAuto->isChecked())
    {
        array_minmax(x, len, &plot_xmin, &plot_xmax);

        ui->customPlot->xAxis->setRange(plot_xmin, plot_xmax);
        ui->plot_xmin_lineEdit->setText(QString::number(plot_xmin));
        ui->plot_xmax_lineEdit->setText(QString::number(plot_xmax));
    }

    if(ui->radioButtonYAuto->isChecked())
    {
        array_minmax(y, len, &plot_ymin, &plot_ymax);

        ui->customPlot->yAxis->setRange(plot_ymin-10, plot_ymax+10);
        ui->plot_ymin_lineEdit->setText(QString::number(plot_ymin));
        ui->plot_ymax_lineEdit->setText(QString::number(plot_ymax));
    }

    ui->customPlot->replot();
}
コード例 #3
0
ファイル: gate.cpp プロジェクト: pontikos/flowWorkspace
vector<bool> rectGate::gating(flowData & fdata){

	vector<coordinate> vertices=param.getVertices();
	unsigned nVertex=vertices.size();
	if(nVertex!=2)
		throw(domain_error("invalid number of vertices for rectgate!"));
	string x=param.xName();
	string y=param.yName();
	valarray<double> xdata(fdata.subset(x));
	valarray<double> ydata(fdata.subset(y));

	unsigned nEvents=xdata.size();
	//init the indices
	vector<bool> ind(nEvents);

	/*
	 * actual gating
	 */
	for(unsigned i=0;i<nEvents;i++)
	{
		bool inX,inY;
		double xMin=vertices.at(0).x;
		double yMin=vertices.at(0).y;

		double xMax=vertices.at(1).x;
		double yMax=vertices.at(1).y;

		if(xMin>xMax||yMin>yMax)
			throw(domain_error("invalid vertices for rectgate!"));

		inX=xdata[i]<=xMax&&xdata[i]>=xMin;
		inY=ydata[i]<=yMax&&ydata[i]>=yMin;
		ind[i]=inX&&inY;
	}


	if(isNegate())
		ind.flip();

	return ind;

}
コード例 #4
0
ファイル: gate.cpp プロジェクト: pontikos/flowWorkspace
/*
 * translated from flowCore::%in% method for ellipsoidGate
 */
vector<bool> ellipseGate::gating(flowData & fdata){


	// get data

	valarray<double> xdata(fdata.subset(param.xName()));
	valarray<double> ydata(fdata.subset(param.yName()));

	//center the data
	xdata = xdata - mu.x;
	ydata = ydata - mu.y;

	//inverse the cov matrix
	/*
	 * 	| a,b |
		| c,d | --> | aa, bb |
					| cc, dd |
	 */
	double a , b, c, d;
	a = cov.at(0).x;
	b = cov.at(0).y;
	c = cov.at(1).x;
	d = cov.at(1).y;

	double det = a* d - b* c;
	double aa, bb, cc, dd;
	aa = d/det;
	bb = -b/det;
	cc = -c/det;
	dd = a/det;

	// if inside of the ellipse
	unsigned nEvents=xdata.size();
	vector<bool> res (nEvents);
	for(unsigned i =0;i<nEvents;i++){
		double x = xdata[i];
		double y = ydata[i];
		res[i] = (x * x * aa + x* y * cc + x* y * bb + y * y * dd) <= pow(dist, 2);
	}

	return res;
}
コード例 #5
0
/******************************************************************************
* Plots the data computed by the modifier.
******************************************************************************/
void BinAndReduceModifierEditor::plotData()
{
	BinAndReduceModifier* modifier = static_object_cast<BinAndReduceModifier>(editObject());
	if(!modifier)
		return;

	int binDataSizeX = std::max(1, modifier->numberOfBinsX());
	int binDataSizeY = std::max(1, modifier->numberOfBinsY());
    if (modifier->is1D()) binDataSizeY = 1;
    size_t binDataSize = binDataSizeX*binDataSizeY;

    if (modifier->is1D()) {
        // If previous plot was a color map, delete and create graph.
        if (!_averagesGraph) {
            if (_averagesColorMap) {
                _averagesPlot->removePlottable(_averagesColorMap);
                _averagesColorMap = nullptr;
            }
            _averagesGraph = _averagesPlot->addGraph();
        }

        _averagesPlot->setInteraction(QCP::iRangeDrag, true);
        _averagesPlot->axisRect()->setRangeDrag(Qt::Vertical);
        _averagesPlot->setInteraction(QCP::iRangeZoom, true);
        _averagesPlot->axisRect()->setRangeZoom(Qt::Vertical);
        if(modifier->firstDerivative()) {
            _averagesPlot->yAxis->setLabel("d( "+modifier->sourceProperty().name()+" )/d( Position )");
        }
        else {
            _averagesPlot->yAxis->setLabel(modifier->sourceProperty().name());
        }

        if(modifier->binData().empty())
            return;

        QVector<double> xdata(binDataSize+2);
        QVector<double> ydata(binDataSize+2);
        double binSize = ( modifier->xAxisRangeEnd() - modifier->xAxisRangeStart() ) / binDataSize;
        for(int i = 0; i < binDataSize; i++) {
            xdata[i+1] = binSize * ((double)i + 0.5) + modifier->xAxisRangeStart();
            ydata[i+1] = modifier->binData()[i];
        }
        xdata.front() = modifier->xAxisRangeStart();
        ydata.front() = modifier->binData().front();
        xdata.back() = modifier->xAxisRangeEnd();
        ydata.back() = modifier->binData().back();
        _averagesPlot->graph()->setLineStyle(QCPGraph::lsStepCenter);
        _averagesPlot->graph()->setData(xdata, ydata);

        // Check if range is already correct, because setRange emits the rangeChanged signal
        // which is to be avoided if the range is not determined automatically.
        _rangeUpdate = false;
        _averagesPlot->xAxis->setRange(modifier->xAxisRangeStart(), modifier->xAxisRangeEnd());
        _averagesPlot->yAxis->setRange(modifier->propertyAxisRangeStart(), modifier->propertyAxisRangeEnd());
        _rangeUpdate = true;
    }
    else {
        // If previous plot was a graph, delete and create color map.
        if (!_averagesColorMap) {
            if (_averagesGraph) {
                _averagesPlot->removeGraph(_averagesGraph);
                _averagesGraph = nullptr;
            }
            _averagesColorMap = new QCPColorMap(_averagesPlot->xAxis, _averagesPlot->yAxis);
            _averagesPlot->addPlottable(_averagesColorMap);
        }

        _averagesPlot->setInteraction(QCP::iRangeDrag, false);
        _averagesPlot->setInteraction(QCP::iRangeZoom, false);
        _averagesPlot->yAxis->setLabel("Position");

        if(modifier->binData().empty())
            return;

        _averagesColorMap->setInterpolate(false);
        _averagesColorMap->setTightBoundary(false);
        _averagesColorMap->setGradient(QCPColorGradient::gpJet);

        _averagesColorMap->data()->setSize(binDataSizeX, binDataSizeY);
        _averagesColorMap->data()->setRange(QCPRange(modifier->xAxisRangeStart(), modifier->xAxisRangeEnd()),
                                            QCPRange(modifier->yAxisRangeStart(), modifier->yAxisRangeEnd()));

        _averagesPlot->xAxis->setRange(QCPRange(modifier->xAxisRangeStart(), modifier->xAxisRangeEnd()));
        _averagesPlot->yAxis->setRange(QCPRange(modifier->yAxisRangeStart(), modifier->yAxisRangeEnd()));

        // Copy data to QCPColorMapData object.
        for (int j = 0; j < binDataSizeY; j++) {
            for (int i = 0; i < binDataSizeX; i++) {
                _averagesColorMap->data()->setCell(i, j, modifier->binData()[j*binDataSizeX+i]);
            }
        }

        // Check if range is already correct, because setRange emits the rangeChanged signal
        // which is to be avoided if the range is not determined automatically.
        _rangeUpdate = false;
        _averagesColorMap->setDataRange(QCPRange(modifier->propertyAxisRangeStart(), modifier->propertyAxisRangeEnd()));
        _rangeUpdate = true;
    }
    
    _averagesPlot->replot(QCustomPlot::rpQueued);
}
コード例 #6
0
ファイル: gate.cpp プロジェクト: pontikos/flowWorkspace
/*
 *
 *  reimplement c++ version of inPolygon_c
 *  indices are allocated within gating function, so it is up to caller to free it
 *  and now it is freed in destructor of its owner "nodeProperties" object
 */
vector<bool> polygonGate::gating(flowData & fdata){




	vector<coordinate> vertices=param.getVertices();
	unsigned nVertex=vertices.size();

	string x=param.xName();
	string y=param.yName();
	valarray<double> xdata(fdata.subset(x));
	valarray<double> ydata(fdata.subset(y));

	unsigned nEvents=xdata.size();
	//init the indices
	vector<bool> ind(nEvents);


	unsigned counter;
	double xinters;
	double p1x, p2x, p1y, p2y;

	for(unsigned i=0; i<nEvents; i++)
	{//iterate over points
	p1x=vertices.at(0).x;
	p1y=vertices.at(0).y;
	counter=0;
	for(unsigned j=1; j <= nVertex; j++)
	{// iterate over vertices
	  /*p1x,p1y and p2x,p2y are the endpoints of the current vertex*/
	  if (j == nVertex)
	  {//the last vertice must "loop around"
		p2x = vertices.at(0).x;
		p2y = vertices.at(0).y;
	  }
	  else
	  {
		p2x = vertices.at(j).x;
		p2y = vertices.at(j).y;
	  }
	  /*if horizontal ray is in range of vertex find the x coordinate where
		ray and vertex intersect*/
	  if(ydata[i] >= min(p1y, p2y) && ydata[i] < max(p1y, p2y) &&xdata[i] <= max(p1x, p2x))
	  {
		  xinters = (ydata[i]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x;
		/*if intersection x coordinate == point x coordinate it lies on the
		  boundary of the polygon, which means "in"*/
		if(xinters==xdata[i])
		{
		  counter=1;
		  break;
		}
		/*count how many vertices are passed by the ray*/
		if (xinters > xdata[i])counter++;
	  }
	  p1x=p2x;
	  p1y=p2y;
	}
	/*uneven number of vertices passed means "in"*/

	ind[i]=((counter % 2) != 0);

	}
	if(isNegate())
		ind.flip();
	return ind;
}
コード例 #7
0
ファイル: mw_tab_plot_1.cpp プロジェクト: JFDuval/FlexSEA
//makePlot created a plot. We now update its data.
void MainWindow::refreshPlot(int *x, int *y, int len, uint8_t plot_index)
{
    static int graph_ylim[2*VAR_NUM] = {0,0,0,0,0,0,0,0,0,0,0,0};

    //From array to QVector:
    QVector<double> xdata(len), ydata(len);
    qCopy(x, x+len, xdata.begin());
    qCopy(y, y+len, ydata.begin());

    ui->customPlot->graph(plot_index)->setData(xdata, ydata);

    //ToDo: Modify this code to take the max of all curves!

    //In Automatic mode we constantly adjust the axis:
    if(ui->radioButtonXAuto->isChecked())
    {
        array_minmax(x, len, &plot_xmin, &plot_xmax);

        ui->customPlot->xAxis->setRange(plot_xmin, plot_xmax);
        ui->plot_xmin_lineEdit->setText(QString::number(plot_xmin));
        ui->plot_xmax_lineEdit->setText(QString::number(plot_xmax));
    }
    else
    {
        //X is in manual mode.
    }

    if(ui->radioButtonYAuto->isChecked())
    {
        //Unusued channels (index == 0) aren't used for the automatic gain
        if(data_to_plot[plot_index] == 0)
        {
            //qDebug() << "Ch[" << plot_index << "] is Unused.";

            //Unused channel. We take the min & max from used channels.
            int u = 0;
            for(int k= 0; k < VAR_NUM; k++)
            {
                //Grab min & max from any used channel
                if(data_to_plot[k] != 0)
                {
                    plot_ymin = graph_ylim[k];
                    plot_ymax = graph_ylim[k+VAR_NUM];
                }
                else
                {
                    u++;
                }
            }

            if(u == VAR_NUM)
            {
                //All unused, force to 0:
                plot_ymin = -10;
                plot_ymax = 10;
            }
            //qDebug() << "Min/Max =" << plot_ymin << "," << plot_ymax;
        }
        else
        {
            //Limits for this graph:
            array_minmax(y, len, &plot_ymin, &plot_ymax);
            //qDebug() << "Ch[" << plot_index << "] is used.";
        }

        //Compare to all others and get max(max(())
        graph_ylim[plot_index] = plot_ymin;
        graph_ylim[plot_index+VAR_NUM] = plot_ymax;

        //qDebug() << "Min/Max =" << plot_ymin << "," << plot_ymax;

        array_minmax(graph_ylim, 2*VAR_NUM, &plot_ymin, &plot_ymax);

        //Add 5%:
        plot_ymin = (plot_ymin-(abs(plot_ymin)/20));
        plot_ymax = (plot_ymax+(abs(plot_ymax)/20));

        //Set axis 5% above minimum
        ui->customPlot->yAxis->setRange(plot_ymin, plot_ymax);
        ui->plot_ymin_lineEdit->setText(QString::number(plot_ymin));
        ui->plot_ymax_lineEdit->setText(QString::number(plot_ymax));
    }
    //qDebug() << "Final Min/Max =" << plot_ymin << "," << plot_ymax;
    //qDebug() << "";

    ui->customPlot->replot();
}
コード例 #8
0
ファイル: MeshEnergy.cpp プロジェクト: 151706061/Slicer3
void MeshEnergy::GetNormalsTangentPlane( const std::vector<int>& C, const std::vector<double>& phi,
                                         std::valarray<double>& ne1, std::valarray<double>& ne2, 
                                         MeshData* vtkNotUsed(meshdata))
{
  vtkPoints*    verts = meshdata->polydata->GetPoints();

  for( ::size_t k_ = 0; k_ < C.size(); k_++ )
    {
    int k = C[k_];
    std::vector<double> nhat(3);
    nhat[0] = meshdata->nx[k]; // these are normal to surface; the nx_ are the contour normals
    nhat[1] = meshdata->ny[k];
    nhat[2] = meshdata->nz[k];
// step 1. create the rotation matrix that orients the current normal as [0,0,1]'.

    double phiang = atan2( nhat[0], nhat[1] );
    std::vector<double> rotate1(9);
    rotate1[0] = cos(phiang); rotate1[1] = -sin(phiang); rotate1[2] = 0;
    rotate1[3] = sin(phiang); rotate1[4] = cos(phiang); rotate1[5] = 0;
    rotate1[6] = 0; rotate1[7] = 0; rotate1[8] = 1.0;
    std::vector<double> nhat_a(3);
    pkmult( nhat, rotate1, nhat_a );
    double ytilde = nhat_a[1];
    double theta = M_PI_2 - atan2(nhat[2],ytilde);
    std::vector<double> rotate2(9);
    rotate2[0] = 1.0; rotate2[1] = 0; rotate2[2] = 0;
    rotate2[3] = 0; rotate2[4] = cos(theta); rotate2[5] = -sin(theta);
    rotate2[6] = 0; rotate2[7] = sin(theta); rotate2[8] = cos(theta);
    std::vector<double> nhat_b(3);
    pkmult( nhat_a, rotate2, nhat_b );
// nhat_b should now be [0 0 1]'

    double thispt[3];
    verts->GetPoint( k, thispt );
// apply rotate2 * rotate1 to each *translated* neighbor of this k-th point
    ::size_t num_neigh = meshdata->adj[k].myNeighbs.size();
    double vec[3];
    std::vector<double> vv(3);
    std::vector<double> vv_(3);
    std::valarray<double> xdata(num_neigh);
    std::valarray<double> ydata(num_neigh);
    std::valarray<double> zdata(num_neigh);
// step 2. create temporary set of std::vectors as copies of neighboring points
// translated to origin
// step 3. apply the rotation to all these points
    for ( ::size_t i = 0; i < num_neigh; i++ )
      {
      int idx = meshdata->adj[k].myNeighbs[i];
      verts->GetPoint( idx, vec );
      vv[0] = vec[0] - thispt[0];
      vv[1] = vec[1] - thispt[1];
      vv[2] = vec[2] - thispt[2];
      pkmult( vv, rotate1, vv_ );
      pkmult( vv_, rotate2, vv );
      xdata[i] = vv[0];
      ydata[i] = vv[1];
      zdata[i] = phi[idx] - phi[k]; //vv[2];
// zero reference phi at the vertex where we are forming tangent plane
      }
/*if( abs(zdata).min() < 1e-6 )
continue;*/

// step 4. find least-squares fit for H(x,y) = ax + by
    std::valarray<double> RHS(2);
    std::valarray<double> ATA(4);
    ATA[0] = (xdata * xdata).sum();
    ATA[1] = (xdata * ydata).sum();
    ATA[2] = ATA[1];
    ATA[3] = (ydata * ydata).sum();

    RHS[0] = (xdata * zdata).sum();
    RHS[1] = (ydata * zdata).sum();

    int maxits = 1000;
    std::valarray<double> ab = RHS; // initial guess
    std::valarray<double> LHS(2);
    pkmult2( ab, ATA, LHS );
    double res = sqrt( ( (LHS - RHS)*(LHS - RHS) ).sum() );
    double tol = 1e-8;
    int iter = 0;
    while( iter < maxits && res > tol )
      {
      iter++;
      ab[0] = (RHS[0] - ( ab[1]*ATA[1] ) )/ ATA[0];
      ab[1] = (RHS[1] - ( ab[0]*ATA[2] ) )/ ATA[3];
      pkmult2( ab, ATA, LHS );
      res = sqrt( ( (LHS - RHS)*(LHS - RHS) ).sum() );
      }
    ne1[k_] = ab[0] / sqrt( (ab*ab).sum() );
    ne2[k_] = ab[1] / sqrt( (ab*ab).sum() );
// step 5. differentiate the plane along principal directions

    }

}
コード例 #9
0
ファイル: MeshEnergy.cpp プロジェクト: 151706061/Slicer3
void MeshEnergy::GetKappa( const std::vector<int>& C, const std::vector<double>& phi,
                           std::valarray<double>& kappa)
{
// kappa: divergence of normal
// dy^2 * dxx - 2dxdydxy + dx^2dyy / ( dx^2 + dy^2 )^(3/2)

  vtkPoints*    verts = meshdata->polydata->GetPoints();

  for( ::size_t k_ = 0; k_ < C.size(); k_++ )\
    {
    int k = C[k_];
    std::vector<double> nhat(3);
    nhat[0] = meshdata->nx[k]; // these are normal to surface; the nx_ are the contour normals
    nhat[1] = meshdata->ny[k];
    nhat[2] = meshdata->nz[k];
// step 1. create the rotation matrix that orients the current normal as [0,0,1]'.

    double phiang = atan2( nhat[0], nhat[1] );
    std::vector<double> rotate1(9);
    rotate1[0] = cos(phiang); rotate1[1] = -sin(phiang); rotate1[2] = 0;
    rotate1[3] = sin(phiang); rotate1[4] = cos(phiang); rotate1[5] = 0;
    rotate1[6] = 0; rotate1[7] = 0; rotate1[8] = 1.0;
    std::vector<double> nhat_a(3);
    pkmult( nhat, rotate1, nhat_a );
    double ytilde = nhat_a[1];
    double theta = M_PI_2 - atan2(nhat[2],ytilde);
    std::vector<double> rotate2(9);
    rotate2[0] = 1.0; rotate2[1] = 0; rotate2[2] = 0;
    rotate2[3] = 0; rotate2[4] = cos(theta); rotate2[5] = -sin(theta);
    rotate2[6] = 0; rotate2[7] = sin(theta); rotate2[8] = cos(theta);
    std::vector<double> nhat_b(3);
    pkmult( nhat_a, rotate2, nhat_b );
// nhat_b should now be [0 0 1]'

    double thispt[3];
    verts->GetPoint( k, thispt );
// apply rotate2 * rotate1 to each *translated* neighbor of this k-th point
    ::size_t num_neigh = meshdata->adj[k].myNeighbs.size();
    double vec[3];
    std::vector<double> vv(3);
    std::vector<double> vv_(3);
    std::valarray<double> xdata(num_neigh);
    std::valarray<double> ydata(num_neigh);
    std::valarray<double> zdata(num_neigh);
// step 2. create temporary set of std::vectors as copies of neighboring points
// translated to origin
// step 3. apply the rotation to all these points
    for (::size_t i = 0; i < num_neigh; i++ )
      {
      int idx = meshdata->adj[k].myNeighbs[i];
      verts->GetPoint( idx, vec );
      vv[0] = vec[0] - thispt[0];
      vv[1] = vec[1] - thispt[1];
      vv[2] = vec[2] - thispt[2];
      pkmult( vv, rotate1, vv_ );
      pkmult( vv_, rotate2, vv );
      xdata[i] = vv[0];
      ydata[i] = vv[1];
      zdata[i] = phi[idx] - phi[k]; //vv[2];
// zero reference phi at the vertex where we are forming tangent plane
      }
/*if( abs(zdata).min() < 1e-6 )
continue;*/

// step 4. find first derivatives
    double phi_x = 0.0;
    double phi_y = 0.0;
    {
    std::valarray<double> RHS(2);
    std::valarray<double> ATA(4);
    ATA[0] = (xdata * xdata).sum();
    ATA[1] = (xdata * ydata).sum();
    ATA[2] = ATA[1];
    ATA[3] = (ydata * ydata).sum();

    RHS[0] = (xdata * zdata).sum();
    RHS[1] = (ydata * zdata).sum();

    int maxits = 1000;
    std::valarray<double> ab = RHS; // initial guess
    std::valarray<double> LHS(2);
    pkmult2( ab, ATA, LHS );
    double res = sqrt( ( (LHS - RHS)*(LHS - RHS) ).sum() );
    double tol = 1e-8;
    int iter = 0;
    while( iter < maxits && res > tol )
      {
      iter++;
      ab[0] = (RHS[0] - ( ab[1]*ATA[1] ) )/ ATA[0];
      ab[1] = (RHS[1] - ( ab[0]*ATA[2] ) )/ ATA[3];
      pkmult2( ab, ATA, LHS );
      res = sqrt( ( (LHS - RHS)*(LHS - RHS) ).sum() );
      }
    phi_x = ab[0];
    phi_y = ab[1];
    }

// step 4. find least-squares fit for phi(x,y) = ax^2 + bxy + cy^2
// to get second derivatives
  std::valarray<double> RHS(3); // A'z
    RHS[0] = ( xdata * xdata * zdata  ).sum();
    RHS[1] = ( xdata * ydata * zdata  ).sum();
    RHS[2] = ( ydata * ydata * zdata  ).sum();

    double tik_delta = 1e-1 * abs(RHS).min();

    std::vector<double> ATA(9); // A'A
    ATA[0] = tik_delta + (xdata * xdata * xdata * xdata).sum();
    ATA[1] = (xdata * xdata * xdata * ydata).sum();
    ATA[2] = (xdata * xdata * ydata * ydata).sum();
    ATA[3] = (xdata * ydata * xdata * xdata).sum();
    ATA[4] = tik_delta + (xdata * ydata * xdata * ydata).sum();
    ATA[5] = (xdata * ydata * ydata * ydata).sum();
    ATA[6] = (ydata * ydata * xdata * xdata).sum();
    ATA[7] = (ydata * ydata * xdata * ydata).sum();
    ATA[8] = tik_delta + (ydata * ydata * ydata * ydata).sum();

    int maxits = 1000;
    std::valarray<double> abc = RHS; // initial guess
    std::valarray<double> LHS(3);
    pkmult( abc, ATA, LHS );
    double res = sqrt( ( (LHS - RHS)*(LHS - RHS) ).sum() );
    double tol = 1e-8;
    int iter = 0;
    while( iter < maxits && res > tol )
      {
      iter++;
      abc[0] = (RHS[0] - ( abc[1]*ATA[1] + abc[2]*ATA[2] ) )/ ATA[0];
      abc[1] = (RHS[1] - ( abc[0]*ATA[3] + abc[2]*ATA[5] ) )/ ATA[4];
      abc[2] = (RHS[2] - ( abc[0]*ATA[6] + abc[1]*ATA[7] ) )/ ATA[8];
      pkmult( abc, ATA, LHS );
      res = sqrt( ( (LHS - RHS)*(LHS - RHS) ).sum() );
      }
// step 5. get the derivatives from quadratic form
    double phi_xx = 2*abc[0];
    double phi_xy = abc[1];
    double phi_yy = 2*abc[2];

    kappa[k_] = phi_y * phi_y * phi_xx - 2 * phi_x * phi_y * phi_xy + phi_x * phi_x * phi_yy;
    if( abs(phi_x) + abs(phi_y) > 1e-9 )
      {
      kappa[k_] /= pow( (phi_x*phi_x + phi_y*phi_y), 1.5 );
      }
    }
}