Erste Diagrammtypen implementiert

This commit is contained in:
ngb 2022-06-19 22:28:09 +02:00
parent a2d883305a
commit 99fda3c37c
7 changed files with 1225 additions and 0 deletions

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 );
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}