Erste Diagrammtypen implementiert
This commit is contained in:
parent
a2d883305a
commit
99fda3c37c
|
@ -0,0 +1,371 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.Color;
|
||||
import schule.ngb.zm.shapes.Rectangle;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class BarChart extends Rectangle {
|
||||
|
||||
public static String DEFAULT_LABEL = "%.2f";
|
||||
|
||||
protected ArrayList<ChartValue> values;
|
||||
|
||||
protected HashMap<ChartValue, Color> colorMap;
|
||||
|
||||
protected double max = -1;
|
||||
|
||||
protected boolean vertical = false, inverted = false, labels = true, labelsInline = false;
|
||||
|
||||
|
||||
public BarChart( double x, double y, double width, double height ) {
|
||||
this(x, y, width, height, -1);
|
||||
}
|
||||
|
||||
public BarChart( double x, double y, double width, double height, double max ) {
|
||||
this(x, y, width, height, max, null);
|
||||
}
|
||||
|
||||
public BarChart( double x, double y, double width, double height, double max, double[] pValues ) {
|
||||
super(x, y, width, height);
|
||||
this.max = max;
|
||||
|
||||
if( pValues != null ) {
|
||||
this.values = new ArrayList<>(pValues.length);
|
||||
for( double value : pValues ) {
|
||||
this.addValue(value);
|
||||
}
|
||||
} else {
|
||||
this.values = new ArrayList<>(10);
|
||||
}
|
||||
|
||||
colorMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public boolean isVertical() {
|
||||
return vertical;
|
||||
}
|
||||
|
||||
public void setVertical( boolean pPillars ) {
|
||||
this.vertical = pPillars;
|
||||
}
|
||||
|
||||
public boolean isInverted() {
|
||||
return inverted;
|
||||
}
|
||||
|
||||
public void setInverted( boolean pInverted ) {
|
||||
this.inverted = pInverted;
|
||||
}
|
||||
|
||||
public boolean isLabelsVisible() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
public void setLabelsVisible( boolean pLabels ) {
|
||||
this.labels = pLabels;
|
||||
}
|
||||
|
||||
public boolean isLabelsInline() {
|
||||
return labelsInline;
|
||||
}
|
||||
|
||||
public void setLabelsInline( boolean pLabelsInline ) {
|
||||
this.labelsInline = pLabelsInline;
|
||||
}
|
||||
|
||||
public double getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void setMax( double pMax ) {
|
||||
this.max = pMax;
|
||||
}
|
||||
|
||||
public ChartValue[] getChartValues() {
|
||||
ChartValue[] ret = new ChartValue[values.size()];
|
||||
return values.toArray(ret);
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
double[] ret = new double[values.size()];
|
||||
int i = 0;
|
||||
for( ChartValue cv : values ) {
|
||||
ret[i++] = cv.getValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void addValue( double pValue ) {
|
||||
addValue(pValue, randomNiceColor());
|
||||
}
|
||||
|
||||
public void addValue( double pValue, Color pColor ) {
|
||||
addValue(pValue, pColor, String.format(DEFAULT_LABEL, pValue));
|
||||
}
|
||||
|
||||
public void addValue( double pValue, Color pColor, String pLabel ) {
|
||||
addValue(new BasicChartValue(pValue, pLabel, pColor));
|
||||
}
|
||||
|
||||
public void addValue( ChartValue pValue ) {
|
||||
values.add(pValue);
|
||||
}
|
||||
|
||||
public void addValues( double[] pValues ) {
|
||||
for( double value : pValues ) {
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addValues( ChartValue[] pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addValues( Collection<ChartValue> pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValue( int i ) {
|
||||
if( i < values.size() ) {
|
||||
values.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValue( ChartValue pValue ) {
|
||||
values.remove(pValue);
|
||||
}
|
||||
|
||||
public void removeValues( ChartValue[] pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
removeValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValues( Collection<ChartValue> pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
removeValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public double getValue( int i ) {
|
||||
return values.get(i).getValue();
|
||||
}
|
||||
|
||||
public void setValue( int i, double pValue ) {
|
||||
values.get(i).setValue(pValue);
|
||||
}
|
||||
|
||||
public boolean containsValue( ChartValue pValue ) {
|
||||
return values.contains(pValue);
|
||||
}
|
||||
|
||||
public String[] getLabels() {
|
||||
String[] ret = new String[values.size()];
|
||||
int i = 0;
|
||||
for( ChartValue cv : values ) {
|
||||
ret[i++] = cv.getLabel();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String getLabel( int i ) {
|
||||
return values.get(i).getLabel();
|
||||
}
|
||||
|
||||
public void setLabel( int i, String pLabel ) {
|
||||
values.get(i).setLabel(pLabel);
|
||||
}
|
||||
|
||||
public void setLabels( String... pLabels ) {
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
if( i < pLabels.length ) {
|
||||
values.get(i).setLabel(pLabels[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color[] getColors() {
|
||||
Color[] ret = new Color[values.size()];
|
||||
int i = 0;
|
||||
for( ChartValue bcv : values ) {
|
||||
ret[i] = bcv.getColor();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setColors( Color... pColors ) {
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
if( i < pColors.length ) {
|
||||
values.get(i).setColor(pColors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color getColor( int i ) {
|
||||
return values.get(i).getColor();
|
||||
}
|
||||
|
||||
public void setColor( int i, Color pColor ) {
|
||||
values.get(i).setColor(pColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
||||
AffineTransform originalTransform = graphics.getTransform();
|
||||
graphics.setTransform(transform);
|
||||
if( vertical ) {
|
||||
drawPillars(graphics);
|
||||
} else {
|
||||
drawBars(graphics);
|
||||
}
|
||||
graphics.setTransform(originalTransform);
|
||||
}
|
||||
|
||||
private void drawBars( Graphics2D graphics ) {
|
||||
double max = this.max;
|
||||
if( max == -1 ) {
|
||||
for( ChartValue cv : values ) {
|
||||
if( cv.getValue() > max ) {
|
||||
max = cv.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double gap = height * .01;
|
||||
double barY = gap * .5;
|
||||
double barHeight = (height - gap * values.size()) / values.size();
|
||||
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
ChartValue cv = values.get(i);
|
||||
double barWidth = (int) round(cv.getValue() * width / max);
|
||||
|
||||
if( cv.getColor() != null ) {
|
||||
graphics.setColor(cv.getColor().getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(getFillColor().getJavaColor());
|
||||
}
|
||||
if( inverted ) {
|
||||
graphics.fillRect((int) (width - barWidth), (int) barY, (int) barWidth, (int) barHeight);
|
||||
} else {
|
||||
graphics.fillRect(0, (int) barY, (int) barWidth, (int) barHeight);
|
||||
}
|
||||
|
||||
if( labels ) {
|
||||
int lWidth = graphics.getFontMetrics().stringWidth(cv.getLabel());
|
||||
int lHeight = graphics.getFontMetrics().getAscent();
|
||||
|
||||
int lX = -10 - lWidth;
|
||||
if( inverted ) {
|
||||
lX = (int) width + 10;
|
||||
}
|
||||
if( labelsInline ) {
|
||||
//lX = (int) (width - barWidth + 10);
|
||||
lX = 10;
|
||||
if( inverted ) {
|
||||
lX = (int) (width - lWidth - 10);
|
||||
}
|
||||
|
||||
if( cv.getColor() != null ) {
|
||||
graphics.setColor(cv.getColor().textcolor().getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(getFillColor().textcolor().getJavaColor());
|
||||
}
|
||||
}
|
||||
int lY = (int) (barY + barHeight * .5 + lHeight * .5);
|
||||
|
||||
graphics.drawString(cv.getLabel(), lX, lY);
|
||||
}
|
||||
|
||||
barY += barHeight + gap;
|
||||
}
|
||||
graphics.setColor(getStrokeColor().getJavaColor());
|
||||
graphics.setStroke(createStroke());
|
||||
if( inverted ) {
|
||||
graphics.drawLine((int) width, 0, (int) width, (int) height);
|
||||
} else {
|
||||
graphics.drawLine((int) 0, (int) 0, (int) 0, (int) height);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPillars( Graphics2D graphics ) {
|
||||
double max = this.max;
|
||||
if( max == -1 ) {
|
||||
for( ChartValue bcv : values ) {
|
||||
if( bcv.getValue() > max ) {
|
||||
max = bcv.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double gap = width * .01;
|
||||
double barX = gap * .5;
|
||||
double barWidth = (width - gap * values.size()) / values.size();
|
||||
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
ChartValue cv = values.get(i);
|
||||
double barHeight = (int) round(cv.getValue() * height / max);
|
||||
|
||||
if( cv.getColor() != null ) {
|
||||
graphics.setColor(cv.getColor().getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(getFillColor().getJavaColor());
|
||||
}
|
||||
if( inverted ) {
|
||||
graphics.fillRect((int) barX, 0, (int) barWidth, (int) barHeight);
|
||||
} else {
|
||||
graphics.fillRect((int) barX, (int) (height - barHeight), (int) barWidth, (int) barHeight);
|
||||
}
|
||||
|
||||
if( labels ) {
|
||||
int lWidth = graphics.getFontMetrics().stringWidth(cv.getLabel());
|
||||
int lHeight = graphics.getFontMetrics().getAscent();
|
||||
|
||||
int lY = (int) (width + 10 + lWidth);
|
||||
if( inverted ) {
|
||||
lY = -10;
|
||||
}
|
||||
if( labelsInline ) {
|
||||
lY = (int) (width - 10);
|
||||
if( inverted ) {
|
||||
lY = 10 + lWidth;
|
||||
}
|
||||
|
||||
if( cv.getColor() != null ) {
|
||||
graphics.setColor(cv.getColor().textcolor().getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(getFillColor().textcolor().getJavaColor());
|
||||
}
|
||||
}
|
||||
int lX = (int) (barX + barWidth * .5 + lHeight * .5);
|
||||
|
||||
AffineTransform tt = graphics.getTransform();
|
||||
graphics.translate(lX, lY);
|
||||
graphics.rotate(-HALF_PI);
|
||||
|
||||
graphics.drawString(cv.getLabel(), 0, 0);
|
||||
|
||||
graphics.setTransform(tt);
|
||||
}
|
||||
|
||||
barX += barWidth + gap;
|
||||
}
|
||||
|
||||
graphics.setColor(getStrokeColor().getJavaColor());
|
||||
graphics.setStroke(createStroke());
|
||||
if( inverted ) {
|
||||
graphics.drawLine(0, 0, (int) width, 0);
|
||||
} else {
|
||||
graphics.drawLine(0, (int) height, (int) width, (int) height);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.Color;
|
||||
|
||||
public class BasicChartValue implements ChartValue {
|
||||
|
||||
private double xValue = 0;
|
||||
|
||||
private double value;
|
||||
|
||||
private String label = null;
|
||||
|
||||
private Color color = null;
|
||||
|
||||
public BasicChartValue( double value ) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public BasicChartValue( double xValue, double value ) {
|
||||
this.xValue = xValue;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public BasicChartValue( double value, String label ) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public BasicChartValue( double xValue, double value, String label ) {
|
||||
this.xValue = xValue;
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public BasicChartValue( double value, Color color ) {
|
||||
this.value = value;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public BasicChartValue( double xValue, double value, Color color ) {
|
||||
this.xValue = xValue;
|
||||
this.value = value;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public BasicChartValue( double value, String label, Color color ) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public BasicChartValue( double xValue, double value, String label, Color color ) {
|
||||
this.xValue = xValue;
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return xValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue( double pValue ) {
|
||||
this.value = pValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel( String pLabel ) {
|
||||
this.label = pLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor( Color pColor ) {
|
||||
this.color = pColor;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.Color;
|
||||
import schule.ngb.zm.Constants;
|
||||
import schule.ngb.zm.Options;
|
||||
import schule.ngb.zm.shapes.Rectangle;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
|
||||
public class ChartAxes extends Rectangle {
|
||||
|
||||
private double xMin = 0.0, xMax = 10.0, xStep = 1.0;
|
||||
|
||||
private double yMin = 0.0, yMax = 10.0, yStep = 1.0;
|
||||
|
||||
private int xTicks = 1, yTicks = 1;
|
||||
|
||||
private Color gridColor = Color.LIGHTGRAY;
|
||||
|
||||
private boolean showArrows = true, showGrid = false;
|
||||
|
||||
public ChartAxes( double x, double y, double width, double height ) {
|
||||
super(x, y, width, height);
|
||||
setAnchor(Options.Direction.CENTER);
|
||||
}
|
||||
|
||||
public void setAxesColor( Color pColor ) {
|
||||
setStrokeColor(pColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
||||
|
||||
double xUnit = width / (xMax - xMin);
|
||||
double yUnit = height / (yMax - yMin);
|
||||
|
||||
AffineTransform originalTransform = graphics.getTransform();
|
||||
graphics.setTransform(transform);
|
||||
|
||||
if( showGrid ) {
|
||||
graphics.setColor(gridColor.getJavaColor());
|
||||
if( xTicks > 0 ) {
|
||||
for( int i = 0; i < (xMax - xMin); i += xTicks ) {
|
||||
graphics.drawLine((int) (i * xUnit), (int) (height), (int) (i * xUnit), (int) (height - (yMax - yMin) * yUnit));
|
||||
}
|
||||
}
|
||||
if( yTicks > 0 ) {
|
||||
for( int i = 0; i < (yMax - yMin); i += yTicks ) {
|
||||
graphics.drawLine(0, (int) (height - i * yUnit), (int) ((xMax - xMin) * xUnit), (int) (height - i * yUnit));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graphics.setColor(strokeColor.getJavaColor());
|
||||
graphics.setStroke(createStroke());
|
||||
graphics.drawLine(0, (int)(height), (int)((xMax-xMin) * xUnit), (int)(height));
|
||||
graphics.drawLine(0, (int)(height), 0, (int)(height - (yMax-yMin) * yUnit));
|
||||
if( showArrows ) {
|
||||
int tipSize = 3;
|
||||
graphics.drawLine((int)((xMax-xMin) * xUnit), (int)(height), (int)((xMax-xMin) * xUnit-tipSize), (int)(height-tipSize));
|
||||
graphics.drawLine((int)((xMax-xMin) * xUnit), (int)(height), (int)((xMax-xMin) * xUnit-tipSize), (int)(height+tipSize));
|
||||
graphics.drawLine(0, (int)(height - (yMax-yMin) * yUnit), -tipSize, (int)(height - (yMax-yMin) * yUnit +tipSize));
|
||||
graphics.drawLine(0, (int)(height - (yMax-yMin) * yUnit), tipSize, (int)(height - (yMax-yMin) * yUnit +tipSize));
|
||||
}
|
||||
|
||||
int tickSize = 3;
|
||||
if( xTicks > 0 ) {
|
||||
for( int i = 0; i < (xMax - xMin); i += xTicks ) {
|
||||
graphics.drawLine((int) (i * xUnit), (int) (height+tickSize), (int) (i * xUnit), (int) (height-tickSize));
|
||||
}
|
||||
}
|
||||
if( yTicks > 0 ) {
|
||||
for( int i = 0; i < (yMax - yMin); i += yTicks ) {
|
||||
graphics.drawLine(-tickSize, (int) (height - i * yUnit), tickSize, (int) (height - i * yUnit));
|
||||
}
|
||||
}
|
||||
|
||||
graphics.setTransform(originalTransform);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.Color;
|
||||
|
||||
/**
|
||||
* Schnittstelle für Datenpunkte, die in einem Diagramm dargestellt werden
|
||||
* sollen. Durch Implementation dieser Schnittstelle kann eine Klasse in den
|
||||
* meisten Diagrammarten dargestllt werden. Einige Diagrammarten benötigen aber
|
||||
* spezialisiertere Schnittstellen, die von dieser abgeleitet werden.
|
||||
*/
|
||||
public interface ChartValue {
|
||||
|
||||
/**
|
||||
* Gibt den x-Wert des Datenpunktes zurück. Nicht alle Diagrammarten
|
||||
* benötigen einen x-Wert und ignorieren diesen. Soll die Klasse nur in
|
||||
* Diagrammen ohne x-Wert (zum Beispiel Kreis- oder Balkendiagramm)
|
||||
* dargestellt werden, wird empfohlen, dass immer 0 zurückgegeben wird.
|
||||
*
|
||||
* @return Der x-Wert des Datenpunktes.
|
||||
*/
|
||||
double getX();
|
||||
|
||||
/**
|
||||
* Gibt den Datenwertert des Datenpunktes zurück.
|
||||
*
|
||||
* @return Der Wert des Datenpunktes.
|
||||
*/
|
||||
double getValue();
|
||||
|
||||
/**
|
||||
* Ändert den Datenwert dieses Datenpunktes.
|
||||
* @param pValue
|
||||
*/
|
||||
void setValue( double pValue );
|
||||
|
||||
/**
|
||||
* Gibt eine Beschriftung für den Datenpunkt zurück. Wird {@code null}
|
||||
* zurückgegeben, dann erstellt das Diagramm automatisch eine Beschriftung,
|
||||
* falls nötig (in der Regel der Datenwert).
|
||||
*
|
||||
* @return Eine Beschriftung.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Ändert die Beschriftung dieses Datenpunktes.
|
||||
* @param pLabel Die neue Beschriftung.
|
||||
*/
|
||||
void setLabel( String pLabel );
|
||||
|
||||
/**
|
||||
* Gibt eine Farbe für den Datenpunkt zurück. Wird {@code null}
|
||||
* zurückgegeben, dann wählt das Diagramm automatisch eine Farbe für die
|
||||
* Darstellung.
|
||||
*
|
||||
* @return Eine Farbe.
|
||||
*/
|
||||
Color getColor();
|
||||
|
||||
/**
|
||||
* Ändert die Farbe dieses Datenpunktes.
|
||||
* @param pColor Die neue Farbe.
|
||||
*/
|
||||
void setColor( Color pColor );
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.shapes.Rectangle;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.Collection;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class LineChart extends Rectangle {
|
||||
|
||||
private class LineChartValue {
|
||||
|
||||
double x;
|
||||
|
||||
double value;
|
||||
|
||||
public LineChartValue( double x, double value ) {
|
||||
this.x = x;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected SortedMap<Double, LineChartValue> values;
|
||||
|
||||
protected boolean drawLines = true;
|
||||
|
||||
public LineChart( double x, double y, double width, double height ) {
|
||||
this(x, y, width, height, null, null);
|
||||
}
|
||||
|
||||
public LineChart( double x, double y, double width, double height, double[] pCoordinates, double[] pValues ) {
|
||||
super(x, y, width, height);
|
||||
|
||||
this.values = new TreeMap<Double, LineChartValue>();
|
||||
if( pCoordinates != null || pValues != null ) {
|
||||
if( pCoordinates == null ) {
|
||||
pCoordinates = new double[0];
|
||||
}
|
||||
if( pValues == null ) {
|
||||
pCoordinates = new double[0];
|
||||
}
|
||||
for( int i = 0; i < max(pCoordinates.length, pValues.length); i++ ) {
|
||||
if( i < pValues.length && i < pCoordinates.length ) {
|
||||
addValue(pCoordinates[i], pValues[i]);
|
||||
} else if( i < pCoordinates.length ) {
|
||||
addValue(pCoordinates[i], 0.0);
|
||||
} else {
|
||||
addValue(0.0, pValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addValue( double pCoordinate, double pValue ) {
|
||||
values.put(pCoordinate, new LineChartValue(pCoordinate, pValue));
|
||||
}
|
||||
|
||||
public void removeValue( double x ) {
|
||||
values.remove(x);
|
||||
}
|
||||
|
||||
public double getValue( double x ) {
|
||||
return values.get(x).value;
|
||||
}
|
||||
|
||||
public void setValue( double x, double pValue ) {
|
||||
values.get(x).value = pValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
||||
double xMax = values.lastKey();
|
||||
double yMax = Double.MIN_VALUE;
|
||||
|
||||
Collection<LineChartValue> val = values.values();
|
||||
for( LineChartValue lcv : val ) {
|
||||
if( lcv.value > yMax ) {
|
||||
yMax = lcv.value;
|
||||
}
|
||||
}
|
||||
|
||||
double xUnit = width/xMax;
|
||||
double yUnit = height/yMax;
|
||||
|
||||
AffineTransform originalTransform = graphics.getTransform();
|
||||
graphics.setTransform(transform);
|
||||
|
||||
drawAxes(graphics, xUnit, yUnit);
|
||||
|
||||
LineChartValue lastLcv = null;
|
||||
for( LineChartValue lcv : val ) {
|
||||
if( drawLines && lastLcv != null ) {
|
||||
graphics.setColor(getStrokeColor().getJavaColor());
|
||||
graphics.setStroke(createStroke());
|
||||
graphics.drawLine((int)(lastLcv.x*xUnit), (int)(height - lastLcv.value*yUnit), (int)(lcv.x*xUnit), (int)(height - lcv.value*yUnit));
|
||||
drawDot(graphics, lastLcv, xUnit, yUnit);
|
||||
}
|
||||
|
||||
drawDot(graphics, lcv, xUnit, yUnit);
|
||||
lastLcv = lcv;
|
||||
}
|
||||
|
||||
graphics.setTransform(originalTransform);
|
||||
}
|
||||
|
||||
private void drawDot( Graphics2D graphics, LineChartValue lcv, double xUnit, double yUnit ) {
|
||||
int dotSize = (int) round(strokeWeight * 2);
|
||||
graphics.setColor(getFillColor().getJavaColor());
|
||||
graphics.fillRect((int)(lcv.x*xUnit - dotSize), (int)(height - lcv.value*yUnit - dotSize), dotSize+dotSize, dotSize+dotSize);
|
||||
}
|
||||
|
||||
private void drawAxes( Graphics2D graphics, double xUnit, double yUnit ) {
|
||||
graphics.setColor(BLACK.getJavaColor());
|
||||
graphics.setStroke(new BasicStroke());
|
||||
|
||||
graphics.drawLine(0, (int)height, 0, 0);
|
||||
graphics.drawLine(0, (int)height, (int)width, (int)height);
|
||||
|
||||
for( double i = xUnit; i <= width; i += xUnit ) {
|
||||
int xx = (int)i;
|
||||
graphics.drawLine(xx, (int)(height-2), xx, (int)(height+2));
|
||||
}
|
||||
for( double i = height-yUnit; i >= 0; i -= yUnit ) {
|
||||
int yy = (int)i;
|
||||
graphics.drawLine(-2, yy, 2, yy);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.Color;
|
||||
import schule.ngb.zm.shapes.Circle;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PieChart extends Circle {
|
||||
|
||||
public static String DEFAULT_LABEL = "%.2f";
|
||||
|
||||
protected ArrayList<ChartValue> values;
|
||||
|
||||
protected HashMap<ChartValue, Color> colorMap;
|
||||
|
||||
protected double sum = 0;
|
||||
|
||||
protected boolean clockwise = true, labels = true, labelsInline = false;
|
||||
|
||||
public PieChart( double x, double y, double radius ) {
|
||||
this(x, y, radius, null);
|
||||
}
|
||||
|
||||
public PieChart( double x, double y, double radius, double[] pValues ) {
|
||||
super(x, y, radius);
|
||||
|
||||
if( pValues != null ) {
|
||||
this.values = new ArrayList<>(pValues.length);
|
||||
|
||||
for( double value : pValues ) {
|
||||
this.addValue(value);
|
||||
sum += value;
|
||||
}
|
||||
} else {
|
||||
this.values = new ArrayList<>(10);
|
||||
}
|
||||
|
||||
colorMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public double getSum() {
|
||||
return sum;
|
||||
}
|
||||
|
||||
public boolean isClockwise() {
|
||||
return clockwise;
|
||||
}
|
||||
|
||||
public void setClockwise( boolean pClockwise ) {
|
||||
this.clockwise = pClockwise;
|
||||
}
|
||||
|
||||
public boolean isLabelsVisible() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
public void setLabelsVisible( boolean pLabels ) {
|
||||
this.labels = pLabels;
|
||||
}
|
||||
|
||||
public boolean isLabelsInline() {
|
||||
return labelsInline;
|
||||
}
|
||||
|
||||
public void setLabelsInline( boolean pLabelsInline ) {
|
||||
this.labelsInline = pLabelsInline;
|
||||
}
|
||||
|
||||
public ChartValue[] getChartValues() {
|
||||
ChartValue[] ret = new ChartValue[values.size()];
|
||||
return values.toArray(ret);
|
||||
}
|
||||
|
||||
public double[] getValues() {
|
||||
double[] ret = new double[values.size()];
|
||||
int i = 0;
|
||||
for( ChartValue cv : values ) {
|
||||
ret[i++] = cv.getValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void addValue( double pValue ) {
|
||||
addValue(pValue, randomNiceColor());
|
||||
}
|
||||
|
||||
public void addValue( double pValue, Color pColor ) {
|
||||
addValue(pValue, String.format(DEFAULT_LABEL, pValue), pColor);
|
||||
}
|
||||
|
||||
public void addValue( double pValue, String pLabel, Color pColor ) {
|
||||
addValue(new BasicChartValue(pValue, pLabel, pColor));
|
||||
}
|
||||
|
||||
public void addValue( ChartValue pValue ) {
|
||||
values.add(pValue);
|
||||
sum += pValue.getValue();
|
||||
}
|
||||
|
||||
public void addValues( double[] pValues ) {
|
||||
for( double value : pValues ) {
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addValues( ChartValue[] pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addValues( Collection<ChartValue> pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValue( int i ) {
|
||||
if( i < values.size() ) {
|
||||
ChartValue pcv = values.remove(i);
|
||||
if( pcv != null ) {
|
||||
sum -= pcv.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValue( ChartValue pValue ) {
|
||||
if( values.remove(pValue) ) {
|
||||
sum -= pValue.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValues( ChartValue[] pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
removeValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValues( Collection<ChartValue> pValues ) {
|
||||
for( ChartValue value : pValues ) {
|
||||
removeValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public double getValue( int i ) {
|
||||
return values.get(i).getValue();
|
||||
}
|
||||
|
||||
public void setValue( int i, double pValue ) {
|
||||
values.get(i).setValue(pValue);
|
||||
}
|
||||
|
||||
public boolean containsValue( ChartValue pValue ) {
|
||||
return values.contains(pValue);
|
||||
}
|
||||
|
||||
public String[] getLabels() {
|
||||
String[] ret = new String[values.size()];
|
||||
int i = 0;
|
||||
for( ChartValue cv : values ) {
|
||||
ret[i++] = cv.getLabel();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String getLabel( int i ) {
|
||||
return values.get(i).getLabel();
|
||||
}
|
||||
|
||||
public void setLabel( int i, String pLabel ) {
|
||||
values.get(i).setLabel(pLabel);
|
||||
}
|
||||
|
||||
public void setLabels( String... pLabels ) {
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
if( i < pLabels.length ) {
|
||||
values.get(i).setLabel(pLabels[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color[] getColors() {
|
||||
Color[] ret = new Color[values.size()];
|
||||
int i = 0;
|
||||
for( ChartValue bcv : values ) {
|
||||
ret[i] = bcv.getColor();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setColors( Color... pColors ) {
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
if( i < pColors.length ) {
|
||||
values.get(i).setColor(pColors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color getColor( int i ) {
|
||||
return values.get(i).getColor();
|
||||
}
|
||||
|
||||
public void setColor( int i, Color pColor ) {
|
||||
values.get(i).setColor(pColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
||||
AffineTransform originalTransform = graphics.getTransform();
|
||||
graphics.setTransform(transform);
|
||||
|
||||
int startAngle = 0;
|
||||
for( int i = 0; i < values.size(); i++ ) {
|
||||
ChartValue pcv;
|
||||
if( clockwise ) {
|
||||
pcv = values.get(values.size() - i - 1);
|
||||
} else {
|
||||
pcv = values.get(i);
|
||||
}
|
||||
int angle = 360 - startAngle;
|
||||
if( i < values.size() - 1 ) {
|
||||
angle = (int) round(pcv.getValue() * 360.0 / sum);
|
||||
}
|
||||
|
||||
if( pcv.getColor() == null ) {
|
||||
if( !colorMap.containsKey(pcv) ) {
|
||||
colorMap.put(pcv, randomNiceColor());
|
||||
}
|
||||
graphics.setColor(colorMap.get(pcv).getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(pcv.getColor().getJavaColor());
|
||||
}
|
||||
graphics.fillArc(0, 0, (int) (radius * 2), (int) (radius * 2), startAngle, angle);
|
||||
|
||||
if( labels ) {
|
||||
String label = pcv.getLabel();
|
||||
if( label == null ) {
|
||||
label = String.format("%.2f", pcv.getValue());
|
||||
}
|
||||
|
||||
int lWidth = graphics.getFontMetrics().stringWidth(label);
|
||||
int lHeight = graphics.getFontMetrics().getAscent();
|
||||
|
||||
double heading = radians(startAngle + angle * .5);
|
||||
|
||||
boolean onLeft = (heading > HALF_PI && heading < PI + HALF_PI);
|
||||
|
||||
|
||||
AffineTransform tt = graphics.getTransform();
|
||||
graphics.translate(radius, radius);
|
||||
|
||||
|
||||
if( labelsInline ) {
|
||||
if( pcv.getColor() == null ) {
|
||||
graphics.setColor(colorMap.get(pcv).textcolor().getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(pcv.getColor().textcolor().getJavaColor());
|
||||
}
|
||||
|
||||
if( onLeft ) {
|
||||
graphics.rotate(-heading - PI);
|
||||
graphics.translate(-radius + 10, lHeight * .5);
|
||||
} else {
|
||||
graphics.rotate(-heading);
|
||||
graphics.translate(radius - 10 - lWidth, lHeight * .5);
|
||||
}
|
||||
} else {
|
||||
double lX = (radius + 10) * Math.cos(-heading);
|
||||
double lY = (radius + 10) * Math.sin(-heading);
|
||||
|
||||
if( onLeft ) {
|
||||
graphics.translate(lX - lWidth, lY + lHeight * .5);
|
||||
} else {
|
||||
graphics.translate(lX, lY + lHeight * .5);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if( labelsInline ) {
|
||||
lX *= (radius - 10);
|
||||
lY *= (radius - 10);
|
||||
if( lX > 0 ) {
|
||||
lX -= lWidth;
|
||||
}
|
||||
if( lY < 0 ) {
|
||||
lY += lHeight;
|
||||
}
|
||||
|
||||
if( pcv.getColor() == null ) {
|
||||
graphics.setColor(colorMap.get(pcv).textcolor().getJavaColor());
|
||||
} else {
|
||||
graphics.setColor(pcv.getColor().textcolor().getJavaColor());
|
||||
}
|
||||
|
||||
graphics.translate(radius, radius);
|
||||
graphics.fillOval(0, 0, 5, 6);
|
||||
graphics.rotate(-Math.atan2(lY, lX));
|
||||
} else {
|
||||
double heading = Math.atan2(lY, lX);
|
||||
if( lX < 0 ) {
|
||||
graphics.translate(-lWidth, 0);
|
||||
}
|
||||
if( lY > 0 ) {
|
||||
graphics.translate(0, lHeight);
|
||||
}
|
||||
|
||||
graphics.rotate(Math.atan2(lY, lX));
|
||||
if( lX < 0 ) {
|
||||
graphics.translate(-radius - 10, 0);
|
||||
} else {
|
||||
graphics.translate(radius + 10, 0);
|
||||
}
|
||||
graphics.fillOval(0, 0, 5, 6);
|
||||
}
|
||||
*/
|
||||
|
||||
graphics.drawString(label, 0, 0);
|
||||
graphics.setTransform(tt);
|
||||
}
|
||||
|
||||
startAngle += angle;
|
||||
}
|
||||
|
||||
graphics.setColor(getStrokeColor().getJavaColor());
|
||||
graphics.setStroke(createStroke());
|
||||
graphics.drawOval(0, 0, (int) (radius * 2), (int) (radius * 2));
|
||||
|
||||
graphics.setTransform(originalTransform);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package schule.ngb.zm.charts;
|
||||
|
||||
import schule.ngb.zm.Color;
|
||||
import schule.ngb.zm.shapes.Circle;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RingChart extends Circle {
|
||||
|
||||
public static String DEFAULT_LABEL = "%.2f";
|
||||
|
||||
public interface RingChartValue extends ChartValue {
|
||||
|
||||
double getMax();
|
||||
|
||||
}
|
||||
|
||||
private class BasicRingChartValue extends BasicChartValue implements RingChartValue {
|
||||
public BasicRingChartValue( double maxValue, double value, String label, Color color ) {
|
||||
super(maxValue, value, label, color);
|
||||
}
|
||||
|
||||
public double getMax() {
|
||||
return getX();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected RingChartValue[] values;
|
||||
|
||||
public RingChart( double x, double y, double radius ) {
|
||||
this(x, y, radius, 3);
|
||||
}
|
||||
|
||||
public RingChart( double x, double y, double radius, int initalSize ) {
|
||||
super(x, y, radius);
|
||||
this.values = new RingChartValue[initalSize];
|
||||
}
|
||||
|
||||
|
||||
public void setValue( int pRing, RingChartValue pValue ) {
|
||||
if( pRing < values.length ) {
|
||||
values[pRing] = pValue;
|
||||
} else {
|
||||
throw new ArrayIndexOutOfBoundsException("Cannot set ring " + pRing + " of " + values.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue( int pRing, double pValue, double pMax ) {
|
||||
setValue(pRing, new BasicRingChartValue(pMax, pValue, String.format(DEFAULT_LABEL, pValue), randomNiceColor()));
|
||||
}
|
||||
|
||||
public void setValue( int pRing, double pValue, double pMax, String pLabel ) {
|
||||
setValue(pRing, new BasicRingChartValue(pMax, pValue, pLabel, randomNiceColor()));
|
||||
}
|
||||
|
||||
public void setValue( int pRing, double pValue, double pMax, String pLabel, Color pColor ) {
|
||||
setValue(pRing, new BasicRingChartValue(pMax, pValue, pLabel, pColor));
|
||||
}
|
||||
|
||||
public void setColors( Color... pColors ) {
|
||||
for( int i = 0; i < values.length; i++ ) {
|
||||
if( i < pColors.length ) {
|
||||
values[i].setColor(pColors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setColor( int i, Color pColor ) {
|
||||
values[i].setColor(pColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
||||
AffineTransform originalTransform = graphics.getTransform();
|
||||
graphics.setTransform(transform);
|
||||
|
||||
double len = values.length + 1;
|
||||
double gap = radius * 0.02;
|
||||
|
||||
//strokeWeight = radius/len;
|
||||
//strokeWeight = ((radius - 2 * values.length) - gap) / len;
|
||||
int outline = 1;
|
||||
strokeWeight = (radius - gap * values.length) / len;
|
||||
|
||||
Stroke outlineStroke = new BasicStroke(
|
||||
(float) strokeWeight,
|
||||
BasicStroke.CAP_ROUND,
|
||||
BasicStroke.JOIN_ROUND);
|
||||
Stroke ringStroke = new BasicStroke(
|
||||
(float) (strokeWeight - 2*outline),
|
||||
BasicStroke.CAP_ROUND,
|
||||
BasicStroke.JOIN_ROUND);
|
||||
|
||||
|
||||
int startAngle = 90;
|
||||
for( int i = 0; i < values.length; i++ ) {
|
||||
RingChartValue rcv = values[values.length - 1 - i];
|
||||
if( rcv != null ) {
|
||||
/*int ringX = (int) (-((i + 1) * strokeWeight));
|
||||
int ringY = (int) (-((i + 1) * strokeWeight) - (i * gap));
|
||||
int ringR = (int) ((i + 1) * strokeWeight * 2); */
|
||||
int ringX = (int) (i * (strokeWeight + gap));
|
||||
int ringY = (int) (i * (strokeWeight + gap));
|
||||
int ringR = (int) ((values.length - i) * (strokeWeight + gap) * 2);
|
||||
int angle = (int) round(rcv.getValue() * 360.0 / rcv.getMax());
|
||||
|
||||
if( rcv.getValue() > rcv.getMax() ) {
|
||||
graphics.setStroke(outlineStroke);
|
||||
graphics.setColor(strokeColor.getJavaColor());
|
||||
graphics.drawArc(ringX, ringY, ringR, ringR, startAngle-angle+360, -60);
|
||||
|
||||
graphics.setStroke(ringStroke);
|
||||
graphics.setColor(rcv.getColor().getJavaColor());
|
||||
graphics.drawArc(ringX, ringY, ringR, ringR, startAngle-angle+360, -60);
|
||||
|
||||
graphics.setStroke(outlineStroke);
|
||||
graphics.setColor(strokeColor.getJavaColor());
|
||||
graphics.drawArc(ringX, ringY, ringR, ringR, startAngle-angle+330, -330);
|
||||
|
||||
graphics.setStroke(ringStroke);
|
||||
graphics.setColor(rcv.getColor().getJavaColor());
|
||||
graphics.drawArc(ringX, ringY, ringR, ringR, startAngle-angle+340, -340);
|
||||
} else {
|
||||
graphics.setStroke(outlineStroke);
|
||||
graphics.setColor(strokeColor.getJavaColor());
|
||||
graphics.drawArc(ringX, ringY, ringR, ringR, startAngle, -angle);
|
||||
|
||||
graphics.setStroke(ringStroke);
|
||||
graphics.setColor(rcv.getColor().getJavaColor());
|
||||
graphics.drawArc(ringX, ringY+outline, ringR, ringR, startAngle, -angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graphics.setTransform(originalTransform);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue