public double calcDensity(int x, int y, int z) {
    double height = calcBaseTerrain(x, z);
    double ocean = calcOceanTerrain(x, z);
    double river = calcRiverTerrain(x, z);

    float temp = biomeProvider.getTemperatureAt(x, z);
    float humidity = biomeProvider.getHumidityAt(x, z);

    Vector2f distanceToMountainBiome = new Vector2f(temp - 0.25f, humidity - 0.35f);

    double mIntens = TeraMath.clamp(1.0 - distanceToMountainBiome.length() * 3.0);
    double densityMountains = calcMountainDensity(x, y, z) * mIntens;
    double densityHills = calcHillDensity(x, y, z) * (1.0 - mIntens);

    int plateauArea = (int) (Chunk.SIZE_Y * 0.10);
    double flatten = TeraMath.clamp(((Chunk.SIZE_Y - 16) - y) / plateauArea);

    return -y
        + (((32.0 + height * 32.0) * TeraMath.clamp(river + 0.25) * TeraMath.clamp(ocean + 0.25))
                + densityMountains * 1024.0
                + densityHills * 128.0)
            * flatten;
  }
  /**
   * Method that allows Physics Engine to modify Speed / Position during the relax phase.
   *
   * @param pNode the node to which the force apply @Note Standard physics is "Position Variation =
   *     Speed x Duration" with a convention of "Duration=1" between to frames
   */
  public void onRelax(code_swarm.PersonNode pNode) {
    Vector2f delta = new Vector2f();

    // A gentle force to attract pNodes to the center
    // NOTE: this should be done prior to attraction/repulsion forces, otherwise
    // tends to generate a grid-like pattern
    Vector2f midpoint = new Vector2f(code_swarm.width / 2, code_swarm.height / 2);
    delta = new Vector2f();
    delta.sub(midpoint, pNode.mPosition);

    delta.scale(1 / delta.length() * 0.003f);
    pNode.mPosition.add(delta);

    // All person nodes attract each other, but only to a certain point, then they repel with gentle
    // force
    for (code_swarm.PersonNode n : code_swarm.getLivingPeople()) {
      if (pNode != n) {
        delta.sub(pNode.mPosition, n.mPosition);
        if (delta.lengthSquared() < MIN_DISTANCE_SQR) {
          // This calculation gives a 'stiff spring' affect
          float toMove = ((float) Math.sqrt(MIN_DISTANCE_SQR) - delta.length()) / 10.0f;

          // This calculation gives a much nicer flow
          // float toMove = 0.03f;
          delta.scale((1 / delta.length()) * toMove);

          n.mPosition.sub(delta);
          pNode.mPosition.add(delta);
        } else {
          float toMove = -0.003f;
          delta.scale((1 / delta.length()) * toMove);

          n.mPosition.sub(delta);
          pNode.mPosition.add(delta);
        }
      }
    }

    // place the edited files around the person
    Iterator<code_swarm.FileNode> editedFiles = pNode.editing.iterator();
    int index = 0;
    int radius = 45;
    final int node_size = 4;
    final int salt = pNode.hashCode(); // used to randomize orientation of circle of nodes
    int num_nodes_in_ring = (int) ((2 * radius * Math.PI) / node_size);
    while (editedFiles.hasNext()) {
      // if we've placed all the nodes in this ring...
      if (index == num_nodes_in_ring) {
        // start on a new ring
        radius += node_size;
        num_nodes_in_ring = (int) ((2 * radius * Math.PI) / node_size);
        index = 0;
      }
      index++;

      code_swarm.FileNode file = editedFiles.next();
      // leave a hole for the null files
      if (file == null) continue;

      final int place_around_ring = index * num_nodes_in_ring + salt;
      int x = (int) (radius * Math.sin(place_around_ring));
      int y = (int) (radius * Math.cos(place_around_ring));

      delta = new Vector2f();
      delta.sub(file.mPosition, new Vector2f(pNode.mPosition.x + x, pNode.mPosition.y + y));
      float distance = delta.length();
      delta.scale(1 / delta.length() * -0.01f * distance);
      file.mPosition.add(delta);
    }
  }