private Matrix4d reorientHelix(int index) {
   Matrix4d matrix = new Matrix4d();
   matrix.setIdentity();
   matrix.setRotation(new AxisAngle4d(1, 0, 0, Math.PI / 2 * (index + 1)));
   matrix.mul(transformationMatrix);
   return matrix;
 }
  /*
   * Modifies the rotation part of the transformation axis for
   * a Cn symmetric complex, so that the narrower end faces the
   * viewer, and the wider end faces away from the viewer. Example: 3LSV
   */
  private void calcZDirection() {
    calcBoundaries();

    // if the longer part of the structure faces towards the back (-z direction),
    // rotate around y-axis so the longer part faces the viewer (+z direction)
    if (Math.abs(minBoundary.z) > Math.abs(maxBoundary.z)) {
      Matrix4d rot = flipY();
      rot.mul(transformationMatrix);
      transformationMatrix.set(rot);
    }
  }
  private static Matrix4d combineTransformation(
      Matrix4d matrix, Vector3d translation, Vector3d rotation) {
    Matrix4d gM = new Matrix4d(matrix);
    Matrix4d m = new Matrix4d();

    m.setIdentity();
    m.setTranslation(translation);
    gM.mul(m);

    m.setIdentity();
    m.rotZ(rotation.z);
    gM.mul(m);

    m.setIdentity();
    m.rotY(rotation.y);
    gM.mul(m);

    m.setIdentity();
    m.rotX(rotation.x);
    gM.mul(m);

    return gM;
  }
  private void calcTransformationBySymmetryAxes() {
    Vector3d[] axisVectors = new Vector3d[2];
    axisVectors[0] = new Vector3d(principalRotationVector);
    axisVectors[1] = new Vector3d(referenceVector);

    //  y,z axis centered at the centroid of the subunits
    Vector3d[] referenceVectors = new Vector3d[2];
    referenceVectors[0] = new Vector3d(Z_AXIS);
    referenceVectors[1] = new Vector3d(Y_AXIS);

    transformationMatrix = alignAxes(axisVectors, referenceVectors);

    // combine with translation
    Matrix4d combined = new Matrix4d();
    combined.setIdentity();
    Vector3d trans = new Vector3d(subunits.getCentroid());
    trans.negate();
    combined.setTranslation(trans);
    transformationMatrix.mul(combined);

    // for helical geometry, set a canonical view for the Z direction
    calcZDirection();
  }
  /**
   * Returns a transformation matrix that rotates refPoints to match coordPoints
   *
   * @param refPoints the points to be aligned
   * @param referenceVectors
   * @return
   */
  private Matrix4d alignAxes(Vector3d[] axisVectors, Vector3d[] referenceVectors) {
    Matrix4d m1 = new Matrix4d();
    AxisAngle4d a = new AxisAngle4d();
    Vector3d axis = new Vector3d();

    // calculate rotation matrix to rotate refPoints[0] into coordPoints[0]
    Vector3d v1 = new Vector3d(axisVectors[0]);
    Vector3d v2 = new Vector3d(referenceVectors[0]);
    double dot = v1.dot(v2);
    if (Math.abs(dot) < 0.999) {
      axis.cross(v1, v2);
      axis.normalize();
      a.set(axis, v1.angle(v2));
      m1.set(a);
      // make sure matrix element m33 is 1.0. It's 0 on Linux.
      m1.setElement(3, 3, 1.0);
    } else if (dot > 0) {
      // parallel axis, nothing to do -> identity matrix
      m1.setIdentity();
    } else if (dot < 0) {
      // anti-parallel axis, flip around x-axis
      m1.set(flipX());
    }

    // apply transformation matrix to all refPoints
    m1.transform(axisVectors[0]);
    m1.transform(axisVectors[1]);

    // calculate rotation matrix to rotate refPoints[1] into coordPoints[1]
    v1 = new Vector3d(axisVectors[1]);
    v2 = new Vector3d(referenceVectors[1]);
    Matrix4d m2 = new Matrix4d();
    dot = v1.dot(v2);
    if (Math.abs(dot) < 0.999) {
      axis.cross(v1, v2);
      axis.normalize();
      a.set(axis, v1.angle(v2));
      m2.set(a);
      // make sure matrix element m33 is 1.0. It's 0 on Linux.
      m2.setElement(3, 3, 1.0);
    } else if (dot > 0) {
      // parallel axis, nothing to do -> identity matrix
      m2.setIdentity();
    } else if (dot < 0) {
      // anti-parallel axis, flip around z-axis
      m2.set(flipZ());
    }

    // apply transformation matrix to all refPoints
    m2.transform(axisVectors[0]);
    m2.transform(axisVectors[1]);

    // combine the two rotation matrices
    m2.mul(m1);

    // the RMSD should be close to zero
    Point3d[] axes = new Point3d[2];
    axes[0] = new Point3d(axisVectors[0]);
    axes[1] = new Point3d(axisVectors[1]);
    Point3d[] ref = new Point3d[2];
    ref[0] = new Point3d(referenceVectors[0]);
    ref[1] = new Point3d(referenceVectors[1]);
    if (SuperPosition.rmsd(axes, ref) > 0.1) {
      System.out.println(
          "Warning: AxisTransformation: axes alignment is off. RMSD: "
              + SuperPosition.rmsd(axes, ref));
    }

    return m2;
  }
  /**
   * Computes the new transform for this interpolator for a given alpha value.
   *
   * @param alphaValue alpha value between 0.0 and 1.0
   * @param transform object that receives the computed transform for the specified alpha value
   * @since Java 3D 1.3
   */
  public void computeTransform(float alphaValue, Transform3D transform) {
    // compute the current value of u from alpha and the
    // determine lower and upper knot points
    computePathInterpolation(alphaValue);

    // Determine the segment within which we will be interpolating
    currentSegmentIndex = this.lowerKnot - 1;

    // if we are at the start of the curve
    if (currentSegmentIndex == 0 && currentU == 0f) {
      iHeading = keyFrames[1].heading;
      iPitch = keyFrames[1].pitch;
      iBank = keyFrames[1].bank;
      iPos.set(keyFrames[1].position);
      iScale.set(keyFrames[1].scale);

      // if we are at the end of the curve
    } else if (currentSegmentIndex == (numSegments - 1) && currentU == 1.0) {
      iHeading = keyFrames[upperKnot].heading;
      iPitch = keyFrames[upperKnot].pitch;
      iBank = keyFrames[upperKnot].bank;
      iPos.set(keyFrames[upperKnot].position);
      iScale.set(keyFrames[upperKnot].scale);

      // if we are somewhere in between the curve
    } else {
      // Get a reference to the current spline segment i.e. the
      // one bounded by lowerKnot and upperKnot
      currentSegment = cubicSplineCurve.getSegment(currentSegmentIndex);

      // interpolate quaternions
      iHeading = currentSegment.getInterpolatedHeading(currentU);
      iPitch = currentSegment.getInterpolatedPitch(currentU);
      iBank = currentSegment.getInterpolatedBank(currentU);

      // interpolate position
      currentSegment.getInterpolatedPositionVector(currentU, iPos);

      // interpolate position
      currentSegment.getInterpolatedScale(currentU, iScale);
      // System.out.println("Pos :" + iPos);
    }

    // Modification by ReubenDB

    if (colorRampingInterpolate == true) {
      float[] curPos = new float[3];
      iPos.get(curPos);

      myColorRamp.getColor(curPos[1], histColor);
      // System.out.println("SETING COLOR:" + histColor + " CurPos: " + curPos[0] + ", " + curPos[1]
      // + ", " + curPos[2]);
      objectCA.setColor(histColor);
      // System.out.println("CurrentAlpha = " + myAlpha.value());
    }

    if (timeDisplayInterpolate == true) {
      myTimeDisplay.updateDisplayFromAlpha(myAlpha.value());
      // System.out.println(myAlpha.value());
    }

    // Generate a transformation matrix in tMat using interpolated
    // heading, pitch and bank
    pitchMat.setIdentity();
    pitchMat.rotX(-iPitch);
    bankMat.setIdentity();
    bankMat.rotZ(iBank);
    tMat.setIdentity();
    tMat.rotY(-iHeading);
    tMat.mul(pitchMat);
    tMat.mul(bankMat);

    // TODO: Vijay - Handle Non-Uniform scale
    // Currently this interpolator does not handle non uniform scale
    // We cheat by just taking the x scale component

    // Scale the transformation matrix
    sMat.set((double) iScale.x);
    tMat.mul(sMat);

    // Set the translation components.
    tMat.m03 = iPos.x;
    tMat.m13 = iPos.y;
    tMat.m23 = iPos.z;
    rotation.set(tMat);

    // construct a Transform3D from:  axis * rotation * axisInverse
    transform.mul(axis, rotation);
    transform.mul(transform, axisInverse);
  }