private void drawFeatheredFullLine(
      Vector2 a, Vector2 b, float width, float feather, Color color) {
    checkMaxVerts(3 * 6);

    // Calculate the normal that defines the center rectangle
    norm.set(b);
    norm.sub(a);
    norm.rotate(90);
    norm.setLength(width / 2); // Scale to line width

    // Calculate the normal that defines feathering
    feath.set(norm);
    feath.setLength(feather / 2);

    zeroAlpha.set(color);
    zeroAlpha.a = 0;

    // ORDER OF RENDERING

    // 1                    2
    // top feather
    // 3                    4

    // center

    // 5                    6
    // bottom feather
    // 7                    8

    // Top feather
    tL.set(a).add(norm).add(feath);
    tR.set(b).add(norm).add(feath);
    bL.set(a).add(norm);
    bR.set(b).add(norm);

    // Draw it
    // Top feather  (1-2-3)
    uncheckedTriangle(tL.x, tL.y, tR.x, tR.y, bL.x, bL.y, zeroAlpha, zeroAlpha, color);
    uncheckedTriangle(tR.x, tR.y, bL.x, bL.y, bR.x, bR.y, zeroAlpha, color, color);

    // Center line
    tL.set(a).add(norm);
    tR.set(b).add(norm);
    bL.set(a).sub(norm);
    bR.set(b).sub(norm);

    // Draw it
    uncheckedTriangle(tL.x, tL.y, tR.x, tR.y, bL.x, bL.y, color, color, color);
    uncheckedTriangle(tR.x, tR.y, bL.x, bL.y, bR.x, bR.y, color, color, color);

    // Bottom feather
    tL.set(a).sub(norm);
    tR.set(b).sub(norm);
    bL.set(a).sub(norm).sub(feath);
    bR.set(b).sub(norm).sub(feath);

    // Draw it
    uncheckedTriangle(tL.x, tL.y, tR.x, tR.y, bL.x, bL.y, color, color, zeroAlpha);
    uncheckedTriangle(tR.x, tR.y, bL.x, bL.y, bR.x, bR.y, color, zeroAlpha, zeroAlpha);
  }
  public void drawLine(Vector2 a, Vector2 b, float width, Color color) {
    setType(GL20.GL_TRIANGLES);

    // Calculate the normal that defines the center rectangle
    norm.set(b);
    norm.sub(a);
    norm.rotate(90);
    norm.setLength(width / 2); // Scale to line width

    // 1                    2
    // top feather
    // 3                    4

    // Top feather
    tL.set(a).add(norm);
    tR.set(b).add(norm);
    bL.set(a).sub(norm);
    bR.set(b).sub(norm);

    uncheckedTriangle(tL.x, tL.y, tR.x, tR.y, bL.x, bL.y, color, color, color);
    uncheckedTriangle(tR.x, tR.y, bL.x, bL.y, bR.x, bR.y, color, color, color);
  }
  /**
   * Rain clouds appear with a certain chance, influenced by the weather For every rain cloud in the
   * grid the velocity of every rain object is updated Rain clouds are removed if they have passed a
   * certain time
   */
  @ScheduledMethod(start = 1, interval = 1, priority = 0)
  public void rain() {
    // Let new raingroups appear with a certain chance
    double chance = SimulationParameters.rainProb;
    // The probability of rain appearing decreases if there is already rain in the grid
    if (noRainGroups == 1) chance = (chance / (noRainGroups)) * 0.5;
    if (noRainGroups == 2) chance = (chance / (noRainGroups)) * 0.1;
    if (noRainGroups > 2) chance = (chance / (noRainGroups)) * 0.01;
    double f = urng.nextDouble();
    if (f < chance) {
      // Let rain appear
      int x = rand.nextInt((SimulationParameters.gridSize - 0) + 1);
      int y = rand.nextInt((SimulationParameters.gridSize - 0) + 1);
      int[] newLoc = {x, y};
      // Let new raingroup appear in random location
      RainGroup rg = new RainGroup(ContextUtils.getContext(this), grid, newLoc);
      noRainGroups++;
      rainGroups.add(rg);
    }

    ArrayList<RainGroup> toRemove = new ArrayList<RainGroup>();
    for (RainGroup rg : rainGroups) {
      // Get velocity vector of the rain
      float x = Wind.getWindVelocity().x;
      float y = Wind.getWindVelocity().y;
      Vector2 velRain = new Vector2(x, y);
      velRain.setLength(
          Wind.getWindVelocity().len() * 0.9f); // Rain speed is a bit lower than that of the wind

      List<Rain> toRemove1 = new ArrayList<Rain>();
      // Let rain be carried by the wind
      if (urng.nextDouble() < velRain.len()) {
        for (Rain rain : rg.getRainObjects()) {
          Directions dir = Directions.fromVectorToDir(velRain);
          GridPoint pt = grid.getLocation(rain);
          int cX = pt.getX() + dir.xDiff;
          int cY = pt.getY() + dir.yDiff;

          // If new rain-location is out of borders, delete this rain object
          // In this way the cloud "travels" out of the grid
          if (cX < 0
              || cX >= SimulationParameters.gridSize
              || cY < 0
              || cY >= SimulationParameters.gridSize) {
            toRemove1.add(rain);
          } else grid.moveTo(rain, cX, cY);
        }
      }

      for (Rain r : toRemove1) {
        rg.removeRain(r);
        TreeBuilder.performance.decreaseRainCount();
      }
    }

    // Remove the raingroups from our list which were removed from the context
    for (RainGroup rg : toRemove) {
      rainGroups.remove(rg);
      noRainGroups--;
    }
  }