/**
   * 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 void setTrajectory(
      int numProjectionMatrices,
      double sourceToAxisDistance,
      double averageAngularIncrement,
      double detectorOffsetX,
      double detectorOffsetY,
      CameraAxisDirection uDirection,
      CameraAxisDirection vDirection,
      SimpleVector rotationAxis,
      PointND rotationCenter,
      double angleFirstProjection) {
    this.projectionMatrices = new Projection[numProjectionMatrices];
    this.primaryAngles = new double[numProjectionMatrices];
    this.numProjectionMatrices = numProjectionMatrices;
    this.sourceToAxisDistance = sourceToAxisDistance;
    this.averageAngularIncrement = averageAngularIncrement;
    this.detectorOffsetU = detectorOffsetX;
    this.detectorOffsetV = detectorOffsetY;

    double cosPhi = Math.cos(General.toRadians(angleFirstProjection));
    double sinPhi = Math.sin(General.toRadians(angleFirstProjection));
    SimpleMatrix rotMat = new SimpleMatrix(3, 3);
    rotMat.setElementValue(0, 0, cosPhi);
    rotMat.setElementValue(0, 1, sinPhi);
    rotMat.setElementValue(1, 0, -sinPhi);
    rotMat.setElementValue(1, 1, cosPhi);
    rotMat.setElementValue(2, 2, 1);
    SimpleVector centerToCameraIdealAtInitialAngle =
        SimpleOperators.multiply(rotMat, new SimpleVector(sourceToAxisDistance, 0, 0));
    Plane3D trajPlane =
        new Plane3D(
            rotationAxis,
            SimpleOperators.multiplyInnerProd(rotationAxis, rotationCenter.getAbstractVector()));
    double distToPlane = trajPlane.computeDistance(new PointND(centerToCameraIdealAtInitialAngle));
    SimpleVector centerToCameraDir =
        SimpleOperators.subtract(
            SimpleOperators.add(
                rotationAxis.multipliedBy(-1 * distToPlane), centerToCameraIdealAtInitialAngle),
            rotationCenter.getAbstractVector());
    centerToCameraDir.divideBy(centerToCameraDir.normL2());
    SimpleVector centerToCameraInitialInPlane =
        centerToCameraDir.multipliedBy(sourceToAxisDistance);

    for (int i = 0; i < numProjectionMatrices; i++) {
      primaryAngles[i] = i * averageAngularIncrement + angleFirstProjection;
      // System.out.println(primaryAngles[i] + " " + averageAngularIncrement + " " +
      // this.reconDimensions[0] + " " + this.reconDimensions[1]);
      projectionMatrices[i] = new Projection();
      double rotationAngle = General.toRadians(primaryAngles[i]);
      projectionMatrices[i].setRtFromCircularTrajectory(
          rotationCenter.getAbstractVector(),
          rotationAxis,
          sourceToAxisDistance,
          centerToCameraInitialInPlane,
          uDirection,
          vDirection,
          rotationAngle);
      SimpleVector spacingUV = new SimpleVector(pixelDimensionX, pixelDimensionY);
      SimpleVector sizeUV = new SimpleVector(detectorWidth, detectorHeight);
      SimpleVector offset = new SimpleVector(detectorOffsetX, detectorOffsetY);
      projectionMatrices[i].setKFromDistancesSpacingsSizeOffset(
          sourceToDetectorDistance, spacingUV, sizeUV, offset, 1.0, 0);
    }
    this.projectionStackSize = numProjectionMatrices;
    // System.out.println("Defined geometry with SDD " +sourceToDetectorDistance);
  }