/*
* RapidMiner
*
* Copyright (C) 2001-2014 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.new_plotter.engine.jfreechart;
import java.awt.Color;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import com.rapidminer.gui.new_plotter.configuration.DefaultDimensionConfig;
import com.rapidminer.gui.new_plotter.configuration.DimensionConfig.PlotDimension;
import com.rapidminer.gui.new_plotter.configuration.GroupCellKey;
import com.rapidminer.gui.new_plotter.configuration.SeriesFormat;
import com.rapidminer.gui.new_plotter.configuration.ValueSource.SeriesUsageType;
import com.rapidminer.gui.new_plotter.data.GroupCellData;
import com.rapidminer.gui.new_plotter.data.GroupCellKeyAndData;
import com.rapidminer.gui.new_plotter.data.GroupCellSeriesData;
import com.rapidminer.gui.new_plotter.data.PlotInstance;
import com.rapidminer.gui.new_plotter.data.ValueSourceData;
import com.rapidminer.gui.new_plotter.listener.RenderFormatDelegateChangeListener;
import com.rapidminer.gui.new_plotter.listener.SeriesFormatListener;
import com.rapidminer.gui.new_plotter.listener.events.SeriesFormatChangeEvent;
import com.rapidminer.gui.new_plotter.utility.ColorProvider;
import com.rapidminer.gui.new_plotter.utility.DataStructureUtils;
import com.rapidminer.gui.new_plotter.utility.ShapeProvider;
import com.rapidminer.gui.new_plotter.utility.SizeProvider;
import com.rapidminer.gui.new_plotter.utility.ValueRange;
/**
* Provides item formatting like color, shape etc. for rendering.
*
* Colors:
* There are two possibilities: either, a series color can be set which is returned
* for all items in a series. Alternatively a series ColorProvider can be set, which
* is then used to retrieve colors for each value in each series. To retrieve colors
* for values, this class must be able to access the values. Thus, if a ColorProvider
* for a series is set, also the series values must be set via setSeriesValues().
*
* If a series color is set, it has precedence over a ColorProvider for the same series.
*
* The same applies for Shapes.
*
* @author Marius Helf, Nils Woehler
*
*/
public class RenderFormatDelegate implements SeriesFormatListener {
private List<RenderFormatDelegateChangeListener> listeners = new LinkedList<RenderFormatDelegateChangeListener>();
private Vector<GroupCellKey> groupCellKeysBySeriesIdx;
private SeriesFormat seriesFormat;
private ColorProvider colorProvider;
private ShapeProvider shapeProvider;
private boolean individualColorForEachItem;
private boolean seriesColorFromDimensionConfig;
private Vector<double[]> colorValues;
private boolean individualShapeForEachItem;
private boolean seriesShapeFromDimensionConfig;
private Vector<double[]> shapeValues;
private boolean individualSizeForEachItem;
private boolean seriesSizeFromDimensionConfig;
private Vector<double[]> sizeValues;
private Vector<double[]> selectedValues;
private SizeProvider sizeProvider;
public RenderFormatDelegate() {
super();
this.groupCellKeysBySeriesIdx = null;
}
public RenderFormatDelegate(ValueSourceData valueSourceData, PlotInstance plotInstance) {
super();
setConfiguration(valueSourceData, plotInstance);
}
/**
* If a series color for seriesIdx is set, returns that color.
* Otherwise returns null.
*/
public Paint getSeriesFillPaint(int seriesIdx) {
if (!individualColorForEachItem) {
if (colorProvider != null) {
if (seriesColorFromDimensionConfig) {
double colorIdx = getValueOfCurrentRange(seriesIdx, PlotDimension.COLOR);
Color seriesColor = colorProvider.getColorForValue(colorIdx);
seriesColor = DataStructureUtils.setColorAlpha(seriesColor, DataStructureUtils.multiplyOpacities256(seriesColor.getAlpha(), seriesFormat.getOpacity()));
Paint seriesPaint = seriesFormat.getAreaFillPaint(seriesColor);
return seriesPaint;
}
}
return seriesFormat.getAreaFillPaint();
} else {
return null;
}
}
/**
* Returns the paint to be used for drawing the item valueIdx in series seriesIdx.
*/
public Paint getItemPaint(int seriesIdx, int valueIdx) {
boolean selected = isItemSelected(seriesIdx, valueIdx);
if (!individualColorForEachItem) {
if (colorProvider != null) {
if (seriesColorFromDimensionConfig) {
double colorIdx = getValueOfCurrentRange(seriesIdx, PlotDimension.COLOR);
Color seriesColor = colorProvider.getColorForValue(colorIdx);
seriesColor = DataStructureUtils.setColorAlpha(seriesColor, DataStructureUtils.multiplyOpacities256(seriesColor.getAlpha(), seriesFormat.getOpacity()));
if (!selected) {
// item not selected? Lower alpha
seriesColor = DataStructureUtils.setColorAlpha(seriesColor, 20);
}
Paint seriesPaint = seriesFormat.getAreaFillPaint(seriesColor);
return seriesPaint;
}
}
Color color = seriesFormat.getItemColor();
if (!selected) {
// item not selected? Lower alpha
color = DataStructureUtils.setColorAlpha(color, 20);
}
return seriesFormat.getAreaFillPaint(color);
}
if (colorProvider == null) {
return null;
} else {
double value = getItemValue(seriesIdx, PlotDimension.COLOR, valueIdx);
Color itemColor = colorProvider.getColorForValue(value);
itemColor = DataStructureUtils.setColorAlpha(itemColor, DataStructureUtils.multiplyOpacities256(itemColor.getAlpha(), seriesFormat.getOpacity()));
if (!selected) {
// item not selected? Lower alpha
itemColor = DataStructureUtils.setColorAlpha(itemColor, 20);
}
Paint paint = seriesFormat.getAreaFillPaint(itemColor);
return paint;
}
}
/**
* Returns if the given item is selected (aka value of 1 in the {@link PlotDimension#SELECTED} column)
* @param seriesIdx
* @param valueIdx
* @return
*/
public boolean isItemSelected(int seriesIdx, int valueIdx) {
double selectedValue = getItemValue(seriesIdx, PlotDimension.SELECTED, valueIdx);
boolean selected = (selectedValue == 1) ? true : false;
return selected;
}
private double getItemValue(int seriesIdx, PlotDimension dimension, int valueIdx) {
switch (dimension) {
case COLOR:
return colorValues.get(seriesIdx)[valueIdx];
case SHAPE:
return shapeValues.get(seriesIdx)[valueIdx];
case SIZE:
return sizeValues.get(seriesIdx)[valueIdx];
case SELECTED:
return selectedValues.get(seriesIdx)[valueIdx];
default:
throw new IllegalArgumentException("getItemValue called for dimension " + dimension + " which is unsupported by RenderFormatDelegate - this should not happen.");
}
}
public Shape getItemShape(int seriesIdx, int valueIdx) {
// get shape
Shape shape;
if (!individualShapeForEachItem) {
Shape seriesShape;
if (seriesShapeFromDimensionConfig) {
double shapeIdx = getValueOfCurrentRange(seriesIdx, PlotDimension.SHAPE);
seriesShape = shapeProvider.getShapeForCategory(shapeIdx);
} else {
seriesShape = seriesFormat.getItemShape().getShape();
}
shape = seriesShape;
} else if (shapeProvider == null) {
return null;
} else {
double shapeValue = getItemValue(seriesIdx, PlotDimension.SHAPE, valueIdx);
// get shape
Shape itemShape = shapeProvider.getShapeForCategory(shapeValue);
shape = itemShape;
}
// scale shape
double scalingFactor;
if (!individualSizeForEachItem) {
if (seriesSizeFromDimensionConfig) {
// calculate series size
double sizeIdx = getValueOfCurrentRange(seriesIdx, PlotDimension.SIZE);
scalingFactor = sizeProvider.getScalingFactorForValue(sizeIdx);
} else {
scalingFactor = seriesFormat.getItemSize();
}
} else if (sizeProvider == null) {
return null;
} else {
double sizeValue = getItemValue(seriesIdx, PlotDimension.SIZE, valueIdx);
scalingFactor = sizeProvider.getScalingFactorForValue(sizeValue);
}
shape = scaleShape(shape, scalingFactor);
return shape;
}
/**
* Scales a shape according to the scaling factor for value
* (given by the sizeProvider if one such exists).
* If no sizeProvider exists, the shape is returned unmodified.
*/
private Shape scaleShape(Shape shape, double scalingFactor) {
// scale shape if necessary
if (scalingFactor != 1) {
AffineTransform t = new AffineTransform();
t.scale(scalingFactor, scalingFactor);
shape = t.createTransformedShape(shape);
}
return shape;
}
/**
* Sets the configuration of this {@link RenderFormatDelegate}. null arguments are not allowed.
*
* @param valueSourceData The {@link ValueSourceData} for which this {@link RenderFormatDelegate} provides the format.
* @param plotInstance The {@link PlotInstance}.
*/
public void setConfiguration(ValueSourceData valueSourceData, PlotInstance plotInstance) {
// retrieve colorProvider
DefaultDimensionConfig colorDimensionConfig = (DefaultDimensionConfig)plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.COLOR);
if (colorDimensionConfig != null) {
colorProvider = plotInstance.getPlotData().getDimensionConfigData(colorDimensionConfig).getColorProvider();
} else {
colorProvider = null;
}
// retrieve shapeProvider
DefaultDimensionConfig shapeDimensionConfig = (DefaultDimensionConfig)plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.SHAPE);
if (shapeDimensionConfig != null) {
shapeProvider = plotInstance.getPlotData().getDimensionConfigData(shapeDimensionConfig).getShapeProvider();
} else {
shapeProvider = null;
}
// retrieve sizeProvider
DefaultDimensionConfig sizeDimensionConfig = (DefaultDimensionConfig)plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.SIZE);
if (sizeDimensionConfig != null) {
sizeProvider = plotInstance.getPlotData().getDimensionConfigData(sizeDimensionConfig).getSizeProvider();
} else {
sizeProvider = null;
}
// set format properties (for whole series or for individual items; from dimension config or from value source config)
individualColorForEachItem = SeriesFormat.calculateIndividualFormatForEachItem(valueSourceData.getValueSource().getDomainConfig(), colorDimensionConfig);
seriesColorFromDimensionConfig = SeriesFormat.useSeriesFormatFromDimensionConfig(valueSourceData.getValueSource().getDomainConfig(), colorDimensionConfig);
individualShapeForEachItem = SeriesFormat.calculateIndividualFormatForEachItem(valueSourceData.getValueSource().getDomainConfig(), shapeDimensionConfig);
seriesShapeFromDimensionConfig = SeriesFormat.useSeriesFormatFromDimensionConfig(valueSourceData.getValueSource().getDomainConfig(), shapeDimensionConfig);
individualSizeForEachItem = SeriesFormat.calculateIndividualFormatForEachItem(valueSourceData.getValueSource().getDomainConfig(), sizeDimensionConfig);
seriesSizeFromDimensionConfig = SeriesFormat.useSeriesFormatFromDimensionConfig(valueSourceData.getValueSource().getDomainConfig(), sizeDimensionConfig);
// retrieve series format
seriesFormat = valueSourceData.getValueSource().getSeriesFormat();
// copy series values if necessary
if (individualColorForEachItem) {
colorValues = copySeriesValues(valueSourceData, PlotDimension.COLOR);
}
if (individualShapeForEachItem) {
shapeValues = copySeriesValues(valueSourceData, PlotDimension.SHAPE);
}
if (individualSizeForEachItem) {
sizeValues = copySeriesValues(valueSourceData, PlotDimension.SIZE);
}
selectedValues = copySeriesValues(valueSourceData, PlotDimension.SELECTED);
// copy group values if necessary
if (seriesColorFromDimensionConfig || seriesShapeFromDimensionConfig || seriesSizeFromDimensionConfig) {
GroupCellSeriesData seriesDataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();
groupCellKeysBySeriesIdx = new Vector<GroupCellKey>(seriesDataForAllGroupCells.groupCellCount());
for (GroupCellKeyAndData groupCellKeyAndData : seriesDataForAllGroupCells) {
groupCellKeysBySeriesIdx.add(groupCellKeyAndData.getKey());
}
}
fireChanged();
}
private Vector<double[]> copySeriesValues(ValueSourceData valueSourceData, PlotDimension dimension) {
Vector<double[]> seriesValues = new Vector<double[]>(valueSourceData.getSeriesDataForAllGroupCells().groupCellCount());
for (GroupCellKeyAndData groupCellKeyAndData : valueSourceData.getSeriesDataForAllGroupCells()) {
GroupCellData groupCellData = groupCellKeyAndData.getData();
double[] seriesData = new double[groupCellData.getSize()];
int i = 0;
for (double d : groupCellData.getDataForUsageType(SeriesUsageType.MAIN_SERIES).get(dimension)) {
seriesData[i] = d;
++i;
}
seriesValues.add(seriesData);
}
return seriesValues;
}
public void addListener(RenderFormatDelegateChangeListener l) {
listeners.add(l);
}
public void removeListener(RenderFormatDelegateChangeListener l) {
listeners.remove(l);
}
private void fireChanged() {
for (RenderFormatDelegateChangeListener l : listeners) {
l.renderFormatDelegateChanged(this);
}
}
@Override
public void seriesFormatChanged(SeriesFormatChangeEvent e) {
fireChanged();
}
public Color getSeriesColor(int seriesIdx) {
if (!individualColorForEachItem){
if (seriesColorFromDimensionConfig) {
double colorIdx = getValueOfCurrentRange(seriesIdx, PlotDimension.COLOR);
int opacity = seriesFormat.getOpacity();
Color color = colorProvider.getColorForValue(colorIdx);
color = DataStructureUtils.setColorAlpha(color, DataStructureUtils.multiplyOpacities256(color.getAlpha(), opacity));
return color;
} else {
return seriesFormat.getItemColor();
}
} else {
return null;
}
}
public SeriesFormat getSeriesFormat() {
return seriesFormat;
}
private double getValueOfCurrentRange(int seriesIdx, PlotDimension dimension) {
ValueRange currentValueRange = groupCellKeysBySeriesIdx.get(seriesIdx).getRangeForDimension(dimension);
if (currentValueRange == null) {
return Double.NaN;
}
double value = currentValueRange.getValue();
return value;
}
public Paint getItemOutlinePaint(int seriesIdx, int valueIdx) {
// TODO Auto-generated method stub
return null;
}
}