preparing for color palette

master
Peter Babič 9 years ago
parent 1c7b7151ad
commit 33f62685c4
  1. 2
      .classpath
  2. 6
      src/neural/BattlefieldParameterEvaluator.java
  3. 104
      src/neural/ColorMap.java
  4. 487
      src/neural/Palette.java

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="neural/ColorMap.java" kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="lib" path="/opt/robocode/libs/robocode.jar" sourcepath="/home/delmadord/dev/Java/robocode-source/robocode-1.9.2.3-src.zip">
<attributes>

@ -22,6 +22,8 @@ import robocode.BattleResults;
import robocode.control.*;
import robocode.control.events.*;
import neural.Palette;
public class BattlefieldParameterEvaluator {
// Minimum allowable battlefield size is 400
final static int MINBATTLEFIELDSIZE = 400;
@ -153,6 +155,7 @@ public class BattlefieldParameterEvaluator {
BasicMLData input = new BasicMLData(MyTestData[NdxCooling + NdxBattleSize * NUMCOOLINGRATES]);
final MLData output = network.compute(input.clone());
double MyResult = output.getData()[0];
// 2 * fix introduced to shift the top gradient more down to se whole ColorMap spectrum
MyValue = ClipColor(MyResult * 2);
MyColor = new Color((float) MyValue, (float) MyValue, (float) MyValue);
OutputRGBint[NdxCooling + NdxBattleSize * NUMCOOLINGRATES] = MyColor.getRGB();
@ -162,7 +165,8 @@ public class BattlefieldParameterEvaluator {
// Plot the training samples
for (int NdxSample = 0; NdxSample < NUMSAMPLES; NdxSample++) {
// MyValue = ClipColor(FinalScore1[NdxSample] / BATTLE_MAX_SCORE);
MyValue = ClipColor(2 * FinalScore1[NdxSample] / 250);
// 2 * fix introduced to shift the top gradient more down to se whole ColorMap spectrum
MyValue = ClipColor(2 * FinalScore1[NdxSample] / BATTLE_MAX_SCORE);
MyColor = new Color((float) MyValue, (float) MyValue, (float) MyValue);
int MyPixelIndex = (int) (Math.round(NUMCOOLINGRATES * ((GunCoolingRate[NdxSample] / MAXGUNCOOLINGRATE) - 0.1) / 0.9) + Math
.round(NUMBATTLEFIELDSIZES * ((BattlefieldSize[NdxSample] / MAXBATTLEFIELDSIZE) - 0.1) / 0.9)

@ -0,0 +1,104 @@
/**
*
*/
package neural;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
public class ColorMap {
/**
* @param data
* @param pixelsWidth
* @param pixelsHeight
* @return
* @throws Exception
* @see https://josetips.wordpress.com/category/java/
*/
public static BufferedImage generateImage(double[][] data, int pixelsWidth, int pixelsHeight) throws Exception {
double x = 0, y = 0;
BufferedImage bi = new BufferedImage(pixelsWidth, pixelsHeight, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g = (Graphics2D) bi.getGraphics();
int w = data[0].length;
int l = data.length;
double Dy = (double) pixelsHeight / (double) l;
double Dx = (double) pixelsWidth / (double) w;
int height = (int) Math.ceil(Dy);
int width = (int) Math.ceil(Dx);
int gap = (int) Math.ceil(width / 4);
double max = -Double.MAX_VALUE;
double min = Double.MAX_VALUE;
for (int i = 0; i < l; i++) {
for (int j = 0; j < w; j++) {
max = data[i][j];
if (data[i][j] < min) {
min = data[i][j];
}
}
}
float fcolor;
float saturation = (float) 0.9;
float bright = (float) 0.7;
g.setColor(Color.WHITE);
g.fillRect(0, 0, pixelsWidth, pixelsHeight);
for (int i = 0; i < l; i++) {
for (int j = 0; j < w; j++) {
fcolor = (float) ((data[i][j] - min) / (max - min));
float hue = fcolor;
g.setColor(Color.getHSBColor((float) (hue * 0.7), saturation, bright));
g.fillRect((int) x, (int) y, width - gap, height);
x += Dx;
}
y += Dy;
x = 0;
}
File f = new File("color.png");
try {
ImageIO.write(bi, "png", f);
}
catch (IOException e) {
e.printStackTrace();
}
g.dispose();
return bi;
}
public static void main(String[] args) {
double[][] data;
int pixelsWidth = 300;
int pixelsHeight = 400;
Random r = new Random();
data = new double[pixelsWidth][];
for (int ndx = 0; ndx < pixelsWidth; ndx++) {
data[ndx] = new double[pixelsHeight];
for (int ndx2 = 0; ndx2 < pixelsHeight; ndx2++) {
// data[ndx][ndx2] = r.nextDouble();
data[ndx][ndx2] = ndx2;
}
}
try {
generateImage(data, pixelsWidth, pixelsHeight);
}
catch (Exception e) {
e.printStackTrace();
}
}
}

@ -0,0 +1,487 @@
//package edu.hws.eck.umb.palette;
package neural;
import java.awt.Color;
import java.util.ArrayList;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* Represents a palette, that is a sequence of colors. A palette assigns
* a color to each real number in the range 0 through 1, inclusive. The color
* is specified at several points in this range (including at least 0 and 1).
* These points are referred to as "division points."
* Between these points, the color is determined by linear interpolation. A palette
* can have color type HSB or RGB. For HSB colors, the interpolation is done in
* the HSB color space; for RGB colors, the interpolation is done in the RGB color
* space.
*/
public class Palette implements Cloneable {
/**
* The colorType for a palette in which colors are specified as Red/Green/Blue values.
*/
public final static int COLOR_TYPE_RGB = 0;
/**
* The colorType for a palette in which colors are specified as Hue/Saturation/Brightness values.
*/
public final static int COLOR_TYPE_HSB = 1;
private int colorType;
private boolean mirrorOutOfRangeComponents;
private ArrayList<Double> divisionPoints; // First element is always 0; last value is always 1
private ArrayList<float[]> divisionPointColors; // Size is divisionPoints.size() + 1
private final ChangeEvent changeEvent = new ChangeEvent(this);
private ArrayList<ChangeListener> changeListeners;
// public static void main(String[] args) {
//
// Palette palette = new Palette();
// palette.split(0.25);
// palette.split(0.5);
// palette.split(0.75);
// System.exit(0);
//
// }
/**
* Creates a palette of HSB color type, showing a rainbow spectrum.
*/
public Palette() {
this(COLOR_TYPE_HSB);
}
/**
* Create a palette of specified color type. For HSB color type, the palette
* is a rainbow spectrum. For the RGB color type, the palette is a grayscale
* from white to black.
* @param colorType One of the constants Palette.COLOR_TYPE_HSB or Palette.COLOR_TYPE_RGB.
* @throws IllegalArgumentException if the parameter is not one of the two valid color type constants.
*/
public Palette(int colorType) {
this.colorType = colorType;
mirrorOutOfRangeComponents = true;
divisionPoints = new ArrayList<Double>();
divisionPointColors = new ArrayList<float[]>();
divisionPoints.add(0.0);
divisionPoints.add(1.0);
if (colorType == COLOR_TYPE_HSB) { // spectrum
divisionPointColors.add(new float[] {0, 1, 1});
divisionPointColors.add(new float[] {1, 1, 1});
}
else if (colorType == COLOR_TYPE_RGB){ // grayscale
divisionPointColors.add(new float[] {1, 1, 1});
divisionPointColors.add(new float[] {0, 0, 0});
}
else
throw new IllegalArgumentException("Palette color type must be TYPE_COLOR_RGB or TYPE_COLOR_HSB");
}
Palette(int colorType, boolean mirrored, ArrayList<Double> divisionPoints, ArrayList<float[]> colorComponents) {
// For use by the PaletteIO and PaletteEditDialog classes in the same package; no error checking done here.
this.colorType = colorType;
this.mirrorOutOfRangeComponents = mirrored;
this.divisionPoints = divisionPoints;
this.divisionPointColors = colorComponents;
}
/**
* Creates one of the built-in palettes used in the Mandelbrot program.
* @param paletteName The name of the palette. Must be one of "Spectrum",
* "PaleSpectrum", "Grayscale", "CyclicGrayscale", "CyclicRedCyan",
* "EarthSky", "HotCold", or "Fire".
* @throws IllegalArgumentException if the parameter is not one of the valid
* built-in palette names.
*/
public static Palette makeDefaultPalette(String paletteName) {
Palette palette;
if (paletteName.equals("Spectrum")) {
palette = new Palette();
}
else if (paletteName.equals("PaleSpectrum")) {
palette = new Palette();
palette.setDivisionPointColorComponents(0, 0, 0.5f, 1);
palette.setDivisionPointColorComponents(1, 1, 0.5f, 1);
}
else if (paletteName.equals("DarkSpectrum")) {
palette = new Palette();
palette.setDivisionPointColorComponents(0, 0, 1, 0.5f);
palette.setDivisionPointColorComponents(1, 1, 1, 0.5f);
}
else if (paletteName.equals("Grayscale")) {
palette =new Palette(Palette.COLOR_TYPE_RGB);
}
else if (paletteName.equals("CyclicGrayscale")) {
palette = new Palette(Palette.COLOR_TYPE_RGB);
palette.split(0.5);
palette.setDivisionPointColorComponents(1, 0, 0, 0);
palette.setDivisionPointColorComponents(2, 1, 1, 1);
}
else if (paletteName.equals("CyclicRedCyan")) {
palette = new Palette(Palette.COLOR_TYPE_RGB);
palette.split(0.5);
palette.setDivisionPointColorComponents(0, 1, 0, 0);
palette.setDivisionPointColorComponents(1, 0, 1, 1);
palette.setDivisionPointColorComponents(2, 1, 0, 0);
}
else if (paletteName.equals("EarthSky")) {
palette = new Palette(Palette.COLOR_TYPE_RGB);
palette.split(0.15);
palette.split(0.33);
palette.split(0.67);
palette.split(0.85);
palette.setDivisionPointColorComponents(0, 1, 1, 1);
palette.setDivisionPointColorComponents(1, 1, 0.8f, 0);
palette.setDivisionPointColorComponents(2, 0.53f, 0.12f, 0.075f);
palette.setDivisionPointColorComponents(3, 0, 0, 0.6f);
palette.setDivisionPointColorComponents(4, 0, 0.4f, 1);
palette.setDivisionPointColorComponents(5, 1, 1, 1);
}
else if (paletteName.equals("HotCold")) {
palette = new Palette(Palette.COLOR_TYPE_RGB);
palette.split(0.16);
palette.split(0.5);
palette.split(0.84);
palette.setDivisionPointColorComponents(0, 1, 1, 1);
palette.setDivisionPointColorComponents(1, 0, 0.4f, 1);
palette.setDivisionPointColorComponents(2, 0.2f, 0.2f, 0.2f);
palette.setDivisionPointColorComponents(3, 1, 0, 0.8f);
palette.setDivisionPointColorComponents(4, 1, 1, 1);
}
else if (paletteName.equals("Fire")) {
palette = new Palette(Palette.COLOR_TYPE_RGB);
palette.split(0.17);
palette.split(0.83);
palette.setDivisionPointColorComponents(0, 0, 0, 0);
palette.setDivisionPointColorComponents(1, 1, 0, 0);
palette.setDivisionPointColorComponents(2, 1, 1, 0);
palette.setDivisionPointColorComponents(3, 1, 1, 1);
}
else
throw new IllegalArgumentException("Unknown palette: " + paletteName);
return palette;
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Palette))
return false;
Palette that = (Palette)obj;
if (that.colorType != colorType)
return false;
if (that.mirrorOutOfRangeComponents != mirrorOutOfRangeComponents)
return false;
if (that.divisionPoints.size() != divisionPoints.size())
return false;
for (int i = 0; i < divisionPoints.size(); i++) {
if (that.divisionPoints.get(i) != divisionPoints.get(i))
return false;
float[] a = that.divisionPointColors.get(i);
float[] b = divisionPointColors.get(i);
if (a[0] != b[0] || a[1] != b[1] || a[2] != b[2])
return false;
}
return true;
}
public Palette clone() {
Palette that = new Palette(this.colorType);
that.mirrorOutOfRangeComponents = this.mirrorOutOfRangeComponents;
that.divisionPoints = new ArrayList<Double>();
that.divisionPoints.addAll(this.divisionPoints);
that.divisionPointColors = new ArrayList<float[]>();
for (float[] c : this.divisionPointColors)
that.divisionPointColors.add(c.clone());
that.changed();
return that;
}
/**
* Copies all properties from a specified palette, making this
* palette equal to the specified palette.
* @param that the palette whose properties are to be copied
*/
public void copyFrom(Palette that) {
this.colorType = that.colorType;
this.mirrorOutOfRangeComponents = that.mirrorOutOfRangeComponents;
this.divisionPoints = new ArrayList<Double>();
this.divisionPoints.addAll(that.divisionPoints);
this.divisionPointColors = new ArrayList<float[]>();
for (float[] c : that.divisionPointColors)
this.divisionPointColors.add(c.clone());
this.changed();
}
/**
* Removes a specified division point, one of the points where the color is specified explicitly.
* @param divisionPointIndex the number of the division point to be removed, which much be
* in the range 1 through ct-2, where ct is the number of division points. It is not possible
* to remove the first division point (which is 0.0) or the last division point (which is 1.0).
*/
public void join(int divisionPointIndex) {
if (divisionPointIndex <= 0 || divisionPointIndex >= divisionPoints.size() - 1)
throw new IllegalArgumentException("Division point index out of range: " + divisionPointIndex);
divisionPoints.remove(divisionPointIndex);
divisionPointColors.remove(divisionPointIndex);
changed();
}
/**
* Adds a division point to the palette. The color associated to the point is obtained by
* interpolating between the colors of the points that neighbor the new point.
* @param divisionPoint The number between 0.0 and 1.0 where the new division point is to
* be added. The value cannot be the same as an existing point.
* @return The index of the new division point, this is, its position number in the list of division points.
* @throws IllegalArgumentException if the parameter is less than 0.0 or greater than 1.0, or if
* a division point already exists at the specified value.
*/
public int split(double divisionPoint) { // Return value is index of divisionPoint/color that are inserted, or -1 if exact number already exists as a divisionPoint
if (divisionPoint <= 0 || divisionPoint >= 1 || Double.isNaN(divisionPoint))
throw new IllegalArgumentException("Division point out of range: " + divisionPoint);
int index = 0;
while (divisionPoint > divisionPoints.get(index))
index++;
if (Math.abs(divisionPoint - divisionPoints.get(index)) < 1e-15)
return -1;
float ratio = (float)( (divisionPoint - divisionPoints.get(index-1))
/ (divisionPoints.get(index) - divisionPoints.get(index-1)) );
float[] c1 = divisionPointColors.get(index-1);
float[] c2 = divisionPointColors.get(index);
float a = c1[0] + ratio*(c2[0] - c1[0]);
float b = c1[1] + ratio*(c2[1] - c1[1]);
float c = c1[2] + ratio*(c2[2] - c1[2]);
float[] color = new float[] { a, b, c };
divisionPoints.add(index, divisionPoint);
divisionPointColors.add(index, color);
changed();
return index;
}
/**
* Get the color that this palette assigns to a specified number.
* @param position the number between 0.0 and 1.0, inclusive, for which the corresponding
* color is to be returned.
* @throws IllegalArgumentException if the position is outside the range 0.0 to 1.0.
*/
public Color getColor(double position) {
if (position < 0 || position > 1)
throw new IllegalArgumentException("Position " + position + " is out of range.");
int pt = 1;
while (position > divisionPoints.get(pt))
pt++;
float ratio = (float)( (position - divisionPoints.get(pt-1))
/ (divisionPoints.get(pt) - divisionPoints.get(pt-1)) );
float[] c1 = divisionPointColors.get(pt-1);
float[] c2 = divisionPointColors.get(pt);
float a = clamp1(c1[0] + ratio*(c2[0] - c1[0]));
float b = clamp2(c1[1] + ratio*(c2[1] - c1[1]));
float c = clamp2(c1[2] + ratio*(c2[2] - c1[2]));
Color color;
if (colorType == COLOR_TYPE_HSB)
color = Color.getHSBColor(a, b, c);
else
color = new Color(a, b, c);
return color;
}
/**
* Get an array of RGB color values corresponding to equally spaced points in the
* range 0.0 to 1.0.
* @param paletteLength The number of points for which colors will be returned.
* @param offset the color values are "rotated" by this amount within the array.
* That is, the color value corresponding to 0.0 is in the array at index = offset
* (or, more exactly, paletteLength % offset).
*/
public int[] makeRGBs(int paletteLength, int offset) {
int[] rgb;
rgb = new int[paletteLength];
rgb[offset % paletteLength] = getDivisionPointColor(0).getRGB();
int ct = 1;
double dx = 1.0 / (paletteLength-1);
int pt = 1;
while (ct < paletteLength-1) {
double position = dx*ct;
while (position > divisionPoints.get(pt))
pt++;
float ratio = (float)( (position - divisionPoints.get(pt-1))
/ (divisionPoints.get(pt) - divisionPoints.get(pt-1)) );
float[] c1 = divisionPointColors.get(pt-1);
float[] c2 = divisionPointColors.get(pt);
float a = clamp1(c1[0] + ratio*(c2[0] - c1[0]));
float b = clamp2(c1[1] + ratio*(c2[1] - c1[1]));
float c = clamp2(c1[2] + ratio*(c2[2] - c1[2]));
Color color;
if (colorType == COLOR_TYPE_HSB)
color = Color.getHSBColor(a, b, c);
else
color = new Color(a, b, c);
rgb[(ct + offset) % paletteLength] = color.getRGB();
ct++;
}
rgb[(offset + paletteLength-1) % paletteLength] = getDivisionPointColor(divisionPoints.size()-1).getRGB();
return rgb;
}
/**
* Returns the number of division points in the palette, always two or more.
*/
public int getDivisionPointCount() {
return divisionPoints.size();
}
/**
* Returns a specified division points. The return value is the range 0.0 to 1.0. Divsion points
* are stored in strictly increasing order.
* @param index The index of the desired division point in the list of division points.
*/
public double getDivisionPoint(int index) {
return divisionPoints.get(index);
}
/**
* Sets the value of a specified division point. Does not apply to the first or last points,
* which always have values 0.0 and 1.0. The new value must be strictly between the positions
* the neighboring division points.
* @param index The index of the division point whose position is to be set.
* @param position The new position for the specified division point.
* @throws IllegalArgumentException if the index or position is not valid.
*/
public void setDivisionPoint(int index, double position) {
if (index <= 0 || index >= divisionPoints.size() - 1)
throw new IllegalArgumentException("Index out of legal range");
if (position <= divisionPoints.get(index-1) || position >= divisionPoints.get(index+1))
throw new IllegalArgumentException("Division point position outside of legal range.");
if (position != divisionPoints.get(index)) {
divisionPoints.set(index, position);
changed();
}
}
/**
* Get the color associated with a given division point.
* @param index The index of the division point in the list of points.
*/
public Color getDivisionPointColor(int index) {
float[] components = divisionPointColors.get(index);
float a = clamp1(components[0]);
float b = clamp2(components[1]);
float c = clamp2(components[2]);
if (colorType == COLOR_TYPE_RGB)
return new Color(a,b,c);
else
return Color.getHSBColor(a,b,c);
}
/**
* Returns the color components for the division point at a specified index.
*/
public float[] getDivisionPointColorComponents(int index) {
return divisionPointColors.get(index).clone();
}
/**
* Set the color components for the division point at a s specified index in the list of
* division points. These components are the color data that is stored for each division
* point and that are used for interpolation between division points. Note that when a color
* is actually computed, the component values must be in the range 0.0 to 1.0. However, the
* values specified here do NOT have to be in this range. Values given here are used for
* interpolation, and then the resulting values are transformed into the range 0.0 to 1.0
* just before the color is computed. This means that the value can effectively oscillate
* several times between two division points.
* @param index
* @param c1 The Red color component for an RGB palette, or the Hue component for an HSB palette.
* @param c2 The Green color component for an RGB palette, or the Saturation component for an HSB palette.
* @param c3 The Blue color component for an RGB palette, or the Brightness component for an HSB palette.
*/
public void setDivisionPointColorComponents(int index, float c1, float c2, float c3) {
float[] c = divisionPointColors.get(index);
if (c1 == c[0] && c2 == c[1] && c3 == c[2])
return;
c[0] = c1;
c[1] = c2;
c[2] = c3;
changed();
}
/**
* Return the color type of this palette, which is one of the constants Palette.COLOR_TYPE_RGB
* or Palette.COLOR_TYPE_HSB.
*/
public int getColorType() {
return colorType;
}
public boolean getMirrorOutOfRangeComponents() {
return mirrorOutOfRangeComponents;
}
/**
* Sets the value of the confusing mirrorOutOfRangeComponents property. This only has
* an effect when a floating-point color component value that is outside the range
* 0.0 to 1.0 has to be transformed to a value within that range. For a Hue, the whole-number
* part is simply discarded. For the other components however, the transformation depends
* on the value of the mirrorOutOfRangeComponents property. If the property is false,
* the whole-number part is discarded, but this results in a discontinuity at integer values.
* If the property is true, this discontinuity is avoided by having the value oscillate with
* period 2 instead of cycle with period 1. The default value is true, and this is never
* changed in the Mandelbrot application. So, really, you shouldn't even be reading this.
*/
public void setMirrorOutOfRangeComponents(boolean mirrorOutOfRangeComponents) {
if (this.mirrorOutOfRangeComponents == mirrorOutOfRangeComponents)
return;
this.mirrorOutOfRangeComponents = mirrorOutOfRangeComponents;
changed();
}
/**
* Add a listener that will be notified whenever the palette changes in any way.
*/
public void addChangeListener(ChangeListener listener) {
if (listener == null)
return;
if (changeListeners == null)
changeListeners = new ArrayList<ChangeListener>();
if (!changeListeners.contains(listener))
changeListeners.add(listener);
}
/**
* Remove a listener (if present in the list of listeners).
*/
public void removeChangeListener(ChangeListener listener) {
if (changeListeners == null)
return;
changeListeners.remove(listener);
if (changeListeners.size() == 0)
changeListeners = null;
}
private float clamp1(float x) {
if (colorType == COLOR_TYPE_HSB || !mirrorOutOfRangeComponents)
return x - (float)Math.floor(x);
else
return clamp2(x);
}
private float clamp2(float x) {
if (!mirrorOutOfRangeComponents)
return x - (float)Math.floor(x);
x = 2*(x/2 - (float)Math.floor(x/2));
if (x > 1)
x = 2 - x;
return x;
}
private void changed() {
if (changeListeners != null)
for (ChangeListener lst : changeListeners)
lst.stateChanged(changeEvent);
}
}
Loading…
Cancel
Save