/**
   * Rotate the point xyz around the line passing through abc with direction uvw
   * http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.html Special case
   * where abc=0
   *
   * @param vec
   * @param axis
   * @param angle_radians in radians
   * @return
   */
  public static Vector3f rotateAroundAxis(Vector3f vec, Vector3f axis, double angle_radians) {
    float C = (float) Math.cos(angle_radians);
    float S = (float) Math.sin(angle_radians);
    float x = vec.x;
    float y = vec.y;
    float z = vec.z;
    float u = axis.x;
    float v = axis.y;
    float w = axis.z;

    // (a*( v*v + w*w) - u*(b*v + c*w - u*x - v*y - w*z))(1.0-C)+x*C+(-c*v + b*w - w*y + v*z)*S
    // (b*( u*u + w*w) - v*(a*v + c*w - u*x - v*y - w*z))(1.0-C)+y*C+( c*u - a*w + w*x - u*z)*S
    // (c*( u*u + v*v) - w*(a*v + b*v - u*x - v*y - w*z))(1.0-C)+z*C+(-b*u + a*v - v*x + u*y)*S
    // but a=b=c=0 so
    // x' = ( -u*(- u*x - v*y - w*z)) * (1.0-C) + x*C + ( - w*y + v*z)*S
    // y' = ( -v*(- u*x - v*y - w*z)) * (1.0-C) + y*C + ( + w*x - u*z)*S
    // z' = ( -w*(- u*x - v*y - w*z)) * (1.0-C) + z*C + ( - v*x + u*y)*S

    float a = (-u * x - v * y - w * z);

    return new Vector3f(
        (-u * a) * (1.0f - C) + x * C + (-w * y + v * z) * S,
        (-v * a) * (1.0f - C) + y * C + (w * x - u * z) * S,
        (-w * a) * (1.0f - C) + z * C + (-v * x + u * y) * S);
  }
  public void rotateFinger() {
    Vector3f forward = new Vector3f(HOME_FORWARD_X, HOME_FORWARD_Y, HOME_FORWARD_Z);
    Vector3f right = new Vector3f(HOME_RIGHT_X, HOME_RIGHT_Y, HOME_RIGHT_Z);
    Vector3f up = new Vector3f();

    up.cross(forward, right);

    Vector3f of = new Vector3f(forward);
    Vector3f or = new Vector3f(right);
    Vector3f ou = new Vector3f(up);

    Vector3f result;

    result =
        rotateAroundAxis(
            forward,
            of,
            Math.toRadians(motion_future.iku)); // TODO rotating around itself has no effect.
    result = rotateAroundAxis(result, or, Math.toRadians(motion_future.ikv));
    result = rotateAroundAxis(result, ou, Math.toRadians(motion_future.ikw));
    motion_future.finger_forward.set(result);

    result = rotateAroundAxis(right, of, Math.toRadians(motion_future.iku));
    result = rotateAroundAxis(result, or, Math.toRadians(motion_future.ikv));
    result = rotateAroundAxis(result, ou, Math.toRadians(motion_future.ikw));
    motion_future.finger_left.set(result);

    motion_future.finger_up.cross(motion_future.finger_forward, motion_future.finger_left);
  }
  public RotaryStewartPlatform2() {
    super();
    setDisplayName("Rotary Stewart Platform 2");

    /*
    // set up bounding volumes
    for(int i=0;i<volumes.length;++i) {
    	volumes[i] = new Cylinder();
    }
    volumes[0].radius=3.2f;
    volumes[1].radius=3.0f*0.575f;
    volumes[2].radius=2.2f;
    volumes[3].radius=1.15f;
    volumes[4].radius=1.2f;
    volumes[5].radius=1.0f*0.575f;*/

    motion_now.rotateBase(0, 0);
    motion_now.updateIKEndEffector();
    motion_now.rebuildShoulders();
    motion_now.updateIKWrists();

    motion_future.set(motion_now);
    setupModels();

    // find the starting height of the end effector at home position
    // @TODO: project wrist-on-bicep to get more accurate distance
    float aa = (motion_now.arms[0].elbow.y - motion_now.arms[0].wrist.y);
    float cc = FOREARM_LENGTH;
    float bb = (float) Math.sqrt((cc * cc) - (aa * aa));
    aa = motion_now.arms[0].elbow.x - motion_now.arms[0].wrist.x;
    cc = bb;
    bb = (float) Math.sqrt((cc * cc) - (aa * aa));
    motion_now.finger_tip.set(0, 0, bb + BASE_TO_SHOULDER_Z - WRIST_TO_FINGER_Z);

    motion_future.finger_tip.set(motion_now.finger_tip);
    moveIfAble();
  }