private double[] compute2DCoordinates(double[] point3D, SimpleMatrix pMatrix) {

    // Compute coordinates in projection data.
    SimpleVector homogeneousPoint =
        SimpleOperators.multiply(pMatrix, new SimpleVector(point3D[0], point3D[1], point3D[2], 1));
    // Transform to 2D coordinates
    double coordU = homogeneousPoint.getElement(0) / homogeneousPoint.getElement(2);
    double coordV = homogeneousPoint.getElement(1) / homogeneousPoint.getElement(2);

    // double pxlSize = config.getGeometry().getPixelDimensionX();

    return new double[] {coordU, coordV};
  }
  public void setData(int x, int y, int z, DarkFieldPCA myPCA) {

    SimpleMatrix eigenVectors = myPCA.getEigenVectors();
    SimpleVector eigenValues = myPCA.getEigenValues();

    for (int i = 0; i < fieldList.size(); i++) {
      SimpleVector eigenVec = eigenVectors.getCol(i);
      double eigenVal = eigenValues.getElement(i);
      eigenVec = eigenVec.multipliedBy(eigenVal);
      fieldList.get(i).setVector(x, y, z, eigenVec);
    }

    //		/**
    //		 * Threshold that checks, if 3 component of eigenvalues is too small
    //		 * If 3 component is too small, don't consider it as a fiber orientation
    //		 * and ignore it
    //		 */
    //		double th = 1E-10;

    //		SimpleVector fiberDir;
    //		if(myPCA.getEigenValues().getElement(2)<th){
    //			fiberDir = new SimpleVector(3);
    //		}else{
    //			fiberDir = myPCA.getEigenVectors().getCol(2).normalizedL2();
    //			fiberDir.multiplyBy(myPCA.getEigenValues().getElement(2));
    //		}

  }
 public static void printSimpleVector(SimpleVector B) {
   int n = B.getLen();
   for (int i = 0; i < n; i++) {
     System.out.print(B.getElement(i) + "\t");
   }
   System.out.print("\n");
 }
  private void followRay(
      SimpleVector pos,
      SimpleVector dir,
      double energyEV,
      FloatProcessor imp,
      int scatterCount,
      double totalDistance) {
    if (energyEV <= 1 || scatterCount > 20000) {
      System.out.println("energy low, times scattered: " + scatterCount);
      return;
    }
    // follow ray until next interaction point
    SimpleVector oldPos = pos.clone();
    double dist = sampler.getDistanceUntilNextInteractionCm(material, energyEV);
    pos.add(dir.multipliedBy(dist));
    pathlengths.add(dist);
    // draw the entire path
    // imp.drawLine((int)(scale*oldPos.getElement(0)), (int)(scale*oldPos.getElement(1)),
    // (int)(scale*pos.getElement(0)), (int)(scale*pos.getElement(1)));
    // draw interaction points only
    imp.drawDot((int) (scale * pos.getElement(0)), (int) (scale * pos.getElement(1)));

    // choose compton or photoelectric effect
    double photo =
        material.getAttenuation(energyEV / 1000, AttenuationType.PHOTOELECTRIC_ABSORPTION);
    double compton =
        material.getAttenuation(energyEV / 1000, AttenuationType.INCOHERENT_ATTENUATION);

    if (sampler.random() * (photo + compton) <= photo) {
      // photoelectric absorption
      energyEV = 0;
      // System.out.println("absorbed after " + scatterCount + " collisions");
      xs.add(pos.getElement(0));
      ys.add(pos.getElement(1));
      zs.add(pos.getElement(2));

      return;
    } else {
      // compton scattering

      energyEV = sampler.sampleComptonScattering(energyEV, dir);

      // send new ray
      followRay(pos, dir, energyEV, imp, scatterCount + 1, totalDistance + dist);
    }
  }
  /**
   * @param x
   * @param y
   * @param z
   * @param vec - EigenValue is encoded in length of vector
   */
  public void setVector(int x, int y, int z, SimpleVector vec) {

    // Check for inconsistency (different dimensions)
    assert (vec.getLen() == 3) : new Exception("Dimension of data vector has to be 3.");
    // Loop through every coordinate
    for (int i = 0; i < 3; i++) {
      super.setAtIndex(x, y, z, i, (float) vec.getElement(i));
    }
  }
    /**
     * Method to calculate alpha and t as indices from the radon transformed image of a view.
     * Neccessary are the inverse projection image (as SimpleMatrix) and the epipolar plane E
     *
     * @param E: epipolar plane E as SimpleVector (4 entries)
     * @return: line integral value as double
     */
    private double getValueByAlphaAndT(SimpleVector E) {

      // compute corresponding epipolar lines //
      // (epipolar lines are of 3x1 = 3x4 * 4x1)
      SimpleVector l_kappa = SimpleOperators.multiply(this.P_Inverse.transposed(), E);

      // init the coordinate shift //
      int t_u = this.projectionWidth / 2;
      int t_v = this.projectionHeight / 2;

      // compute angle alpha and distance to origin t //
      double l0 = l_kappa.getElement(0);
      double l1 = l_kappa.getElement(1);
      double l2 = l_kappa.getElement(2);

      double alpha_kappa_RAD = Math.atan2(-l0, l1) + Math.PI / 2;

      double t_kappa = -l2 / Math.sqrt(l0 * l0 + l1 * l1);
      // correct the coordinate shift //
      t_kappa -= t_u * Math.cos(alpha_kappa_RAD) + t_v * Math.sin(alpha_kappa_RAD);

      // correct some alpha falling out of the radon window //
      if (alpha_kappa_RAD < 0) {
        alpha_kappa_RAD *= -1.0;
      } else if (alpha_kappa_RAD > Math.PI) {
        alpha_kappa_RAD = 2.0 * Math.PI - alpha_kappa_RAD;
      }

      // write back to l_kappa //
      l_kappa.setElementValue(0, Math.cos(alpha_kappa_RAD));
      l_kappa.setElementValue(1, Math.sin(alpha_kappa_RAD));
      l_kappa.setElementValue(2, -t_kappa);

      // calculate x and y coordinates for derived radon transformed image //
      double x = t_kappa * this.lineIncrement + 0.5 * this.radonWidth;
      double y = alpha_kappa_RAD * this.angleIncrement;

      // get intensity value out of radon transformed image //
      // (interpolation needed)
      return InterpolationOperators.interpolateLinear(this.radon, x, y);
    }
  /**
   * Constructs the triangle corresponding to the i-th face in a mesh given the connectivity
   * information fcs and the vertices vtc and adds it to the CompoundShape.
   *
   * @param vtc The vertices of the mesh.
   * @param fcs The faces of the mesh, i.e connectivity information.
   * @param i The index of the face to be constructed.
   */
  private void addTriangleAtIndex(CompoundShape cs, SimpleMatrix vtc, SimpleMatrix fcs, int i) {
    SimpleVector face = fcs.getRow(i);

    SimpleVector dirU = vtc.getRow((int) face.getElement(1));
    dirU.subtract(vtc.getRow((int) face.getElement(0)));
    double l2 = dirU.normL2();
    SimpleVector dirV = vtc.getRow((int) face.getElement(2));
    dirV.subtract(vtc.getRow((int) face.getElement(0)));
    if (dirV.normL2() < l2) {
      l2 = dirV.normL2();
    }
    double nN = General.crossProduct(dirU.normalizedL2(), dirV.normalizedL2()).normL2();

    if (l2 < Math.sqrt(CONRAD.DOUBLE_EPSILON) || nN < Math.sqrt(CONRAD.DOUBLE_EPSILON)) {
    } else {
      Triangle t =
          new Triangle(
              new PointND(vtc.getRow((int) face.getElement(0))),
              new PointND(vtc.getRow((int) face.getElement(1))),
              new PointND(vtc.getRow((int) face.getElement(2))));
      cs.add(t);
    }
  }
  /** @param pathFiberVTK */
  public void writeToVectorField(String pathFiberVTK) {

    ArrayList<Index3D> indexListe = new ArrayList<Index3D>();

    for (int x = 0; x < imgSizeX; x++) {
      for (int y = 0; y < imgSizeY; y++) {
        for (int z = 0; z < imgSizeZ; z++) {

          // Get EigenVec of smallest eigenValue
          SimpleVector direction = fieldList.get(2).getSimpleVectorAtIndex(x, y, z);

          double length = direction.normL2();

          if (length != 0) {
            Index3D curIndex = new Index3D(x, y, z);
            indexListe.add(curIndex);
          }
        }
      }
    }

    try {
      FileOutputStream foStream = new FileOutputStream(pathFiberVTK);
      BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(foStream));

      bufWriter.write("# vtk DataFile Version 2.0");
      bufWriter.write(System.getProperty("line.separator"));
      bufWriter.write("Unstructured Grid Example");
      bufWriter.write(System.getProperty("line.separator"));
      bufWriter.write("ASCII");
      bufWriter.write(System.getProperty("line.separator"));
      bufWriter.write("DATASET UNSTRUCTURED_GRID");
      bufWriter.write(System.getProperty("line.separator"));

      // TODO Not sure why that
      bufWriter.write("POINTS " + indexListe.size() + " float");
      bufWriter.write(System.getProperty("line.separator"));

      // First write positions

      for (int poinIdx = 0; poinIdx < indexListe.size(); poinIdx++) {
        Index3D index3D = indexListe.get(poinIdx);
        bufWriter.write(index3D.x + " " + index3D.y + " " + index3D.z);
        bufWriter.write(System.getProperty("line.separator"));
      }

      bufWriter.write("CELLS " + indexListe.size() + " " + 2 * indexListe.size());
      bufWriter.write(System.getProperty("line.separator"));

      int indCells = 0;

      for (int poinIdx = 0; poinIdx < indexListe.size(); poinIdx++) {
        bufWriter.write("1 " + indCells);
        bufWriter.write(System.getProperty("line.separator"));
        indCells = indCells + 1;
      }

      bufWriter.write("CELL_TYPES " + indexListe.size());
      bufWriter.write(System.getProperty("line.separator"));

      for (int pointIdx = 0; pointIdx < indexListe.size(); pointIdx++) {
        bufWriter.write("1");
        bufWriter.write(System.getProperty("line.separator"));
      }

      bufWriter.write("POINT_DATA  " + indexListe.size());
      bufWriter.write(System.getProperty("line.separator"));
      bufWriter.write("SCALARS eigenValues float 3");
      bufWriter.write(System.getProperty("line.separator"));
      bufWriter.write("LOOKUP_TABLE default");
      bufWriter.write(System.getProperty("line.separator"));

      // Write scalars
      for (int pointIdx = 0; pointIdx < indexListe.size(); pointIdx++) {

        Index3D coordIdx = indexListe.get(pointIdx);
        SimpleVector vec1 =
            fieldList.get(0).getSimpleVectorAtIndex(coordIdx.x, coordIdx.y, coordIdx.z);
        SimpleVector vec2 =
            fieldList.get(1).getSimpleVectorAtIndex(coordIdx.x, coordIdx.y, coordIdx.z);
        SimpleVector vec3 =
            fieldList.get(2).getSimpleVectorAtIndex(coordIdx.x, coordIdx.y, coordIdx.z);

        bufWriter.write(vec1.normL2() + " " + vec2.normL2() + " " + vec3.normL2());
        bufWriter.write(System.getProperty("line.separator"));
      }

      bufWriter.write("SCALARS scalarDirection float 1");
      bufWriter.write(System.getProperty("line.separator"));
      bufWriter.write("LOOKUP_TABLE default");
      bufWriter.write(System.getProperty("line.separator"));

      // Write scalars
      for (int pointIdx = 0; pointIdx < indexListe.size(); pointIdx++) {

        Index3D coordIdx = indexListe.get(pointIdx);
        SimpleVector curEigVec =
            fieldList.get(2).getSimpleVectorAtIndex(coordIdx.x, coordIdx.y, coordIdx.z);

        curEigVec = curEigVec.absoluted();

        if (curEigVec.max() == curEigVec.getElement(0)) {
          bufWriter.write("0");
        } else if (curEigVec.max() == curEigVec.getElement(1)) {
          bufWriter.write("0.5");
        } else {
          bufWriter.write("1");
        }

        bufWriter.write(System.getProperty("line.separator"));
      }

      for (int eigIdx = 0; eigIdx < 3; eigIdx++) {

        bufWriter.write("VECTORS eigenVector" + eigIdx + " float");

        // Write scalars
        for (int pointIdx = 0; pointIdx < indexListe.size(); pointIdx++) {

          Index3D coordIdx = indexListe.get(pointIdx);
          SimpleVector curEigVec =
              fieldList.get(eigIdx).getSimpleVectorAtIndex(coordIdx.x, coordIdx.y, coordIdx.z);
          bufWriter.write(System.getProperty("line.separator"));
          bufWriter.write(
              curEigVec.getElement(0)
                  + " "
                  + curEigVec.getElement(1)
                  + " "
                  + ""
                  + curEigVec.getElement(2));
        }
        bufWriter.write(System.getProperty("line.separator"));
      }

      bufWriter.flush();
      foStream.close();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  public Grid2D raytraceScene(PrioritizableScene phantomScene, Projection projection) {
    Trajectory geom = Configuration.getGlobalConfiguration().getGeometry();
    // Grid2D slice = new Grid2D(geom.getDetectorWidth(), geom.getDetectorHeight());
    Grid2D slice =
        detector.createDetectorGrid(geom.getDetectorWidth(), geom.getDetectorHeight(), projection);
    rayTracer.setScene(phantomScene);
    // Second rule of optimization is: Optimize later.
    PointND raySource = new PointND(0, 0, 0);
    raySource.setCoordinates(projection.computeCameraCenter());
    StraightLine castLine = new StraightLine(raySource, new SimpleVector(0, 0, 0));

    SimpleVector centerPixDir = null;
    if (accurate) {
      centerPixDir = projection.computePrincipalAxis();
    }
    // SimpleVector prinpoint = trajectory.getProjectionMatrix(sliceNumber).getPrincipalPoint();

    double xcorr = 0; // trajectory.getDetectorWidth()/2 - prinpoint.getElement(0);
    double ycorr = 0; // trajectory.getDetectorHeight()/2 - prinpoint.getElement(1);

    double length = trajectory.getSourceToDetectorDistance();
    Edge environmentEdge = new Edge(new PointND(0), new PointND(length));

    ArrayList<PhysicalObject> fallBackBackground = new ArrayList<PhysicalObject>(1);
    SimpleVector pixel = new SimpleVector(0, 0);
    boolean negate = false;
    for (int y = 0; y < trajectory.getDetectorHeight(); y++) {
      for (int x = 0; x < trajectory.getDetectorWidth(); x++) {
        pixel.setElementValue(0, x - xcorr);
        pixel.setElementValue(1, y - ycorr);
        SimpleVector dir = projection.computeRayDirection(pixel);
        if ((y == 0) && (x == 0)) {
          // Check that ray direction is towards origin.
          double max = 0;
          int index = 0;
          for (int i = 0; i < 3; i++) {
            if (Math.abs(dir.getElement(i)) > max) {
              max = Math.abs(dir.getElement(i));
              index = i;
            }
          }
          double t = -raySource.get(index) / dir.getElement(index);
          if (t < 0) negate = true;
        }
        if (negate) dir.negate();
        castLine.setDirection(dir);

        ArrayList<PhysicalObject> segments = rayTracer.castRay(castLine);
        if (accurate) {
          double dirCosine = SimpleOperators.multiplyInnerProd(centerPixDir, dir);
          length = trajectory.getSourceToDetectorDistance() / dirCosine;
        }

        if (segments == null) {
          fallBackBackground.clear();
          segments = fallBackBackground;
        } else {
          if (accurate) {
            environmentEdge =
                new Edge(new PointND(0), new PointND(length - getTotalSegmentsLength(segments)));
          }
        }
        environment.setShape(environmentEdge);
        segments.add(environment);
        /* old code:
        double integral = absorptionModel.evaluateLineIntegral(segments);

        slice.putPixelValue(x, y, integral);
         */
        detector.writeToDetector(slice, x, y, segments);
      }
    }
    return slice;
  }
  @Override
  public Grid2D applyToolToImage(Grid2D imageProcessor) {
    FloatProcessor imp = new FloatProcessor(imageProcessor.getWidth(), imageProcessor.getHeight());
    imp.setPixels(imageProcessor.getBuffer());

    if (!initBead) initializeBead();
    ImageProcessor imp1 = imp.duplicate(); // original

    double[][] beadMean3D = config.getBeadMeanPosition3D(); // [beadNo][x,y,z]
    double[] uv = new double[1];

    SimpleMatrix pMatrix = config.getGeometry().getProjectionMatrix(imageIndex).computeP();
    // [projection #][bead #][u, v, state[0: initial, 1: registered, 2: updated by hough searching]]
    double[][][] beadPosition2D = config.getBeadPosition2D();

    int noBeadRegistered = 0;

    double[][] xy1 = new double[WeightBearingBeadPositionBuilder.beadNo][2]; // original
    double[][] xy2 =
        new double[WeightBearingBeadPositionBuilder.beadNo]
            [2]; // warped	(mapped to the mean), control points, reference

    double[][] xy1_hat = new double[WeightBearingBeadPositionBuilder.beadNo][2]; // original
    double[][] xy2_hat = new double[WeightBearingBeadPositionBuilder.beadNo][2]; // original

    // double distanceReferenceToCurrentBead = 0;

    for (int i = WeightBearingBeadPositionBuilder.currentBeadNo; i >= 0; i--) {

      if (beadMean3D[i][0] != 0
          || beadMean3D[i][1] != 0
          || beadMean3D[i][2] != 0) { // assume bead 3d is registered.

        uv = compute2DCoordinates(beadMean3D[i], pMatrix);

        // find bead location if registered by txt: state 1
        if (beadPosition2D[imageIndex][i][2] == 1) {

          noBeadRegistered++;

          if (isDisplay) {
            imp1.setValue(2);
            imp1.drawLine(
                (int) Math.round(beadPosition2D[imageIndex][i][0] - 10),
                (int) Math.round(beadPosition2D[imageIndex][i][1] - 10),
                (int) Math.round(beadPosition2D[imageIndex][i][0] + 10),
                (int) Math.round(beadPosition2D[imageIndex][i][1] + 10));
            imp1.drawLine(
                (int) Math.round(beadPosition2D[imageIndex][i][0] - 10),
                (int) Math.round(beadPosition2D[imageIndex][i][1] + 10),
                (int) Math.round(beadPosition2D[imageIndex][i][0] + 10),
                (int) Math.round(beadPosition2D[imageIndex][i][1] - 10));
            imp1.drawString(
                "Bead " + i + " (state:" + (int) beadPosition2D[imageIndex][i][2] + ")",
                (int) beadPosition2D[imageIndex][i][0],
                (int) beadPosition2D[imageIndex][i][1] - 10);
          }

          xy1[noBeadRegistered - 1][0] = beadPosition2D[imageIndex][i][0];
          xy1[noBeadRegistered - 1][1] = beadPosition2D[imageIndex][i][1];

          xy2[noBeadRegistered - 1][0] = uv[0];
          xy2[noBeadRegistered - 1][1] = uv[1];

        } else if (imageIndex != 0
            && imageIndex != config.getGeometry().getNumProjectionMatrices() - 1) {

          if (beadPosition2D[imageIndex - 1][i][2] == 1
              && beadPosition2D[imageIndex + 1][i][2] == 1) {

            noBeadRegistered++;

            double xMean =
                (beadPosition2D[imageIndex - 1][i][0] + beadPosition2D[imageIndex - 1][i][0]) / 2;
            double yMean =
                (beadPosition2D[imageIndex + 1][i][1] + beadPosition2D[imageIndex + 1][i][1]) / 2;

            if (isDisplay) {
              imp1.setValue(2);
              imp1.drawLine(
                  (int) Math.round(xMean - 10),
                  (int) Math.round(yMean - 10),
                  (int) Math.round(xMean + 10),
                  (int) Math.round(yMean + 10));
              imp1.drawLine(
                  (int) Math.round(xMean - 10),
                  (int) Math.round(yMean + 10),
                  (int) Math.round(xMean + 10),
                  (int) Math.round(yMean - 10));
              imp1.drawString("Bead " + i + " (state:" + "M)", (int) xMean, (int) yMean - 10);
            }

            xy1[noBeadRegistered - 1][0] = xMean;
            xy1[noBeadRegistered - 1][1] = yMean;

            xy2[noBeadRegistered - 1][0] = uv[0];
            xy2[noBeadRegistered - 1][1] = uv[1];
          }
        }

        // mean projected bead
        //				imp1.drawLine((int) Math.round(uv[0]-10), (int) Math.round(uv[1]), (int)
        // Math.round(uv[0]+10), (int) Math.round(uv[1]));
        //				imp1.drawLine((int) Math.round(uv[0]), (int) Math.round(uv[1]-10), (int)
        // Math.round(uv[0]), (int) Math.round(uv[1]+10));
      }
    }

    if (isDisplay) {
      for (int x = 0; x < config.getGeometry().getDetectorWidth(); x += 50)
        imp1.drawLine(x, 0, x, config.getGeometry().getDetectorHeight());
      for (int y = 0; y < config.getGeometry().getDetectorHeight(); y += 50)
        imp1.drawLine(0, y, config.getGeometry().getDetectorWidth(), y);
    }

    if (isCornerIncluded) {
      xy1[noBeadRegistered + 0][0] = 0;
      xy1[noBeadRegistered + 0][1] = 0;
      xy2[noBeadRegistered + 0][0] = 0;
      xy2[noBeadRegistered + 0][1] = 0;

      xy1[noBeadRegistered + 1][0] = 0;
      xy1[noBeadRegistered + 1][1] = config.getGeometry().getDetectorHeight();
      xy2[noBeadRegistered + 1][0] = 0;
      xy2[noBeadRegistered + 1][1] = config.getGeometry().getDetectorHeight();

      xy1[noBeadRegistered + 2][0] = config.getGeometry().getDetectorWidth();
      xy1[noBeadRegistered + 2][1] = 0;
      xy2[noBeadRegistered + 2][0] = config.getGeometry().getDetectorWidth();
      xy2[noBeadRegistered + 2][1] = 0;

      xy1[noBeadRegistered + 3][0] = config.getGeometry().getDetectorWidth();
      xy1[noBeadRegistered + 3][1] = config.getGeometry().getDetectorHeight();
      xy2[noBeadRegistered + 3][0] = config.getGeometry().getDetectorWidth();
      xy2[noBeadRegistered + 3][1] = config.getGeometry().getDetectorHeight();

      noBeadRegistered = noBeadRegistered + 4;
    }

    boolean fScaling = true;

    double minX = Double.MAX_VALUE;
    double maxX = 0;
    double minY = Double.MAX_VALUE;
    double maxY = 0;
    double c = 0;
    if (fScaling) { // ----- scaling to reduce condition # of A matrix
      for (int i = 0; i < noBeadRegistered; i++) {
        minX = Math.min(minX, xy1[i][0]);
        maxX = Math.max(maxX, xy1[i][0]);
        minY = Math.min(minY, xy1[i][1]);
        maxY = Math.max(maxY, xy1[i][1]);
      }
      c = Math.max(maxX - minX, maxY - minY);

      for (int i = 0; i < noBeadRegistered; i++) {
        xy1_hat[i][0] = (xy1[i][0] - minX) / c;
        xy1_hat[i][1] = (xy1[i][1] - minY) / c;

        xy2_hat[i][0] = (xy2[i][0] - minX) / c;
        xy2_hat[i][1] = (xy2[i][1] - minY) / c;
      }
    } else {
      xy1_hat = xy1;
      xy2_hat = xy2;
    }

    ImageProcessor imp2 = imp1.duplicate(); // warped

    /*
     * A*x = b
     * Matrix A = (n + 3) * (n + 3);
     * n (noBeadRegistered + 4): # of control points + 4 corner points (assume corner points are static)
     */

    int n = noBeadRegistered + 3;

    SimpleMatrix A = new SimpleMatrix(n, n);
    SimpleVector x_x = new SimpleVector(n);
    SimpleVector x_y = new SimpleVector(n);
    SimpleVector b_x = new SimpleVector(n);
    SimpleVector b_y = new SimpleVector(n);

    double rij = 0;
    double valA = 0;
    double valb_x = 0;
    double valb_y = 0;

    // Matrix L formation
    // alpha: mean of distances between control points' xy-projections) is a constant only present
    // on the diagonal of K
    // lambda: TPS smoothing regularization coefficient

    double alpha = 0.0;
    double lambda = 1.6; // 1.6
    for (int i = 0; i < noBeadRegistered; i++) { // i= # of row
      for (int j = i; j < noBeadRegistered; j++) { // j= # of column
        alpha +=
            Math.sqrt(
                Math.pow(xy2_hat[i][0] - xy2_hat[j][0], 2)
                    + Math.pow(xy2_hat[i][1] - xy2_hat[j][1], 2));
      }
    }
    alpha = alpha / Math.pow(noBeadRegistered, 2);

    for (int i = 0; i < n; i++) { // i= # of row
      for (int j = i; j < n; j++) { // j= # of column
        if (i < 3 && j < 3) valA = 0;
        else if (i >= 3 && j >= 3 && i == j) {
          valA = Math.pow(alpha, 2) * lambda;
          // valA = lambda;
          if (imageIndex < 10)
            System.out.println("Regularization = " + valA + ", lambda= " + lambda);
        } else if (i == 0 && j >= 0) valA = 1;
        else if (i == 1 && j >= 3) valA = xy1_hat[j - 3][0];
        else if (i == 2 && j >= 3) valA = xy1_hat[j - 3][1];
        else {
          rij =
              Math.pow(xy1_hat[j - 3][0] - xy1_hat[i - 3][0], 2)
                  + Math.pow(xy1_hat[j - 3][1] - xy1_hat[i - 3][1], 2);
          if (rij == 0) valA = 0;
          else valA = rij * Math.log(rij);
        }

        A.setElementValue(i, j, valA);
        A.setElementValue(j, i, valA);
      }

      if (i < 3) {
        valb_x = 0;
        valb_y = 0;
      } else {
        //				valb_x = xy2_hat[i-3][0]-xy1_hat[i-3][0];
        //				valb_y = xy2_hat[i-3][1]-xy1_hat[i-3][1];
        valb_x = xy2[i - 3][0] - xy1[i - 3][0];
        valb_y = xy2[i - 3][1] - xy1[i - 3][1];
        //				if (imageIndex > 150 && imageIndex < 170)
        //					System.out.println("Idx" + imageIndex + ",Elevation" + (i-3) + ": " + valb_x + "---"
        // + valb_y);
      }

      b_x.setElementValue(i, valb_x);
      b_y.setElementValue(i, valb_y);
    }

    // System.out.println("A condition number=" + A.conditionNumber(MatrixNormType.MAT_NORM_L1));
    // System.out.println("A condition number=" + A.conditionNumber(MatrixNormType.MAT_NORM_LINF));

    x_x = Solvers.solveLinearSysytemOfEquations(A, b_x);
    x_y = Solvers.solveLinearSysytemOfEquations(A, b_y);

    if (fScaling) {
      // ----- pixel space coefficients a, b scaling back
      double tmpA0 =
          x_x.getElement(0) - x_x.getElement(1) * (minX / c) - x_x.getElement(2) * (minY / c);
      for (int j = 0; j < noBeadRegistered; j++) {
        tmpA0 -=
            Math.log(c)
                * 2
                * x_x.getElement(j + 3)
                * (Math.pow(xy1_hat[j][0], 2) + Math.pow(xy1_hat[j][1], 2));
      }
      x_x.setElementValue(0, tmpA0);
      tmpA0 = x_y.getElement(0) - x_y.getElement(1) * (minX / c) - x_y.getElement(2) * (minY / c);
      for (int j = 0; j < noBeadRegistered; j++) {
        tmpA0 -=
            Math.log(c)
                * 2
                * x_y.getElement(j + 3)
                * (Math.pow(xy1_hat[j][0], 2) + Math.pow(xy1_hat[j][1], 2));
      }
      x_y.setElementValue(0, tmpA0);

      x_x.setElementValue(1, x_x.getElement(1) / c);
      x_y.setElementValue(1, x_y.getElement(1) / c);
      x_x.setElementValue(2, x_x.getElement(2) / c);
      x_y.setElementValue(2, x_y.getElement(2) / c);

      for (int i = 3; i < n; i++) {
        x_x.setElementValue(i, x_x.getElement(i) / Math.pow(c, 2));
        x_y.setElementValue(i, x_y.getElement(i) / Math.pow(c, 2));
      }
      // ----- pixel space coefficients a, b scaling back end
    }

    double devU = 0;
    double devV = 0;
    // Do warping
    // if (imageIndex == 0) {
    for (int y = 0; y < config.getGeometry().getDetectorHeight(); y++) {
      // for (int y=252; y<253; y++) {
      for (int x = 0; x < config.getGeometry().getDetectorWidth(); x++) {
        // for (int x=606; x<607; x++) {
        devU = x_x.getElement(0) + x_x.getElement(1) * x + x_x.getElement(2) * y;
        devV = x_y.getElement(0) + x_y.getElement(1) * x + x_y.getElement(2) * y;
        for (int i = 0; i < noBeadRegistered; i++) {
          rij = Math.pow(xy1[i][0] - x, 2) + Math.pow(xy1[i][1] - y, 2);
          if (rij > 0) {
            devU += x_x.getElement(i + 3) * rij * Math.log(rij);
            devV += x_y.getElement(i + 3) * rij * Math.log(rij);
          }
        }

        //					devU = 0;
        //					devV = 0;

        imp2.setf(x, y, (float) imp1.getInterpolatedValue(x - devU, y - devV));

        // System.out.println("x, y=" + x + ", " + y + "\t" + devU + ", " + devV);
        // maxDevU = Math.max(maxDevU, devU);
        // maxDevV = Math.max(maxDevV, devV);
      }
    }

    // Error estimate after transformation
    //			for (int i=0; i<= WeightBearingBeadPositionBuilder.currentBeadNo; i++){
    //
    //				if (beadMean3D[i][0] != 0 || beadMean3D[i][1] != 0 || beadMean3D[i][2] != 0){ // assume
    // bead 3d is registered.
    //
    //					// find bead location if registered by txt: state 1
    //					if (beadPosition2D[imageIndex][i][2] == 1){
    //
    //						// Projected Reference
    //						uv = compute2DCoordinates(beadMean3D[i], pMatrix);
    //						double x = uv[0];
    //						double y = uv[1];
    //						// bead detected position in 2d
    //						// Transform to 2D coordinates, time variant position
    //						//beadPosition2D[imageIndex][i][0];
    //						//beadPosition2D[imageIndex][i][1];
    //
    //						devU = x_x.getElement(0) + x_x.getElement(1)*x + x_x.getElement(2)*y;
    //						devV = x_y.getElement(0) + x_y.getElement(1)*x + x_y.getElement(2)*y;
    //						for (int j=0; j<noBeadRegistered; j++){
    //							rij = Math.pow(xy1[j][0]-x, 2) + Math.pow(xy1[j][1]-y, 2);
    //							if (rij > 0) {
    //								devU += x_x.getElement(j+3)*rij*Math.log(rij);
    //								devV += x_y.getElement(j+3)*rij*Math.log(rij);
    //							}
    //						}
    //
    //						distanceReferenceToCurrentBead +=
    // Math.sqrt(Math.pow(uv[0]-(beadPosition2D[imageIndex][i][0]+devU),
    // 2)+Math.pow(uv[1]-(beadPosition2D[imageIndex][i][1]+devV), 2));
    //
    //					}
    //				}
    //			}
    //			System.out.println("Euclidean distance\t" + imageIndex + "\t" +
    // distanceReferenceToCurrentBead/noBeadRegistered);

    // }

    if (isDisplay) {
      for (int i = WeightBearingBeadPositionBuilder.currentBeadNo; i >= 0; i--) {

        if (beadMean3D[i][0] != 0
            || beadMean3D[i][1] != 0
            || beadMean3D[i][2] != 0) { // assume bead 3d is registered.

          uv = compute2DCoordinates(beadMean3D[i], pMatrix);

          imp2.setValue(2);
          // mean projected bead
          imp2.drawLine(
              (int) Math.round(uv[0] - 10),
              (int) Math.round(uv[1]),
              (int) Math.round(uv[0] + 10),
              (int) Math.round(uv[1]));
          imp2.drawLine(
              (int) Math.round(uv[0]),
              (int) Math.round(uv[1] - 10),
              (int) Math.round(uv[0]),
              (int) Math.round(uv[1] + 10));
        }
      }
    }
    Grid2D result = new Grid2D((float[]) imp2.getPixels(), imp2.getWidth(), imp2.getHeight());
    return result;
  }
  private void visualizeKleinNishina(double energyJoule) {
    int gridWidth = 600;
    int gridHeight = 500;
    double maxAngle = 360;

    Grid2D grid = new Grid2D(gridWidth, gridHeight);

    FloatProcessor fp = new FloatProcessor(grid.getWidth(), grid.getHeight());
    fp.setPixels(grid.getBuffer());

    // find max value
    double max = 0;
    for (int i = 0; i < maxAngle; ++i) {
      double value = XRayTracerSampling.comptonAngleProbability(energyJoule, Math.toRadians(i));

      if (value > max) {
        max = value;
      }
    }

    fp.drawLine((int) 0, grid.getHeight() / 2, grid.getWidth(), grid.getHeight() / 2);
    fp.drawLine(grid.getWidth() / 2, 0, grid.getWidth() / 2, grid.getHeight());
    fp.drawOval((grid.getWidth() - grid.getHeight()) / 2, 0, grid.getHeight(), grid.getHeight());
    double lastX = 0;
    double lastY = 0;

    // draw angle distribution
    for (int i = 0; i < maxAngle; ++i) {
      double value = XRayTracerSampling.comptonAngleProbability(energyJoule, Math.toRadians(i));

      SimpleMatrix m = Rotations.createBasicZRotationMatrix(Math.toRadians(i));
      SimpleVector v = new SimpleVector(((value / max) * ((double) grid.getHeight() / 2.d)), 0, 0);

      SimpleVector r = SimpleOperators.multiply(m, v);

      double x = grid.getWidth() / 2 + r.getElement(0);
      double y = grid.getHeight() / 2 + r.getElement(1);
      if (i > 0) fp.drawLine((int) lastX, (int) lastY, (int) x, (int) y);
      lastX = x;
      lastY = y;
    }

    grid.show(
        "Normalized Klein-Nishina cross-section as a function of scatter angle ("
            + energyJoule / eV
            + "eV)");

    grid = new Grid2D(gridWidth, gridHeight);

    fp = new FloatProcessor(grid.getWidth(), grid.getHeight());
    fp.setPixels(grid.getBuffer());

    // draw histogram with rejection sampling of the klein-nishina
    // distribution
    int[] angles = new int[grid.getWidth()];
    for (int i = 0; i < numSamples; ++i) {
      double angle = Math.toDegrees(sampler.getComptonAngleTheta(energyJoule));
      int pos = (int) (angle * grid.getWidth() / 360.d);
      angles[pos] += 1;
    }
    double max2 = 0;
    for (int i = 0; i < angles.length; ++i) {
      if (angles[i] > max2) {
        max2 = angles[i];
      }
    }
    for (int i = 0; i < angles.length; ++i) {
      double x = i;
      double y = ((angles[i]) * (grid.getHeight() / (max2)));
      fp.drawLine((int) x, (int) grid.getHeight(), (int) x, grid.getHeight() - (int) y);
    }

    // draw klein-nishina probability function
    lastX = 0;
    lastY = 0;
    for (int i = 0; i < maxAngle; ++i) {
      double value = XRayTracerSampling.comptonAngleProbability(energyJoule, Math.toRadians(i));
      double x = (i * (grid.getWidth() / 360.f));
      double y = grid.getHeight() - ((value) * (grid.getHeight() / (max)));

      fp.drawLine((int) lastX, (int) lastY, (int) x, (int) y);
      lastX = x;
      lastY = y;
    }

    grid.show(
        "Energy: " + energyJoule / eV + "eV; x-axis: angle[0-360]; y-axis: probability[0-max]");
  }
  /**
   * method to calculate a mapping K from two source positions C0, C1 to a plane C0 (C1) is the
   * source position from the first (second) view
   */
  public void createMappingToEpipolarPlane() {

    // set up source matrices //
    SimpleVector C0 = this.view1.C;
    SimpleVector C1 = this.view2.C;

    // compute Pluecker coordinates //
    double L01 = C0.getElement(0) * C1.getElement(1) - C0.getElement(1) * C1.getElement(0);
    double L02 = C0.getElement(0) * C1.getElement(2) - C0.getElement(2) * C1.getElement(0);
    double L03 = C0.getElement(0) * C1.getElement(3) - C0.getElement(3) * C1.getElement(0);
    double L12 = C0.getElement(1) * C1.getElement(2) - C0.getElement(2) * C1.getElement(1);
    double L13 = C0.getElement(1) * C1.getElement(3) - C0.getElement(3) * C1.getElement(1);
    double L23 = C0.getElement(2) * C1.getElement(3) - C0.getElement(3) * C1.getElement(2);

    // construct B (6x1) //
    SimpleVector B = new SimpleVector(L01, L02, L03, L12, L13, L23);

    // compute infinity point in direction of B //
    SimpleVector N = new SimpleVector(-L03, -L13, -L23, 0);

    // compute plane E0 containing B and X0=(0,0,0,1) //
    SimpleVector E0 = SimpleOperators.getPlueckerJoin(B, new SimpleVector(0, 0, 0, 1));

    // find othonormal basis from plane normals //
    // (vectors are of 3x1)
    SimpleVector a2 = new SimpleVector(E0.getElement(0), E0.getElement(1), E0.getElement(2));
    SimpleVector a3 = new SimpleVector(N.getElement(0), N.getElement(1), N.getElement(2));
    // set vectors to unit length
    a2.normalizeL2();
    a3.normalizeL2();

    // calculate cross product to get the last basis vector //
    SimpleVector a1 = General.crossProduct(a2, a3).negated();
    // (a1 is already of unit length -> no normalization needed)

    // set up assembly matrix A (4x3) //
    SimpleMatrix A = new SimpleMatrix(4, 3);
    A.setSubColValue(0, 0, a1);
    A.setSubColValue(0, 1, a2);
    A.setSubColValue(0, 2, C0);

    // store mapping matrix K (4x3 = 4x4 * 4x3) //
    this.K = SimpleOperators.multiplyMatrixProd(SimpleOperators.getPlueckerMatrixDual(B), A);
  }