/**
   * Update the camera position along the path based on the specified distance from the beginning.
   *
   * @param fNormalizedDist normalized distance from the beginning of the path for the location of
   *     the camera for viewing
   */
  private void setPathDist(float fNormalizedDist) {

    // Make sure the distance is in the [0,1] range.
    fNormalizedDist = clampNormalizedPathDistance(fNormalizedDist);

    // Compute the actual distance along the curve.
    float fDist = fNormalizedDist * getPathLength();

    // Save the current distance.
    m_kBranchState.m_fNormalizedPathDist = fNormalizedDist;

    // Get the path point (position and tangent) based on distance.
    // It needs to be double precision for the view to use.
    Curve3f kCurve = m_kBranchState.m_kBranchCurve;
    float fTime = kCurve.GetTime(fDist, 100, 1e-02f);
    Vector3f kViewPoint = kCurve.GetPosition(fTime);

    // If the gaze distance is zero, then use the tangent vector
    // to the curve.
    // If the path is being followed in the reverse direction,
    // then the direction of looking down the path needs to
    // be reversed (negated).
    Vector3f kLookatVector = new Vector3f();
    boolean bLookatVectorUseTangent = true;

    if (m_fGazeDist > 0.0f) {
      float fTimeGazeDist = m_kBranchState.getForwardNormalizedTime(m_fGazeDist);

      if (fTime != fTimeGazeDist) {
        Vector3f kVec = kCurve.GetPosition(fTimeGazeDist);
        kLookatVector.Sub(kVec, kViewPoint);
        kLookatVector.Normalize();
        bLookatVectorUseTangent = false;
      }
    }

    if (bLookatVectorUseTangent) {
      kLookatVector = kCurve.GetTangent(fTime);

      if (!m_kBranchState.m_bMoveForward) {
        kLookatVector.Neg();
      }
    }

    // Update the view given the view position, view direction,
    // and a hint for the view up vector.
    setView(kViewPoint, kLookatVector);

    // Notify listener that we are updated.
    notifyCallback(EVENT_CHANGE_POSITION);
  }
  /**
   * Set the camera to the specified be located at the specified view point and looking in the
   * specified direction.
   *
   * @param kViewPoint coordinates of the camera view point
   * @param kViewdirVector coordinates of the camera view direction vector. This vector must be
   *     normalized.
   */
  private void setView(Vector3f kViewPoint, Vector3f kViewdirVector) {
    // Use the view direction vector to create positive weights where more
    // weight is given to an axis that has less of a component in the
    // direction vector.  Use the weights to create an average of
    // two desired (orthogonal axis) up vectors.  Normalize this average
    // vector to create a combined view up vector to use.
    Vector3f kV = new Vector3f(kViewdirVector);

    kV.Set(Math.abs(kV.X), Math.abs(kV.Y), Math.abs(kV.Z));
    kV.Sub(Vector3f.ONE, kV);

    Vector3f kViewupVector = new Vector3f(0.0f, 0.0f, 0.0f);

    kViewupVector.ScaleAdd(m_kViewup1.Dot(kV), m_kViewup1, kViewupVector);
    kViewupVector.ScaleAdd(m_kViewup2.Dot(kV), m_kViewup2, kViewupVector);
    kViewupVector.Normalize();

    // Project the view-up vector onto the plane which is
    // perpendicular to the view direction vector.  By getting to
    // this point, we know that the view-up vector and the view
    // direction vectors are not aligned.  This projected vector is
    // normalized and becomes the new view-up vector.
    Vector3f kViewdirProjection = new Vector3f();

    kViewdirProjection.Scale(kViewdirVector.Dot(kViewupVector), kViewdirVector);
    kViewupVector.Sub(kViewdirProjection);
    kViewupVector.Normalize();

    Vector3f kViewleftVector = new Vector3f();

    kViewleftVector.Cross(kViewupVector, kViewdirVector);

    m_kViewPoint.Copy(kViewPoint);
    m_kViewDirection.Copy(kViewdirVector);
    m_kViewUp.Copy(kViewupVector);
  }
  /**
   * Loop through all of the possible branch choices and select the one that is the closest to the
   * current view direction vector. Take a vector from the current view point to a point along each
   * choice branch. Compute the dot-product between the current view direction vector and each of
   * these branch direction vectors and take the one with the largest positive value, i.e., most in
   * alignment.
   */
  private void setClosestChoiceBranch() {
    // Retrieve the current combined viewing direction vector.
    // Note that the sign of the view direction vector is negated
    // for the reasons described in the setView method.
    // Matrix4f kMatrixView = parentScene.getWVMatrix();
    // Vector3f kViewDirection = new Vector3f(-kMatrixView.M02, -kMatrixView.M12, -kMatrixView.M22);
    Vector3f kViewDirection = new Vector3f(parentScene.getCameraDirection());

    // Record the current view position and combined view orientation.
    // Vector3f kP0 = new Vector3f(kMatrixView.M03, kMatrixView.M13, kMatrixView.M23);
    Vector3f kP0 = new Vector3f(getViewPoint());

    // Check point down path which is maximum of the step distance
    // and the gaze distance.
    float fPointDist = Math.max(m_fGazeDist, m_fPathStep);
    float fBestAlign = -1.0f;
    int iBestAlignBranchChoiceIndex = -1;

    for (int iBranch = 0; iBranch < m_akBranchChoice.length; iBranch++) {

      // Get vector from current view point to point down branch path.
      BranchState kBranch = m_akBranchChoice[iBranch];
      Vector3f kV = new Vector3f();

      kV.Sub(kBranch.getForwardNormalizedPosition(fPointDist), kP0);
      kV.Normalize();

      // Only accept the best aligned branches we can supposedly see.
      float fAlign = kV.Dot(kViewDirection);

      if ((fAlign > 0.0f) && (fAlign > fBestAlign)) {
        fBestAlign = fAlign;
        iBestAlignBranchChoiceIndex = iBranch;
      }
    }

    // Select the "nearest" branch.
    if (iBestAlignBranchChoiceIndex >= 0) {

      // Select the new branch.
      m_iBranchChoiceIndex = iBestAlignBranchChoiceIndex;
      m_bChooseBranch = false;
      setBranch(m_akBranchChoice[m_iBranchChoiceIndex]);
    } else {
      beep();
    }
  }