Diagram: Work in Progress

thobaben_diagram
Harald Wolff 2016-04-28 16:29:16 +02:00
parent f1345de8ce
commit 5c3b2f81d9
13 changed files with 633 additions and 79 deletions

View File

@ -1,5 +1,6 @@
package org.hwo.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
@ -16,7 +17,7 @@ public class JDiagram extends JPanel {
public JDiagram(){
setMinimumSize(new Dimension(80, 80));
diagram = new Diagram();
getDiagram();
diagram.setFont( getFont() );
}
@ -26,6 +27,8 @@ public class JDiagram extends JPanel {
}
public Diagram getDiagram() {
if (diagram==null)
diagram = new Diagram();
return diagram;
}
public void setDiagram(Diagram diagram) {
@ -57,4 +60,24 @@ public class JDiagram extends JPanel {
super.setFont(font);
}
@Override
public Color getBackground() {
return getDiagram().getBackground();
}
@Override
public void setBackground(Color bg) {
getDiagram().setBackground(bg);
repaint();
}
@Override
public void setForeground(Color fg) {
getDiagram().setForeground(fg);
repaint();
}
@Override
public Color getForeground() {
return getDiagram().getForeground();
}
}

View File

@ -0,0 +1,23 @@
package org.hwo.ui.diagram;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.SortedSet;
public class AnnotatedDiagramProvider<T> extends AnnotatedPlotProvider{
ArrayList<Float> keys;
HashMap<Float, Object> objects;
public AnnotatedDiagramProvider(Class<T> clazz) {
super(clazz);
}
@Override
public Float getValue(int x, int graph) {
return super.getValue(x, graph);
}
}

View File

@ -0,0 +1,196 @@
package org.hwo.ui.diagram;
import java.awt.Color;
import java.beans.MethodDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import org.hwo.ui.diagram.annotation.Plot;
public class AnnotatedPlotProvider implements PlotProvider2{
private Class<?>
clazz;
private GraphDefinition[]
graphDefinitions;
private int maxOrdinate;
private Color[] colors;
private Vector<Object>
values;
public AnnotatedPlotProvider(Class<?> clazz){
this.setClazz(clazz);
this.setValues(new Object[0]);
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
updateClazz();
}
public Class<?> getClazz() {
return clazz;
}
public void setValues(Object[] values) {
this.values = new Vector<Object>();
this.values.addAll(Arrays.asList(values));
}
public void setValues(Vector v){
this.values = v;
}
public Vector<Object> getValues() {
return values;
}
private void updateClazz(){
LinkedList<GraphDefinition> gdl = new LinkedList<AnnotatedPlotProvider.GraphDefinition>();
for (Field f: this.clazz.getDeclaredFields()){
Plot plot = f.getAnnotation(Plot.class);
if (plot != null){
gdl.add(new GraphDefinition(f));
}
}
for (Method m: this.clazz.getDeclaredMethods()){
Plot plot = m.getAnnotation(Plot.class);
if (plot != null){
gdl.add(new GraphDefinition(m));
}
}
this.colors = new Color[ gdl.size() ];
for (GraphDefinition gd: gdl){
if (gd.getOrdinate() > maxOrdinate)
maxOrdinate = gd.getOrdinate();
this.colors[ gdl.indexOf(gd) ] = gd.getColor();
}
this.graphDefinitions = gdl.toArray(new GraphDefinition[0]);
}
public Color getColor(int graph){
return this.colors[graph];
}
public void setColors(int graph,Color color) {
this.colors[graph] = color;
}
@Override
public int getNumGraphs() {
return this.graphDefinitions.length;
}
@Override
public Color[] getColors() {
return this.colors;
}
public int getMaxOrdinate() {
return maxOrdinate;
}
public int getLength(){
return this.values.size();
}
public String getLabel(int graph){
return this.graphDefinitions[graph].getLabel();
}
public Float getValue(int x,int graph){
Object o = this.values.get(x);
if (o==null)
return null;
return this.graphDefinitions[graph].getValue(o);
}
public int getOrdinate(int graph){
return this.graphDefinitions[graph].getOrdinate();
}
class GraphDefinition{
Method method;
Field field;
String label;
int ordinate;
Color color;
public GraphDefinition(Method method){
this.method = method;
this.setPlot(method.getAnnotation(Plot.class));
}
public GraphDefinition(Field field){
this.field = field;
this.field.setAccessible(true);
this.setPlot(field.getAnnotation(Plot.class));
}
private void setPlot(Plot plot){
this.label = plot.label();
this.ordinate = plot.ordinate();
this.color = new Color(plot.r(), plot.g(), plot.b());
}
public int getOrdinate() {
return ordinate;
}
public String getLabel() {
return label;
}
public Color getColor() {
return color;
}
public Float getValue(Object o){
try {
if (field != null){
if (field.getType().equals(Integer.class)){
return ((Integer)field.get(o)).floatValue();
};
if (field.getType().equals(Double.class)){
return ((Double)field.get(o)).floatValue();
}
if (field.getType().equals(Float.class)){
return (Float)field.get(o);
}
}
if (method != null){
if (method.getReturnType().equals(Integer.class)){
return ((Integer)method.invoke(o, null)).floatValue();
}
if (method.getReturnType().equals(Double.class)){
return ((Double)method.invoke(o, null)).floatValue();
}
if (method.getReturnType().equals(Float.class)){
return ((Float)method.invoke(o, null));
}
}
} catch (IllegalAccessException illegalAccessException){
illegalAccessException.printStackTrace();
return null;
} catch (InvocationTargetException invocationTargetException){
invocationTargetException.printStackTrace();
return null;
}
return null;
}
}
}

View File

@ -0,0 +1,40 @@
package org.hwo.ui.diagram;
import java.util.SortedMap;
public class AnnotatedSortedMapPlotProvider<T> extends AnnotatedPlotProvider implements PlotProvider2,PlotLabeler {
private SortedMap<Object, T> sortedMap;
private Object[] keys;
private T[] values;
public AnnotatedSortedMapPlotProvider(Class<T> clazz) {
super(clazz);
this.sortedMap = null;
}
public void setSortedMap(SortedMap<Object, T> sortedMap) {
this.sortedMap = sortedMap;
}
public SortedMap<Object, T> getSortedMap() {
return sortedMap;
}
public void sortedMapChanged(){
this.keys = this.sortedMap.keySet().toArray();
}
@Override
public String getAbzisseLabel(Diagram diagram, int pos) {
return "";
}
@Override
public String getOrdinateLabel(Diagram diagram, Float value) {
return value.toString();
}
}

View File

@ -0,0 +1,48 @@
package org.hwo.ui.diagram;
public class ArrayDiagramDataProvider implements DiagramDataProvider {
private Object[] values;
private Float[] indeces;
public ArrayDiagramDataProvider() {
this.values = new Object[0];
this.indeces = new Float[0];
}
public void setValues(Object[] values) {
this.values = values;
this.indeces = new Float[values.length];
for (int i=0;i<values.length;i++){
this.indeces[i] = new Float(i);
}
}
public void setValues(Object[] values,Float f) {
this.values = values;
this.indeces = new Float[values.length];
for (int i=0;i<values.length;i++){
this.indeces[i] = new Float(i) * f;
}
}
public Object[] getValues() {
return values;
}
@Override
public Float[] getPlotKeys() {
return this.indeces;
}
@Override
public Object[] getPlotData() {
return this.values;
}
@Override
public String getKeyLabel(Float key) {
return null;
}
}

View File

@ -8,12 +8,13 @@ import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class Diagram {
private PlotProvider plotProvider;
private PlotProvider2 plotProvider;
private PlotPainter plotPainter;
private int x0,
@ -23,46 +24,89 @@ public class Diagram {
int yh,xw;
private float yscale;
private float yoffset;
private Float scaleBorder;
private Float[] minValues,maxValues;
private Float[] yscales;
private Float[] yoffsets;
private boolean[] autoscale;
private Font font;
private int numLabels;
private String labelFormatSpec;
private int xlabel_skip;
private LinkedList<ColoredBackground> coloredBackgrounds;
private PlotLabeler defaultLabeler;
private Color background,
foreground;
private Float abMinimum,
abMaximum;
public Diagram(){
background = Color.WHITE;
foreground = Color.BLACK;
coloredBackgrounds = new LinkedList<ColoredBackground>();
yoffset = -1.0f;
yscale = 2.0f;
plotProvider = new SimplePlotProvider(2, 2);
plotPainter = new LinePlotPainter();
font = new Font("Arial", Font.PLAIN, 24);
numLabels = 0;
labelFormatSpec = "%.4f";
coloredBackgrounds.add(new ColoredBackground(new Color(0.7f, 1.0f, 0.7f),-0.25f,0.25f));
coloredBackgrounds.add(new ColoredBackground(new Color(1.0f, 1.0f, 0.5f),-0.5f,-0.25f));
coloredBackgrounds.add(new ColoredBackground(new Color(1.0f, 1.0f, 0.5f),0.25f,0.5f));
scaleBorder = 0.1f;
defaultLabeler = new SimplePlotLabeler();
setPlotProvider(new AnnotatedPlotProvider(Object.class));
}
public float getYoffset() {
return yoffset;
private void updatePlotProviderParams(){
minValues = new Float[ plotProvider.getMaxOrdinate()+1];
maxValues = new Float[ plotProvider.getMaxOrdinate()+1];
yscales = new Float[ plotProvider.getMaxOrdinate()+1];
yoffsets = new Float[ plotProvider.getMaxOrdinate()+1];
autoscale = new boolean[ plotProvider.getMaxOrdinate()+1];
Arrays.fill(minValues, 0, minValues.length, Float.MAX_VALUE);
Arrays.fill(maxValues, 0, maxValues.length, Float.MIN_VALUE);
Arrays.fill(yscales, 0, yscales.length, new Float(1.0));
Arrays.fill(yoffsets, 0, yoffsets.length, new Float(0.0));
}
public void setYoffset(float yoffset) {
this.yoffset = yoffset;
public Color getBackground() {
return background;
}
public float getYscale() {
return yscale;
public void setBackground(Color background) {
this.background = background;
}
public void setYscale(float yscale) {
this.yscale = yscale;
public Color getForeground() {
return foreground;
}
public void setForeground(Color foreground) {
this.foreground = foreground;
}
public float getYoffset(int graph) {
return yoffsets[graph];
}
public void setYoffset(int graph,float yoffset) {
this.yoffsets[graph] = yoffset;
}
public float getYscale(int graph) {
return yscales[graph];
}
public void setYscale(int graph,float yscale) {
this.yscales[graph] = yscale;
}
public int getXlabel_skip() {
return xlabel_skip;
}
public void setXlabel_skip(int xlabel_skip) {
this.xlabel_skip = xlabel_skip;
}
public Font getFont() {
@ -86,11 +130,12 @@ public class Diagram {
this.plotPainter = plotPainter;
}
public PlotProvider getPlotProvider() {
public PlotProvider2 getPlotProvider() {
return plotProvider;
}
public void setPlotProvider(PlotProvider plotProvider) {
public void setPlotProvider(PlotProvider2 plotProvider) {
this.plotProvider = plotProvider;
updatePlotProviderParams();
}
public String getLabelFormatSpec() {
@ -104,42 +149,56 @@ public class Diagram {
return coloredBackgrounds;
}
public void setYMinimum(Float minimum){
setYoffset(minimum);
public void setYMinimum(int graph,Float minimum){
setYoffset(graph,minimum);
}
public Float getYMinimum(){
return getYoffset();
public Float getYMinimum(int graph){
return getYoffset(graph);
}
public void setYMaximum(Float maximum){
setYscale(maximum - yoffset);
public void setYMaximum(int graph,Float maximum){
setYscale(graph,maximum - yoffsets[graph]);
}
public Float getYMaximum(){
return getYscale() + yoffset;
public Float getYMaximum(int graph){
return getYscale(graph) + yoffsets[graph];
}
public void autoscale(int ordinate){
yscales[ordinate] = (maxValues[ordinate] - minValues[ordinate]) * (1.0f + scaleBorder);
yoffsets[ordinate] = minValues[ordinate] - ((maxValues[ordinate] - minValues[ordinate]) * scaleBorder);
if (yscales[ordinate] == 0.0f){
if (yoffsets[ordinate] == 0.0f){
yoffsets[ordinate] = -1.0f;
yscales[ordinate] = 2.0f * (1.0f + scaleBorder);
} else {
yscales[ordinate] = yoffsets[ordinate] * (1.0f + scaleBorder);
yoffsets[ordinate] -= yscales[ordinate] / 2.0f;
}
}
System.err.println(String.format("AutoScale Ordinate %d: %f x %f", ordinate, yoffsets[ordinate], yscales[ordinate]));
}
public void autoScale(){
Float max = Float.MIN_VALUE,min = Float.MAX_VALUE;
Float[][] matrix = this.plotProvider.getMatrix();
for (Float[] p:matrix){
for (Float value: p){
if (value == null)
continue;
if (value < min)
min = value;
if (value > max)
max = value;
}
for (int i=0;i<minValues.length;i++){
autoscale(i);
}
yoffset = min;
yscale = max - min;
System.err.println(String.format("AutoScale: %f x %f", yoffset, yscale));
}
public void setAutoScale(boolean autoScale){
for (int i=0;i<this.autoscale.length;i++)
this.autoscale[i] = autoScale;
}
public boolean isAutoScale(int ordinate){
return this.autoscale[ordinate];
}
public void setAutoScale(int ordinate,boolean autoScale){
this.autoscale[ordinate] = autoScale;
}
public BufferedImage plot(int width,int height){
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
@ -148,18 +207,20 @@ public class Diagram {
return image;
}
private int value2y(float value){
return y0 - (int)(yh * ((value - yoffset)/yscale));
private int value2y(int graph,Float value){
return y0 - (int)(yh * ((value - yoffsets[graph])/yscales[graph]));
}
private int point2x(int point){
return x0 + (xw * point / (this.plotProvider.getPoints()-1));
return x0 + (xw * point / (this.plotProvider.getLength()-1));
}
public void plot(Graphics2D g,int width,int height){
boolean rescaled = false;
int lineHeight = font.getSize() * Toolkit.getDefaultToolkit().getScreenResolution() / 72 ;
int nLabels = numLabels;
g.setColor(Color.WHITE);
g.setColor(background);
g.fillRect(0, 0, width, height);
y0 = height - lineHeight - 10;
@ -175,29 +236,38 @@ public class Diagram {
nLabels = 1;
for (ColoredBackground bg: coloredBackgrounds){
int ya = value2y(bg.getMin());
int yb = value2y(bg.getMax());
int ya = value2y(0,bg.getMin());
int yb = value2y(0,bg.getMax());
g.setColor(bg.getColor());
g.fillRect(0, yb, width, ya - yb);
}
g.setColor(Color.BLACK);
g.setColor(foreground);
if (this.font != null)
g.setFont(this.font);
for (int i=0;i<=nLabels;i++){
String l = String.format(labelFormatSpec, (yoffset + (yscale * i / nLabels)));
int lw = g.getFontMetrics().stringWidth(l);
for (int graph=0; graph < minValues.length; graph++){
int mw = 0;
if (lw > x0)
x0 = lw;
}
for (int i=0;i<=nLabels;i++){
String l = String.format(labelFormatSpec, (yoffset + (yscale * i / nLabels)));
int lw = g.getFontMetrics().stringWidth(l);
for (int i=0;i<=nLabels;i++){
String l = this.defaultLabeler.getOrdinateLabel(this, (yoffsets[graph] + (yscales[graph] * i / nLabels)));
int lw = g.getFontMetrics().stringWidth(l);
if (lw > mw)
mw = lw;
}
g.drawString(l, 2 + (x0 - lw), y0 - (yh * i / nLabels) + (lineHeight/4));
x0 += mw;
for (int i=0;i<=nLabels;i++){
String l = this.defaultLabeler.getOrdinateLabel(this, (yoffsets[graph] + (yscales[graph] * i / nLabels)));
int lw = g.getFontMetrics().stringWidth(l);
g.drawString(l, 2 + (x0 - lw), y0 - (yh * i / nLabels) + (lineHeight/4));
}
x0 += 5;
}
x0 += 10;
xw = x1 - x0;
@ -206,16 +276,16 @@ public class Diagram {
g.drawLine(x0 - 5, y0 - (yh * i / nLabels) , x0, y0 - (yh * i / nLabels));
}
for (int i=0;i<this.plotProvider.getPoints();i++){
for (int i=0;i<this.plotProvider.getLength();i+=1 + this.xlabel_skip){
g.drawLine(point2x(i), y0, point2x(i), y0 + 5);
String xlabel = this.plotProvider.getPointLabel(i);
String xlabel = this.defaultLabeler.getAbzisseLabel(this, i);
int lw = g.getFontMetrics().stringWidth(xlabel);
g.drawString(xlabel, point2x(i) - (lw/2), y0 + lineHeight);
g.drawString(xlabel, point2x(i) - (lw/2), y0 + 5 + lineHeight);
}
g.setColor(Color.BLACK);
g.setColor(foreground);
g.drawLine(x0, y0 + 5, x0, y1 - 10);
g.drawLine(x0, y1 - 10 , x0 - 5, y1 - 5);
@ -225,27 +295,62 @@ public class Diagram {
g.drawLine(x1 + 10 , y0, x1 + 5, y0 - 5);
g.drawLine(x1 + 10 , y0, x1 + 5, y0 + 5);
g.setColor(Color.RED);
Float[][] matrix = this.plotProvider.getMatrix();
Color[] colors = this.plotProvider.getColors();
for (int n=0;n<this.plotProvider.getPlots();n++){
Float[] min,max;
min = new Float[this.plotProvider.getMaxOrdinate()+1];
max = new Float[this.plotProvider.getMaxOrdinate()+1];
Arrays.fill(min, Float.MAX_VALUE);
Arrays.fill(max, Float.MIN_VALUE);
for (int n=0;n<this.plotProvider.getNumGraphs();n++){
int ordinate = this.plotProvider.getOrdinate(n);
plotPainter.reset();
for (int i=0;i<this.plotProvider.getPoints();i++){
for (int i=0;i<this.plotProvider.getLength();i++){
int x,y;
Float v = this.plotProvider.getValue(i, n);
if (matrix[n][i] != null){
if (v != null){
x = point2x(i);
y = value2y(matrix[n][i]);
y = value2y(ordinate,v);
plotPainter.paintPoint(g, colors[n], x, y);
if (v < min[ordinate])
min[ordinate] = v;
if (v > max[ordinate])
max[ordinate] = v;
} else {
plotPainter.reset();
}
}
}
for (int i=0;i<this.autoscale.length;i++){
if (this.autoscale[i]){
if (!(this.minValues[i].equals(min[i]) && this.maxValues[i].equals(max[i]))){
this.minValues[i] = min[i];
this.maxValues[i] = max[i];
this.autoscale(i);
rescaled = true;
}
}
}
if (rescaled){
this.plot(g,width,height);
}
}
public PlotLabeler getDefaultLabeler() {
return defaultLabeler;
}
public void setDefaultLabeler(PlotLabeler defaultLabeler) {
this.defaultLabeler = defaultLabeler;
}
}

View File

@ -0,0 +1,10 @@
package org.hwo.ui.diagram;
public interface DiagramDataProvider {
public Float[] getPlotKeys();
public Object[] getPlotData();
public String getKeyLabel(Float key);
}

View File

@ -1,11 +1,20 @@
package org.hwo.ui.diagram;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
public class LinePlotPainter implements PlotPainter {
int lx,ly;
float width;
Stroke stroke;
public LinePlotPainter() {
setWidth(1.0f);
}
@Override
public void reset() {
@ -17,11 +26,22 @@ public class LinePlotPainter implements PlotPainter {
public void paintPoint(Graphics2D g, Color color, int x, int y) {
if (lx != -1){
g.setColor(color);
Stroke s = g.getStroke();
g.setStroke(stroke);
g.drawLine(lx, ly, x, y);
g.setStroke(s);
}
lx = x;
ly = y;
}
public void setWidth(float width) {
this.width = width;
this.stroke = new BasicStroke(width);
}
public float getWidth() {
return width;
}
}

View File

@ -0,0 +1,8 @@
package org.hwo.ui.diagram;
public interface PlotLabeler {
public String getOrdinateLabel(Diagram diagram,Float value);
public String getAbzisseLabel(Diagram diagram,int pos);
}

View File

@ -0,0 +1,16 @@
package org.hwo.ui.diagram;
import java.awt.Color;
public interface PlotProvider2 {
public int getMaxOrdinate();
public int getLength();
public int getNumGraphs();
public String getLabel(int graph);
public Float getValue(int x,int graph);
public int getOrdinate(int graph);
public Color[] getColors();
}

View File

@ -0,0 +1,40 @@
package org.hwo.ui.diagram;
public class SimplePlotLabeler implements PlotLabeler {
private int decimals;
private String ordinateFormat;
public SimplePlotLabeler() {
this.setDecimals(2);
}
public SimplePlotLabeler(int decimals){
this.setDecimals(decimals);
}
public void setDecimals(int decimals) {
this.decimals = decimals;
this.ordinateFormat = String.format("%%.%df", decimals);
}
public int getDecimals() {
return decimals;
}
@Override
public String getOrdinateLabel(Diagram diagram, Float value) {
if (value == null)
return "";
if (value.isNaN())
return "";
return String.format(this.ordinateFormat,value);
}
@Override
public String getAbzisseLabel(Diagram diagram, int pos) {
return new Integer(pos).toString();
}
}

View File

@ -0,0 +1,9 @@
package org.hwo.ui.diagram.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Abszisse {
}

View File

@ -0,0 +1,16 @@
package org.hwo.ui.diagram.annotation;
import java.awt.Color;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Plot {
public String label();
public int ordinate() default 0;
public int r() default 0;
public int g() default 0;
public int b() default 0;
}