/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.display2d;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import org.opensourcephysics.display.DisplayRes;
import org.opensourcephysics.display.InteractivePanel;
import org.opensourcephysics.display.axes.XAxis;
import org.opensourcephysics.display.axes.XYAxis;
public class VectorColorMapper {
public static final int SPECTRUM = 0;
public static final int RED = 1;
public static final int BLUE = 2;
public static final int GREEN = 3;
public static final int GRAY = 4;
public static final int BLACK = 5; // solid black
private static final Color RED_COMP;
private static final Color GREEN_COMP;
private static final Color BLUE_COMP;
private Color background = Color.WHITE;
private Color[] colors;
private Color[] compColors;
private double ceil, floor;
private int numColors;
private int paletteType;
private JFrame legendFrame;
private VectorPlot legendPlot;
private InteractivePanel legendPanel;
static {
float[] hsb = Color.RGBtoHSB(255, 0, 0, null);
RED_COMP = Color.getHSBColor((hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complement red hue
hsb = Color.RGBtoHSB(0, 255, 0, null);
GREEN_COMP = Color.getHSBColor((hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complement green hue
hsb = Color.RGBtoHSB(0, 255, 0, null);
BLUE_COMP = Color.getHSBColor((hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complement blue hue
}
/**
* Constructor VectorColorMapper
* @param _numColors
* @param _ceil
*/
public VectorColorMapper(int _numColors, double _ceil) {
ceil = _ceil;
numColors = _numColors;
floor = (numColors<2) ? 0 : ceil/(numColors-1);
paletteType = SPECTRUM;
createSpectrumPalette(); // default colors
}
/**
* Sets the number of colors
* @param _numColors
*/
public void setNumberOfColors(int _numColors) {
if(_numColors==numColors) {
return;
}
numColors = _numColors;
floor = (numColors<2) ? 0 : ceil/(numColors-1);
setPaletteType(paletteType);
}
public double getFloor() {
return floor;
}
public double getCeiling() {
return ceil;
}
/**
* Sets the color palette.
* @param _paletteType
*/
public void setPaletteType(int _paletteType) {
if(paletteType==_paletteType && numColors==colors.length) {
return;
}
floor = (numColors<2) ? 0 : ceil/(numColors-1);
paletteType = _paletteType;
switch(paletteType) { // computes new palette
case RED :
createRedSpectrumPalette();
return;
case BLUE :
createBlueSpectrumPalette();
return;
case GREEN :
createGreenSpectrumPalette();
return;
case BLACK : // always black; no need to compute anything
return;
case GRAY :
createGraySpectrumPalette();
return;
default : // spectrum of colors from light blue toward blue toward red toward black
createSpectrumPalette();
}
}
/**
* Checks to see if background color matches the color palette background.
* @param backgroundColor
*/
public void checkPallet(Color backgroundColor) {
if(background==backgroundColor) {
return; // backgrounds match
}
background = backgroundColor;
switch(paletteType) { // compute palette using new background
case RED :
createRedSpectrumPalette();
return;
case BLUE :
createBlueSpectrumPalette();
return;
case GREEN :
createGreenSpectrumPalette();
return;
case BLACK : // always black; no need to compute anything
return;
case GRAY :
createGraySpectrumPalette();
return;
default : // spectrum of colors from light blue toward blue toward red toward black
createSpectrumPalette();
}
}
/**
* Sets the scale.
*
* @param _ceil
*/
public void setScale(double _ceil) {
ceil = _ceil;
floor = (numColors<2) ? 0 : ceil/(numColors-1);
}
/**
* Converts a double to a the complementary color.
* @param mag
* @return the color
*/
public Color doubleToCompColor(double mag) {
if(mag<=floor) { // magnitudes less than floor are clear
return background;
}
int index;
switch(paletteType) {
case RED :
if(mag>=ceil) {
return RED_COMP;
}
index = (int) ((numColors-1)*(mag/ceil));
return compColors[index]; // shade of red
case BLUE :
if(mag>=ceil) {
return BLUE_COMP;
}
index = (int) ((numColors-1)*(mag/ceil));
return compColors[index]; // shade of blue
case GREEN :
if(mag>=ceil) {
return GREEN_COMP;
}
index = (int) ((numColors-1)*(mag/ceil));
return compColors[index]; // shade of green
case BLACK : // complement of BLACK is WHITE
return Color.WHITE;
case GRAY : // cannot complement gray
if(mag>=ceil) {
return Color.black;
}
index = (int) ((numColors-1)*(mag/ceil));
return colors[index]; // shade of gray from white
default : // spectrum of colors from light blue toward blue toward red toward black
Color c = getSpectrumColor(mag);
float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
float[] hsbBack = Color.RGBtoHSB(background.getRed(), background.getGreen(), background.getBlue(), null);
return Color.getHSBColor((2*hsbBack[0]-hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complementary hue
}
}
/**
* Converts a double to a color.
* @param mag
* @return the color
*/
public Color doubleToColor(double mag) {
if(mag<=floor) { // magnitudes less than floor are clear
return background;
}
int index;
switch(paletteType) {
case RED :
if(mag>=ceil) {
return Color.RED;
}
index = (int) ((numColors-1)*(mag/ceil));
return colors[index]; // shade of red
case BLUE :
if(mag>=ceil) {
return Color.BLUE;
}
index = (int) ((numColors-1)*(mag/ceil));
return colors[index]; // shade of blue
case GREEN :
if(mag>=ceil) {
return Color.GREEN;
}
index = (int) ((numColors-1)*(mag/ceil));
return colors[index]; // shade of green
case BLACK : // always black
return Color.black;
case GRAY :
if(mag>=ceil) {
return Color.black;
}
index = (int) ((numColors-1)*(mag/ceil));
return colors[index]; // shade of gray from white
default : // spectrum of colors
return getSpectrumColor(mag);
}
}
private Color getSpectrumColor(double mag) {
if((background==Color.BLACK)&&(mag>=ceil)) { // magnitude greater than max tends from RED toward pure WHITE
int s = (int) (255*(1-ceil/mag));
return new Color(255, s, s);
} else if(mag>=ceil) { // magnitude greater than max tends from RED toward pure BLACK
return new Color((int) (255.0*ceil/mag), 0, 0);
}
int index = (int) ((numColors-1)*(mag/ceil));
return colors[index];
}
private void createRedSpectrumPalette() {
colors = new Color[numColors];
compColors = new Color[numColors];
int bgr = background.getRed();
int bgg = background.getGreen();
int bgb = background.getBlue();
for(int i = 0; i<numColors; i++) { // start with the background and increase toward pure RED
int tr = bgr+(255-bgr)*i/numColors; // increase the red
int tg = bgg-bgg*i/numColors; // decrease the green
int tb = bgb-bgb*i/numColors; // decrease the blue
colors[i] = new Color(tr, tg, tb);
float[] hsb = Color.RGBtoHSB(tr, tg, tb, null);
Color c = Color.getHSBColor((hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complementary hue
tr = bgr+(c.getRed()-bgr)*i/numColors;
tg = bgg+(c.getGreen()-bgg)*i/numColors;
tb = bgb+(c.getBlue()-bgb)*i/numColors;
compColors[i] = new Color(tr, tg, tb);
}
}
private void createGreenSpectrumPalette() {
colors = new Color[numColors];
compColors = new Color[numColors];
int bgr = background.getRed();
int bgg = background.getGreen();
int bgb = background.getBlue();
for(int i = 0; i<numColors; i++) { // start with the background and increase toward pure GREEN
int tr = bgr-bgr*i/numColors; // decrease the red
int tg = bgg+(255-bgg)*i/numColors; // increase the green
int tb = bgb-bgb*i/numColors; // decrease the blue
colors[i] = new Color(tr, tg, tb);
float[] hsb = Color.RGBtoHSB(tr, tg, tb, null);
Color c = Color.getHSBColor((hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complementary hue
tr = bgr+(c.getRed()-bgr)*i/numColors;
tg = bgg+(c.getGreen()-bgg)*i/numColors;
tb = bgb+(c.getBlue()-bgb)*i/numColors;
compColors[i] = new Color(tr, tg, tb);
}
}
private void createBlueSpectrumPalette() {
colors = new Color[numColors];
compColors = new Color[numColors];
int bgr = background.getRed();
int bgg = background.getGreen();
int bgb = background.getBlue();
for(int i = 0; i<numColors; i++) { // start with the background and increase toward pure BLUE
int tr = bgr-bgr*i/numColors; // decrease the red
int tg = bgg-bgg*i/numColors; // decrease the green
int tb = bgb+(255-bgb)*i/numColors; // increase the blue
colors[i] = new Color(tr, tg, tb);
float[] hsb = Color.RGBtoHSB(tr, tg, tb, null);
Color c = Color.getHSBColor((hsb[0]+0.5f)%1, hsb[1], hsb[2]); // complementary hue
tr = bgr+(c.getRed()-bgr)*i/numColors;
tg = bgg+(c.getGreen()-bgg)*i/numColors;
tb = bgb+(c.getBlue()-bgb)*i/numColors;
compColors[i] = new Color(tr, tg, tb);
}
}
private void createGraySpectrumPalette() {
compColors = colors = new Color[numColors]; // cannot complement gray color
if(background==Color.BLACK) { // special case; increase toward white
for(int i = 0; i<numColors; i++) {
int sat = 255*i/numColors; // increase the saturation
colors[i] = new Color(sat, sat, sat);
}
return;
}
int bgr = background.getRed();
int bgg = background.getGreen();
int bgb = background.getBlue();
for(int i = 0; i<numColors; i++) { // start with the background and increase toward BLACK
int tr = bgr-bgr*i/numColors; // decrease the red
int tg = bgg-bgg*i/numColors; // decrease the green
int tb = bgb-bgb*i/numColors; // decrease the blue
colors[i] = new Color(tr, tg, tb);
}
}
private void createSpectrumPalette() {
compColors = colors = new Color[numColors]; // use same colors for complement
int n1 = numColors/3;
n1 = Math.max(1, n1);
int bgr = background.getRed();
int bgg = background.getGreen();
int bgb = background.getBlue();
for(int i = 0; i<n1; i++) { // start with the background and increase toward all blue
int tr = bgr-bgr*i/n1;
int tg = bgg-bgg*i/n1;
int tb = Math.min(255,bgg+(255-bgb)*i/n1);
colors[i] = new Color(tr, tg, tb);
}
for(int i = n1; i<numColors; i++) { // decrease blue and increase green and then red
double sigma = n1/1.2;
double arg1 = (i-n1)/sigma;
double arg2 = (i-2*n1)/sigma;
double arg3 = (i-numColors)/sigma;
int b = (int) (255*Math.exp(-arg1*arg1));
int g = (int) (255*Math.exp(-arg2*arg2));
int r = (int) (255*Math.exp(-arg3*arg3));
r = Math.min(255, r);
b = Math.min(255, b);
g = Math.min(255, g);
colors[i] = new Color(r, g, b);
}
}
public JFrame showLegend() {
double floor = 0;
double ceil = this.ceil*2;
legendPanel = new InteractivePanel();
legendPanel.setPreferredSize(new java.awt.Dimension(300, 120));
legendPanel.setPreferredGutters(0, 0, 0, 35);
legendPanel.setClipAtGutter(false);
legendPanel.setSquareAspect(false);
if((legendFrame==null)||!legendFrame.isDisplayable()) {
legendFrame = new JFrame(DisplayRes.getString("GUIUtils.Legend")); //$NON-NLS-1$
}
legendFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
legendFrame.setResizable(true);
legendFrame.setContentPane(legendPanel);
int numVecs = 30;
GridPointData pointdata = new GridPointData(numVecs, 2, 3);
double[][][] data = pointdata.getData();
double delta = 1.5*ceil/numVecs;
double cval = floor-delta/2;
for(int i = 0, n = data.length; i<n; i++) {
data[i][1][2] = cval;
data[i][1][3] = 0;
data[i][1][4] = 4;
cval += delta;
}
pointdata.setScale(0, 1.5*ceil+delta, 0, 1);
legendPlot = new VectorPlot(pointdata);
legendPlot.setPaletteType(paletteType);
legendPlot.setAutoscaleZ(false, 0.5*ceil, ceil);
legendPlot.update();
legendPanel.addDrawable(legendPlot);
XAxis xaxis = new XAxis(""); //$NON-NLS-1$
xaxis.setLocationType(XYAxis.DRAW_AT_LOCATION);
xaxis.setLocation(-0.0);
xaxis.setEnabled(true);
legendPanel.addDrawable(xaxis);
legendFrame.pack();
legendFrame.setVisible(true);
return legendFrame;
}
public JFrame getLegendFrame() {
return legendFrame;
}
public void updateLegend() {
if(legendPlot==null) {
return;
}
legendPlot.setPaletteType(paletteType);
legendPlot.setAutoscaleZ(false, 0.5*ceil, ceil);
legendPlot.update();
legendPanel.repaint();
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/