/**
   * Return a midpoint of a helix, calculated from three positions of three adjacent subunit
   * centers.
   *
   * @param p1 center of first subunit
   * @param p2 center of second subunit
   * @param p3 center of third subunit
   * @return midpoint of helix
   */
  private Point3d getMidPoint(Point3d p1, Point3d p2, Point3d p3) {
    Vector3d v1 = new Vector3d();
    v1.sub(p1, p2);
    Vector3d v2 = new Vector3d();
    v2.sub(p3, p2);
    Vector3d v3 = new Vector3d();
    v3.add(v1);
    v3.add(v2);
    v3.normalize();

    // calculat the total distance between to subunits
    double dTotal = v1.length();
    // calculate the rise along the y-axis. The helix axis is aligned with y-axis,
    // therfore, the rise between subunits is the y-distance
    double rise = p2.y - p1.y;
    // use phythagorean theoremm to calculate chord length between two subunit centers
    double chord = Math.sqrt(dTotal * dTotal - rise * rise);
    //		System.out.println("Chord d: " + dTotal + " rise: " + rise + "chord: " + chord);
    double angle = helixLayers.getByLargestContacts().getAxisAngle().angle;

    // using the axis angle and the chord length, we can calculate the radius of the helix
    // http://en.wikipedia.org/wiki/Chord_%28geometry%29
    double radius = chord / Math.sin(angle / 2) / 2; // can this go to zero?
    //		System.out.println("Radius: " + radius);

    // project the radius onto the vector that points toward the helix axis
    v3.scale(radius);
    v3.add(p2);
    //		System.out.println("Angle: " +
    // Math.toDegrees(helixLayers.getByLowestAngle().getAxisAngle().angle));
    Point3d cor = new Point3d(v3);
    return cor;
  }
 public HelixAxisAligner(QuatSymmetryResults results) {
   this.subunits = results.getSubunits();
   this.helixLayers = results.getHelixLayers();
   if (subunits == null) {
     throw new IllegalArgumentException("HelixAxisTransformation: Subunits are null");
   } else if (helixLayers == null) {
     throw new IllegalArgumentException("HelixAxisTransformation: HelixLayers is null");
   } else if (subunits.getSubunitCount() == 0) {
     throw new IllegalArgumentException("HelixAxisTransformation: Subunits is empty");
   } else if (helixLayers.size() == 0) {
     throw new IllegalArgumentException("HelixAxisTransformation: HelixLayers is empty");
   }
 }
  private List<Integer> getLongestLayerLine() {
    int len = 0;
    int index = 0;

    Helix helix = helixLayers.getByLargestContacts();
    List<List<Integer>> layerLines = helix.getLayerLines();
    for (int i = 0; i < layerLines.size(); i++) {
      if (layerLines.get(i).size() > len) {
        len = layerLines.get(i).size();
        index = i;
      }
    }
    return layerLines.get(index);
  }
 /**
  * Returns a vector along the principal rotation axis for the alignment of structures along the
  * z-axis
  *
  * @return principal rotation vector
  */
 private void calcPrincipalRotationVector() {
   //		AxisAngle4d axisAngle = helixLayers.getByLowestAngle().getAxisAngle();
   AxisAngle4d axisAngle = helixLayers.getByLargestContacts().getAxisAngle();
   principalRotationVector = new Vector3d(axisAngle.x, axisAngle.y, axisAngle.z);
 }