content: package tim.prune.threedee;
  
  public class TerrainPatch
  {
  	private final int _gridSize;
  	private final double[] _altitudes;
  	private final int[] _tempDists;
  
  	/**
  	 * Constructor
  	 * @param inGridSize size of grid edge
  	 */
  	public TerrainPatch(int inGridSize)
  	{
  		_gridSize = inGridSize;
  		int numNodes = inGridSize * inGridSize;
  		_altitudes = new double[numNodes];
  		_tempDists = new int[numNodes];
  	}
  
  	/**
  	 * Add an altitude interpolation to the mix
  	 * @param inPointIndex point index to array
  	 * @param inValue altitude value in metres
  	 * @param inGapIndex index of point within gap, from 1 to gapLength-1
  	 * @param inGapLength length of gap, minimum 2
  	 */
  	public void addAltitude(int inPointIndex, double inValue, int inGapIndex, int inGapLength)
  	{
  		final int dist = Math.min(inGapIndex, inGapLength-inGapIndex);
  		if (_tempDists[inPointIndex] == 0)
  		{
  			if (_altitudes[inPointIndex] > 0.0) System.err.println("Altitude shouldn't be 0 if dist is 0!");
  			// first point
  			_altitudes[inPointIndex] = inValue;
  			_tempDists[inPointIndex] = dist;
  		}
  		else
  		{
  			// second point
  			final double firstValue = _altitudes[inPointIndex];
  			final int firstDist     = _tempDists[inPointIndex];
  			final double firstWeight = dist * 1.0 / (dist + firstDist);
  			final double secondWeight= firstDist * 1.0 / (dist + firstDist);
  			_altitudes[inPointIndex] = firstWeight * firstValue + secondWeight * inValue;
  			_tempDists[inPointIndex] = 0;
  		}
  	}
  
  	/**
  	 * Smooth the patch to reduce blockiness
  	 */
  	public void smooth()
  	{
  		double[] altCopy = new double[_altitudes.length];
  		for (int i=0; i<_gridSize; i++)
  		{
  			for (int j=0; j<_gridSize; j++)
  			{
  				if (hasAltitude(i, j) && hasAltitude(i-1, j) && hasAltitude(i+1, j)
  					&& hasAltitude(i, j+1) && hasAltitude(i-1, j+1) && hasAltitude(i+1, j+1)
  					&& hasAltitude(i, j-1) && hasAltitude(i-1, j-1) && hasAltitude(i+1, j-1))
  				{
  					// got a 3x3 square, can do a blur
  					double alt = (getAltitude(i, j) + getAltitude(i-1, j) + getAltitude(i+1, j)
  						+ getAltitude(i, j+1) + getAltitude(i-1, j+1) + getAltitude(i+1, j+1)
  						+ getAltitude(i, j-1) + getAltitude(i-1, j-1) + getAltitude(i+1, j-1)) / 9.0;
  					altCopy[i * _gridSize + j] = alt;
  				}
  			}
  		}
  		// Copy results back
  		for (int k=0; k<altCopy.length; k++)
  		{
  			if (altCopy[k] > 0.0)
  			{
  				_altitudes[k] = altCopy[k];
  			}
  		}
  	}
  
  	/**
  	 * @param inI first index
  	 * @param inJ second index
  	 * @return true if there is an altitude in the patch in this position
  	 */
  	private boolean hasAltitude(int inI, int inJ)
  	{
  		return inI >= 0 && inI < _gridSize && inJ >= 0 && inJ < _gridSize
  			&& _altitudes[inI * _gridSize + inJ] > 0.0;
  	}
  
  	/**
  	 * @param inI first index
  	 * @param inJ second index
  	 * @return true if there is an altitude in the patch in this position
  	 */
  	private double getAltitude(int inI, int inJ)
  	{
  		if (inI >= 0 && inI < _gridSize && inJ >= 0 && inJ < _gridSize)
  		{
  			return _altitudes[inI * _gridSize + inJ];
  		}
  		return 0.0;
  	}
  
  	/**
  	 * @param inPointIndex point index
  	 * @return altitude value
  	 */
  	public double getAltitude(int inPointIndex)
  	{
  		if (_tempDists[inPointIndex] != 0) {
  			System.err.println("Dists should be 0 if we're retrieving!");
  		}
  		return _altitudes[inPointIndex];
  	}
  }
  