content: package tim.prune.gui.colour;
  
  import java.awt.Color;
  
  /**
   * Abstract class to do the discrete colouring of points,
   * using start and end colours and a wrapping index
   */
  public abstract class DiscretePointColourer extends PointColourer
  {
  	/** array of discrete colours to use */
  	private Color[] _discreteColours = null;
  	/** array of colour indexes */
  	private int[] _colourIndexes = null;
  
  
  	/**
  	 * Constructor
  	 * @param inStartColour start colour of scale
  	 * @param inEndColour end colour of scale
  	 * @param inMaxColours number of unique colours before wrap
  	 * @param inWideHues true for wide mode, false for narrow
  	 */
  	public DiscretePointColourer(Color inStartColour, Color inEndColour,
  		int inMaxColours, boolean inWideHues)
  	{
  		super(inStartColour, inEndColour, inMaxColours, inWideHues);
  	}
  
  	/**
  	 * Initialise the array to the right size
  	 * @param inNumPoints number of points in the track
  	 */
  	protected void init(int inNumPoints)
  	{
  		if (_colourIndexes == null || _colourIndexes.length != inNumPoints)
  		{
  			// Array needs to be created or resized
  			if (inNumPoints > 0) {
  				_colourIndexes = new int[inNumPoints];
  			}
  			else {
  				_colourIndexes = null;
  			}
  		}
  	}
  
  	/**
  	 * Set the colour at the given index
  	 * @param inPointIndex point index
  	 * @param inColourIndex index of colour to use
  	 */
  	protected void setColour(int inPointIndex, int inColourIndex)
  	{
  		if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0)
  		{
  			_colourIndexes[inPointIndex] = inColourIndex;
  		}
  	}
  
  	/**
  	 * Get the colour for the given point index
  	 * @param inPointIndex index of point in track
  	 * @return colour object
  	 */
  	public Color getColour(int inPointIndex)
  	{
  		if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0 && getMaxColours() > 0)
  		{
  			int colourIndex = _colourIndexes[inPointIndex] % getMaxColours();
  			if (colourIndex >= 0 && _discreteColours != null && colourIndex < _discreteColours.length) {
  				return _discreteColours[colourIndex];
  			}
  		}
  		// not found, use default
  		return super.getDefaultColour();
  	}
  
  	/**
  	 * Generate the set of discrete colours to use
  	 * @param inNumCategories number of different categories found in the data
  	 */
  	void generateDiscreteColours(int inNumCategories)
  	{
  		int maxColours = getMaxColours();
  		if (maxColours <= 1) {maxColours = 2;}
  		if (inNumCategories < 1) {inNumCategories = 1;}
  		else if (inNumCategories > maxColours) {inNumCategories = maxColours;}
  
  		// Use this number of categories to generate the colours
  		_discreteColours = new Color[inNumCategories];
  		for (int i=0; i<inNumCategories; i++) {
  			_discreteColours[i] = mixColour(i, inNumCategories);
  		}
  	}
  
  	/**
  	 * Mix the given colours together by interpolating H,S,B values
  	 * @param inIndex index from 0 to inWrap-1
  	 * @param inWrap wrap length
  	 * @return mixed colour
  	 */
  	private Color mixColour(int inIndex, int inWrap)
  	{
  		float fraction = inWrap < 2 ? 0.0f : (float) inIndex / (float) (inWrap - 1);
  		return mixColour(fraction);
  	}
  
  	/**
  	 * @param inIndex specified colour index
  	 * @return precalculated colour at the given index
  	 */
  	protected Color getDiscreteColour(int inIndex)
  	{
  		if (_discreteColours == null || inIndex < 0 || getMaxColours() <= 1) {
  			return getDefaultColour();
  		}
  		return _discreteColours[inIndex % getMaxColours()];
  	}
  
  }
  