private Matrix3d computeDesiredFootRotation(
      double angleToDestination, RobotSide swingLegSide, ReferenceFrame supportFootFrame) {
    RigidBodyTransform supportFootToWorldTransform =
        supportFootFrame.getTransformToDesiredFrame(ReferenceFrame.getWorldFrame());
    Matrix3d supportFootToWorldRotation = new Matrix3d();
    supportFootToWorldTransform.get(supportFootToWorldRotation);

    double maxTurnInAngle = 0.25;
    double maxTurnOutAngle = 0.4;

    double amountToYaw;
    switch (blindWalkingDirection.getEnumValue()) {
      case BACKWARD:
        {
          amountToYaw = AngleTools.computeAngleDifferenceMinusPiToPi(angleToDestination, Math.PI);
          break;
        }
      case LEFT:
        {
          amountToYaw =
              AngleTools.computeAngleDifferenceMinusPiToPi(angleToDestination, Math.PI / 2.0);
          break;
        }
      case RIGHT:
        {
          amountToYaw =
              AngleTools.computeAngleDifferenceMinusPiToPi(angleToDestination, -Math.PI / 2.0);
          break;
        }
      case FORWARD:
        {
          amountToYaw = angleToDestination;
          break;
        }
      default:
        {
          throw new RuntimeException("Shouldn't get here!");
        }
    }

    if (swingLegSide == RobotSide.LEFT) {
      amountToYaw = MathTools.clipToMinMax(amountToYaw, -maxTurnInAngle, maxTurnOutAngle);

    } else {
      amountToYaw = MathTools.clipToMinMax(amountToYaw, -maxTurnOutAngle, maxTurnInAngle);
    }

    Matrix3d yawRotation = new Matrix3d();
    yawRotation.rotZ(amountToYaw);

    Matrix3d ret = new Matrix3d();
    ret.mul(yawRotation, supportFootToWorldRotation);

    return ret;
  }
  private void computeProportionalTerm(FrameOrientation desiredOrientation) {
    desiredOrientation.changeFrame(bodyFrame);
    desiredOrientation.getQuaternion(errorQuaternion);
    errorAngleAxis.set(errorQuaternion);
    errorAngleAxis.setAngle(AngleTools.trimAngleMinusPiToPi(errorAngleAxis.getAngle()));

    // Limit the maximum position error considered for control action
    double maximumError = gains.getMaximumProportionalError();
    if (errorAngleAxis.getAngle() > maximumError) {
      errorAngleAxis.setAngle(Math.signum(errorAngleAxis.getAngle()) * maximumError);
    }

    proportionalTerm.set(errorAngleAxis.getX(), errorAngleAxis.getY(), errorAngleAxis.getZ());
    proportionalTerm.scale(errorAngleAxis.getAngle());
    rotationErrorInBody.set(proportionalTerm);

    proportionalGainMatrix.transform(proportionalTerm.getVector());
  }
  private FrameVector2d computeDesiredOffsetFromSupportAnkle(
      ReferenceFrame supportAnkleZUpFrame,
      RobotSide swingLegSide,
      double angleToDestination,
      double distanceToDestination) {
    if (distanceToDestination < DISTANCE_TO_DESTINATION_FOR_STEP_IN_PLACE) {
      return new FrameVector2d(
          supportAnkleZUpFrame,
          0.0,
          swingLegSide.negateIfRightSide(desiredStepWidth.getDoubleValue()));
    }

    double absoluteAngleToDestination;
    switch (blindWalkingDirection.getEnumValue()) {
      case BACKWARD:
        {
          absoluteAngleToDestination =
              Math.abs(AngleTools.computeAngleDifferenceMinusPiToPi(angleToDestination, Math.PI));
          break;
        }
      case LEFT:
        {
          absoluteAngleToDestination =
              Math.abs(
                  AngleTools.computeAngleDifferenceMinusPiToPi(angleToDestination, Math.PI / 2.0));
          break;
        }
      case RIGHT:
        {
          absoluteAngleToDestination =
              Math.abs(
                  AngleTools.computeAngleDifferenceMinusPiToPi(angleToDestination, -Math.PI / 2.0));
          break;
        }
      case FORWARD:
        {
          absoluteAngleToDestination = Math.abs(angleToDestination);
          break;
        }
      default:
        {
          throw new RuntimeException("Shouldn't get here!");
        }
    }

    double minAngleBeforeShorterSteps = 0.1;
    double maxAngleBeforeTurnInPlace = 0.5;

    double percentToStepInXY =
        1.0
            - (absoluteAngleToDestination - minAngleBeforeShorterSteps)
                / (maxAngleBeforeTurnInPlace - minAngleBeforeShorterSteps);

    if (percentToStepInXY > 1.0) percentToStepInXY = 1.0;
    if (percentToStepInXY < 0.0) percentToStepInXY = 0.0;

    switch (blindWalkingDirection.getEnumValue()) {
      case BACKWARD:
        {
          double backwardsDistanceReduction = 0.75;
          desiredOffsetFromSquaredUp.set(
              -backwardsDistanceReduction * percentToStepInXY * desiredStepForward.getDoubleValue(),
              0.0);
          break;
        }
      case LEFT:
        {
          desiredOffsetFromSquaredUp.set(
              0.0, percentToStepInXY * desiredStepSideward.getDoubleValue());
          break;
        }
      case RIGHT:
        {
          desiredOffsetFromSquaredUp.set(
              0.0, -percentToStepInXY * desiredStepSideward.getDoubleValue());
          break;
        }
      case FORWARD:
        {
          desiredOffsetFromSquaredUp.set(
              percentToStepInXY * desiredStepForward.getDoubleValue(), 0.0);
          break;
        }
      default:
        {
          throw new RuntimeException("Shouldn't get here!");
        }
    }

    double stepLength = desiredOffsetFromSquaredUp.length();

    double maxDistanceToAllow =
        distanceToDestination - 0.5 * DISTANCE_TO_DESTINATION_FOR_STEP_IN_PLACE;
    if (maxDistanceToAllow < 0.0) maxDistanceToAllow = 0.0;

    if (stepLength > maxDistanceToAllow) {
      desiredOffsetFromSquaredUp.scale(maxDistanceToAllow / stepLength);
      stepLength = desiredOffsetFromSquaredUp.length();
    }

    if (stepLength > maxStepLength.getDoubleValue()) {
      desiredOffsetFromSquaredUp.scale(maxStepLength.getDoubleValue() / stepLength);
    }

    FrameVector2d desiredOffsetFromAnkle =
        new FrameVector2d(
            supportAnkleZUpFrame,
            desiredOffsetFromSquaredUp.getX(),
            desiredOffsetFromSquaredUp.getY()
                + swingLegSide.negateIfRightSide(desiredStepWidth.getDoubleValue()));

    if (swingLegSide == RobotSide.LEFT) {
      desiredOffsetFromAnkle.setY(
          MathTools.clipToMinMax(
              desiredOffsetFromAnkle.getY(),
              minStepWidth.getDoubleValue(),
              maxStepWidth.getDoubleValue()));
    } else {
      desiredOffsetFromAnkle.setY(
          MathTools.clipToMinMax(
              desiredOffsetFromAnkle.getY(),
              -maxStepWidth.getDoubleValue(),
              -minStepWidth.getDoubleValue()));
    }

    return desiredOffsetFromAnkle;
  }