--- /dev/null
+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
+ */
+ public DiscretePointColourer(Color inStartColour, Color inEndColour, int inMaxColours)
+ {
+ super(inStartColour, inEndColour, inMaxColours);
+ }
+
+ /** max number of colours is required here */
+ public static boolean isMaxColoursRequired() {
+ return true;
+ }
+
+ /**
+ * 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
+ */
+ protected 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()];
+ }
+
+}