/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.plotter; import java.awt.Color; import com.rapidminer.datatable.DataTable; import com.rapidminer.datatable.DataTableRow; import com.rapidminer.gui.MainFrame; import com.rapidminer.parameter.ParameterTypeColor; import com.rapidminer.tools.ParameterService; import com.rapidminer.tools.parameter.ParameterChangeListener; /** * This class delivers colors according to the user settings. * * @author Ingo Mierswa */ public class ColorProvider { private static Color minColor; private static Color maxColor; /** used as default before Studio 7.5 */ private static final Color MIN_DEFAULT_COLOR_PRE_75 = new Color(58, 58, 255); /** used as default before Studio 7.5 */ private static final Color MAX_DEFAULT_COLOR_PRE_75 = new Color(255, 48, 48); public static final Color MIN_DEFAULT_COLOR = new Color(73, 144, 226); public static final Color MAX_DEFAULT_COLOR = new Color(232, 76, 61); static { // update colors when user changes them in the settings ParameterService.registerParameterChangeListener(new ParameterChangeListener() { @Override public void informParameterSaved() { // ignore } @Override public void informParameterChanged(String key, String value) { if (MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MINCOLOR.equals(key)) { minColor = getColorFromProperty(MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MINCOLOR, MIN_DEFAULT_COLOR); } if (MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MAXCOLOR.equals(key)) { maxColor = getColorFromProperty(MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MAXCOLOR, MAX_DEFAULT_COLOR); } } }); minColor = getColorFromProperty(MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MINCOLOR, MIN_DEFAULT_COLOR); boolean fixed = false; // this was the previous default and we cannot detect if the user has ever changed it so we // force the new scheme here if (minColor.equals(Color.BLUE) || minColor.equals(MIN_DEFAULT_COLOR_PRE_75)) { ParameterService.setParameterValue(MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MINCOLOR, ParameterTypeColor.color2String(MIN_DEFAULT_COLOR)); fixed = true; } maxColor = getColorFromProperty(MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MAXCOLOR, MAX_DEFAULT_COLOR); // this was the previous default and we cannot detect if the user has ever changed it so we // force the new scheme here if (maxColor.equals(Color.RED) || maxColor.equals(MAX_DEFAULT_COLOR_PRE_75)) { ParameterService.setParameterValue(MainFrame.PROPERTY_RAPIDMINER_GUI_PLOTTER_LEGEND_MAXCOLOR, ParameterTypeColor.color2String(MAX_DEFAULT_COLOR)); fixed = true; } if (fixed) { ParameterService.saveParameters(); } } private boolean reduceBrightness; /** * Creates a new {@link ColorProvider}. */ public ColorProvider() { this(false); } /** * Creates a new {@link ColorProvider} which reduces the brightness of the returned colors. * * @param reduceBrightness * if <code>true</code>, will reduce brightness of returned colors */ public ColorProvider(boolean reduceBrightness) { this.reduceBrightness = reduceBrightness; } public Color getMinLegendColor() { return minColor; } public Color getMaxLegendColor() { return maxColor; } /** * Helper methods which can be used to deliver a value for the point color. For nominal values * with two classes, this method tries to search another column with a name xxx(name) and * changes the color a bit to the opponent color if the values are not the same. This might be * nice for example in case of a predicted value and a real value. */ public double getPointColorValue(DataTable table, DataTableRow row, int column, double min, double max) { double colorValue = row.getValue(column); if (max == min && table.isNominal(column)) { return colorValue / (table.getNumberOfValues(column) - 1); } else { double normalized = (colorValue - min) / (max - min); if (!Double.isNaN(colorValue)) { if (table.isNominal(column) && table.getNumberOfValues(column) == 2) { String columnName = table.getColumnName(column); int startParIndex = columnName.indexOf("(") + 1; if (startParIndex >= 0) { int endParIndex = columnName.indexOf(")", startParIndex); if (endParIndex >= 0) { String otherColumnName = columnName.substring(startParIndex, endParIndex); int otherColumnIndex = table.getColumnIndex(otherColumnName); if (otherColumnIndex >= 0) { if (table.isNominal(otherColumnIndex)) { double compareValue = row.getValue(otherColumnIndex); if (!Double.isNaN(compareValue)) { int compareIndex = (int) compareValue; String compareString = table.mapIndex(otherColumnIndex, compareIndex); compareIndex = table.mapString(column, compareString); if (colorValue != compareIndex) { // both values are different --> change color if (normalized > 0.8) { normalized = 0.8; } else if (normalized < 0.2) { normalized = 0.2; } } } } } } } } } return normalized; } } public Color getPointBorderColor(DataTable table, DataTableRow row, int column) { Color result = Color.BLACK; if (table.isNominal(column)) { // nominal --> try to find compare column double colorValue = row.getValue(column); if (!Double.isNaN(colorValue)) { int colorIndex = (int) colorValue; String columnName = table.getColumnName(column); int startParIndex = columnName.indexOf("(") + 1; if (startParIndex >= 0) { int endParIndex = columnName.indexOf(")", startParIndex); if (endParIndex >= 0) { String otherColumnName = columnName.substring(startParIndex, endParIndex); int otherColumnIndex = table.getColumnIndex(otherColumnName); if (otherColumnIndex >= 0) { if (table.isNominal(otherColumnIndex)) { double compareValue = row.getValue(otherColumnIndex); if (!Double.isNaN(compareValue)) { int compareIndex = (int) compareValue; String compareString = table.mapIndex(otherColumnIndex, compareIndex); compareIndex = table.mapString(column, compareString); if (colorIndex != compareIndex) { // both values are different --> change color result = Color.RED; } } } } } } } } if (reduceBrightness) { return reduceColorBrightness(result); } else { return result; } } /** * Returns a color for the given value. The value must be normalized, i.e. between zero and one. */ public Color getPointColor(double value) { return getPointColor(value, 255); } /** * Returns a color for the given value. The value must be normalized, i.e. between zero and one. * Please note that high alpha values are more transparent. */ public Color getPointColor(double value, int alpha) { if (Double.isNaN(value)) { return Color.LIGHT_GRAY; } Color MIN_LEGEND_COLOR = getMinLegendColor(); Color MAX_LEGEND_COLOR = getMaxLegendColor(); float[] minCol = Color.RGBtoHSB(MIN_LEGEND_COLOR.getRed(), MIN_LEGEND_COLOR.getGreen(), MIN_LEGEND_COLOR.getBlue(), null); float[] maxCol = Color.RGBtoHSB(MAX_LEGEND_COLOR.getRed(), MAX_LEGEND_COLOR.getGreen(), MAX_LEGEND_COLOR.getBlue(), null); // double hColorDiff = 1.0f - 0.68f; double hColorDiff = maxCol[0] - minCol[0]; double sColorDiff = maxCol[1] - minCol[1]; double bColorDiff = maxCol[2] - minCol[2]; // lower brightness to 90% Color color = new Color(Color.HSBtoRGB((float) (minCol[0] + hColorDiff * value), (float) (minCol[1] + value * sColorDiff), (float) (minCol[2] + value * bColorDiff))); if (alpha < 255) { color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); } if (reduceBrightness) { color = reduceColorBrightness(color); } return color; } /** * Returns the original color, just slightly less bright. * * @param color * @return */ public static Color reduceColorBrightness(Color color) { // lower brightness to 85% and saturation to 85% int r, g, b; float[] hsb = new float[3]; r = color.getRed(); g = color.getGreen(); b = color.getBlue(); Color.RGBtoHSB(r, g, b, hsb); // brightness hsb[2] *= 0.80f; // saturation hsb[1] *= 0.85f; return Color.getHSBColor(hsb[0], hsb[1], hsb[2]); } private static Color getColorFromProperty(String propertyName, Color errorColor) { String propertyString = ParameterService.getParameterValue(propertyName); if (propertyString != null) { String[] colors = propertyString.split(","); if (colors.length != 3) { throw new IllegalArgumentException("Color '" + propertyString + "' defined as value for property '" + propertyName + "' is not a vaild color. Colors must be of the form 'r,g,b'."); } else { Color color = new Color(Integer.parseInt(colors[0].trim()), Integer.parseInt(colors[1].trim()), Integer.parseInt(colors[2].trim())); return color; } } else { return errorColor; } } }