@Override
  public void applyStrafeAction(double s_elapsed) {
    switch (next_action.strafe) {
      case NONE:
        double strafe_velocity = Math.hypot(strafe_x_velocity, strafe_y_velocity);
        double delta = s_elapsed * SPEED_DECREASE_PER_SECOND * strafe_x_velocity / strafe_velocity;
        if (Math.abs(strafe_x_velocity) > Math.abs(delta)) {
          strafe_x_velocity -= delta;
        }
        else {
          strafe_x_velocity = 0.0;
        }
        delta = s_elapsed * SPEED_DECREASE_PER_SECOND * strafe_y_velocity / strafe_velocity;
        if (Math.abs(strafe_y_velocity) > Math.abs(delta)) {
          strafe_y_velocity -= delta;
        }
        else {
          strafe_y_velocity = 0.0;
        }
        break;
      case LEFT:
        Point2D.Double strafe_dxdy = MapUtils.perpendicularVector(1.0, direction);
        double max = move_speed * strafe_dxdy.x;
        strafe_x_velocity -= SPEED_INCREASE_PER_SECOND * strafe_dxdy.x * s_elapsed;
        if (Math.abs(strafe_x_velocity) > Math.abs(max)) {
          strafe_x_velocity = -max;
        }
        max = move_speed * strafe_dxdy.y;
        strafe_y_velocity -= SPEED_INCREASE_PER_SECOND * strafe_dxdy.y * s_elapsed;
        if (Math.abs(strafe_y_velocity) > Math.abs(max)) {
          strafe_y_velocity = -max;
        }
        break;
      case RIGHT:
        strafe_dxdy = MapUtils.perpendicularVector(1.0, direction);
        max = move_speed * strafe_dxdy.x;
        strafe_x_velocity += SPEED_INCREASE_PER_SECOND * strafe_dxdy.x * s_elapsed;
        if (Math.abs(strafe_x_velocity) > Math.abs(max)) {
          strafe_x_velocity = max;
        }
        max = move_speed * strafe_dxdy.y;
        strafe_y_velocity += SPEED_INCREASE_PER_SECOND * strafe_dxdy.y * s_elapsed;
        if (Math.abs(strafe_y_velocity) > Math.abs(max)) {
          strafe_y_velocity = max;
        }
        break;
      default:
        throw new DescentMapException("Unexpected StrafeDirection: " + next_action.strafe);
    }
  }