content: package tim.prune.data;
  
  /**
   * Class to manage the scaling of points, used by the ThreeDModel
   */
  public class PointScaler
  {
  	/** Original data */
  	private final Track _track;
  	/** Secondary data for terrain grid */
  	private Track _terrainTrack = null;
  	// Scaled values for data track
  	private double[] _xValues = null;
  	private double[] _yValues = null;
  	private double[] _altValues = null;
  	// Scaled values for terrain track, if any
  	private double[] _terrainxValues = null;
  	private double[] _terrainyValues = null;
  	private double[] _terrainAltValues = null;
  	// Altitude range
  	private double _altitudeRange = 0.0;
  
  
  	/**
  	 * Constructor
  	 * @param inTrack Track object
  	 */
  	public PointScaler(Track inTrack)
  	{
  		_track = inTrack;
  	}
  
  	/**
  	 * @param inTrack terrain track to add
  	 */
  	public void addTerrain(Track inTrack)
  	{
  		_terrainTrack = inTrack;
  	}
  
  	/**
  	 * Scale the points
  	 */
  	public void scale()
  	{
  		// Work out extents
  		TrackExtents extents = new TrackExtents(_track);
  		extents.applySquareBorder();
  		// Horizontal distance
  		double horizDistanceMetres = Math.max(extents.getHorizontalDistanceMetres(), 1.0);
  		final int numPoints = _track.getNumPoints();
  
  		// Find altitude range (including terrain)
  		DoubleRange altRangeMetres = extents.getAltitudeRange();
  		if (_terrainTrack != null) {
  			altRangeMetres.combine(new TrackExtents(_terrainTrack).getAltitudeRange());
  		}
  		_altitudeRange = altRangeMetres.getRange() / horizDistanceMetres;
  		double minAltitudeMetres = altRangeMetres.getMinimum();
  
  		// create new arrays for scaled values
  		if (_xValues == null || _xValues.length != numPoints)
  		{
  			_xValues = new double[numPoints];
  			_yValues = new double[numPoints];
  			_altValues = new double[numPoints];
  			if (_terrainTrack != null)
  			{
  				_terrainxValues = new double[_terrainTrack.getNumPoints()];
  				_terrainyValues = new double[_terrainTrack.getNumPoints()];
  				_terrainAltValues = new double[_terrainTrack.getNumPoints()];
  			}
  		}
  
  		final double midXvalue = extents.getXRange().getMidValue();
  		final double midYvalue = extents.getYRange().getMidValue();
  		final double xyRange   = extents.getXRange().getRange();
  
  		// Calculate scaled values
  		for (int p=0; p<numPoints; p++)
  		{
  			DataPoint point = _track.getPoint(p);
  			if (point != null)
  			{
  				_xValues[p] = (_track.getX(p) - midXvalue) / xyRange;
  				_yValues[p] = (midYvalue - _track.getY(p)) / xyRange; // y values have to be inverted
  				_altValues[p] = (point.getAltitude().getMetricValue() - minAltitudeMetres) / horizDistanceMetres;
  			}
  		}
  		if (_terrainTrack != null)
  		{
  			for (int p=0; p<_terrainTrack.getNumPoints(); p++)
  			{
  				_terrainxValues[p] = (_terrainTrack.getX(p) - midXvalue) / xyRange;
  				_terrainyValues[p] = (midYvalue - _terrainTrack.getY(p)) / xyRange; // y values have to be inverted
  				_terrainAltValues[p] = (_terrainTrack.getPoint(p).getAltitude().getMetricValue() - minAltitudeMetres) / horizDistanceMetres;
  			}
  		}
  	}
  
  
  
  	/**
  	 * Get the horizontal value for the specified point
  	 * @param inIndex index of point, starting at 0
  	 * @return scaled horizontal value
  	 */
  	public double getHorizValue(int inIndex)
  	{
  		return _xValues[inIndex];
  	}
  
  	/**
  	 * Get the vertical value for the specified point
  	 * @param inIndex index of point, starting at 0
  	 * @return scaled vertical value
  	 */
  	public double getVertValue(int inIndex)
  	{
  		return _yValues[inIndex];
  	}
  
  	/**
  	 * Get the altitude value for the specified point
  	 * @param inIndex index of point, starting at 0
  	 * @return scaled altitude value
  	 */
  	public double getAltValue(int inIndex)
  	{
  		return _altValues[inIndex];
  	}
  
  	/**
  	 * @return altitude range as fraction of horizontal range
  	 */
  	public double getAltitudeRange()
  	{
  		return _altitudeRange;
  	}
  
  	/**
  	 * Get the horizontal value for the specified point
  	 * @param inIndex index of point, starting at 0
  	 * @return scaled horizontal value
  	 */
  	public double getTerrainHorizValue(int inIndex)
  	{
  		return _terrainxValues[inIndex];
  	}
  
  	/**
  	 * Get the vertical value for the specified point
  	 * @param inIndex index of point, starting at 0
  	 * @return scaled vertical value
  	 */
  	public double getTerrainVertValue(int inIndex)
  	{
  		return _terrainyValues[inIndex];
  	}
  
  	/**
  	 * @param inIndex index of point in terrain track
  	 * @return scaled altitude value for the specified terrain point
  	 */
  	public double getTerrainAltValue(int inIndex)
  	{
  		if (_terrainAltValues != null) {
  			return _terrainAltValues[inIndex];
  		}
  		return 0.0;
  	}
  }
  