提交 8f3aad24 编写于 作者: K kohsuke

made this stack area renderer reusable.


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@1566 71c3de6d-444a-0410-be80-ed276b4c234a
上级 5030a791
......@@ -12,6 +12,7 @@ import hudson.util.DataSetBuilder;
import hudson.util.IOException2;
import hudson.util.RunList;
import hudson.util.ShiftedCategoryAxis;
import hudson.util.StackedAreaRenderer2;
import hudson.util.TextFile;
import hudson.util.XStream2;
import org.apache.tools.ant.taskdefs.Copy;
......@@ -21,27 +22,17 @@ import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.AreaRendererEndType;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.StackedAreaRenderer;
import org.jfree.chart.urls.CategoryURLGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
......@@ -547,7 +538,7 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
private JFreeChart createBuildTimeTrendChart() {
class Label implements Comparable<Label> {
private final Run run;
final Run run;
public Label(Run r) {
this.run = r;
......@@ -584,13 +575,6 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
return l;
}
public String getURL() {
return String.valueOf(run.number);
}
public String getTooltip() {
return run.getDisplayName()+" : "+run.getDurationString();
}
}
DataSetBuilder<String,Label> data = new DataSetBuilder<String, Label>();
......@@ -633,138 +617,25 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
StackedAreaRenderer ar = new StackedAreaRenderer() {
StackedAreaRenderer ar = new StackedAreaRenderer2() {
@Override
public Paint getItemPaint(int row, int column) {
Label key = (Label) dataset.getColumnKey(column);
return key.getColor();
}
// Mostly copied from the base class.
@Override
public void drawItem(Graphics2D g2,
CategoryItemRendererState state,
Rectangle2D dataArea,
CategoryPlot plot,
CategoryAxis domainAxis,
ValueAxis rangeAxis,
CategoryDataset dataset,
int row,
int column,
int pass) {
// plot non-null values...
Number dataValue = dataset.getValue(row, column);
if (dataValue == null) {
return;
}
double value = dataValue.doubleValue();
// leave the y values (y1, y0) untranslated as it is going to be be
// stacked up later by previous series values, after this it will be
// translated.
double xx1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
dataArea, plot.getDomainAxisEdge());
double previousHeightx1 = getPreviousHeight(dataset, row, column);
double y1 = value + previousHeightx1;
RectangleEdge location = plot.getRangeAxisEdge();
double yy1 = rangeAxis.valueToJava2D(y1, dataArea, location);
g2.setPaint(getItemPaint(row, column));
g2.setStroke(getItemStroke(row, column));
// add an item entity, if this information is being collected
EntityCollection entities = state.getEntityCollection();
// in column zero, the only job to do is draw any visible item labels
// and this is done in the second pass...
if (column == 0) {
if (pass == 1) {
// draw item labels, if visible
if (isItemLabelVisible(row, column)) {
drawItemLabel(g2, plot.getOrientation(), dataset, row, column,
xx1, yy1, (y1 < 0.0));
}
}
} else {
Number previousValue = dataset.getValue(row, column - 1);
if (previousValue != null) {
double xx0 = domainAxis.getCategoryMiddle(column - 1,
getColumnCount(), dataArea, plot.getDomainAxisEdge());
double y0 = previousValue.doubleValue();
// Get the previous height, but this will be different for both
// y0 and y1 as the previous series values could differ.
double previousHeightx0 = getPreviousHeight(dataset, row,
column - 1);
// Now stack the current y values on top of the previous values.
y0 += previousHeightx0;
// Now translate the previous heights
double previousHeightxx0 = rangeAxis.valueToJava2D(
previousHeightx0, dataArea, location);
double previousHeightxx1 = rangeAxis.valueToJava2D(
previousHeightx1, dataArea, location);
// Now translate the current y values.
double yy0 = rangeAxis.valueToJava2D(y0, dataArea, location);
if (pass == 0) {
// left half
Polygon p = new Polygon();
p.addPoint((int) xx0, (int) yy0);
p.addPoint((int) (xx0+xx1)/2, (int) (yy0+yy1)/2);
p.addPoint((int) (xx0+xx1)/2, (int) (previousHeightxx0+previousHeightxx1)/2);
p.addPoint((int) xx0, (int) previousHeightxx0);
g2.setPaint(getItemPaint(row, column-1));
g2.setStroke(getItemStroke(row, column-1));
g2.fill(p);
if (entities != null)
addItemEntity(entities, dataset, row, column-1, p);
// right half
p = new Polygon();
p.addPoint((int) xx1, (int) yy1);
p.addPoint((int) (xx0+xx1)/2, (int) (yy0+yy1)/2);
p.addPoint((int) (xx0+xx1)/2, (int) (previousHeightxx0+previousHeightxx1)/2);
p.addPoint((int) xx1, (int) previousHeightxx1);
g2.setPaint(getItemPaint(row, column));
g2.setStroke(getItemStroke(row, column));
g2.fill(p);
if (entities != null)
addItemEntity(entities, dataset, row, column, p);
} else {
if (isItemLabelVisible(row, column)) {
drawItemLabel(g2, plot.getOrientation(), dataset, row,
column, xx1, yy1, (y1 < 0.0));
}
}
}
}
}
};
ar.setEndType(AreaRendererEndType.TRUNCATE);
ar.setSeriesPaint(0,ColorPalette.BLUE);
ar.setItemURLGenerator(new CategoryURLGenerator() {
public String generateURL(CategoryDataset dataset, int row, int column) {
Label label = (Label) dataset.getColumnKey(column);
return label.getURL();
return String.valueOf(label.run.number);
}
});
ar.setToolTipGenerator(new CategoryToolTipGenerator() {
@Override
public String generateToolTip(CategoryDataset dataset, int row, int column) {
Label label = (Label) dataset.getColumnKey(column);
return label.getTooltip();
return label.run.getDisplayName() + " : " + label.run.getDurationString();
}
});
};
plot.setRenderer(ar);
// crop extra space around the graph
......
......@@ -16,8 +16,11 @@ import java.io.IOException;
import hudson.model.Build;
/**
* See issue 93. Detect an error in X11 and handle it gracefully.
* Chart generation utility code around JFreeChart.
*
* @see StackedAreaRenderer2
* @see DataSetBuilder
* @see ShiftedCategoryAxis
* @author Kohsuke Kawaguchi
*/
public class ChartUtil {
......
package hudson.util;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.AreaRendererEndType;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.StackedAreaRenderer;
import org.jfree.chart.urls.CategoryURLGenerator;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.RectangleEdge;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Paint;
import java.awt.geom.Rectangle2D;
/**
* Modified {@link StackedAreaRenderer}.
*
* <ol>
* <li>Built-in support for {@link #generateToolTip(CategoryDataset, int, int) tooltip}
* and {@link #generateURL(CategoryDataset, int, int) hyperlinks} for clickable map.
* <li>Clickable map hit test is modified so that the entire area is clickable,
* not just the small area around each data point.
* <li>Rendering algorithm is modified so that
* {@link #getItemPaint(int, int) different color on the same data series}
* will look more natural.
* </ol>
*
* @author Kohsuke Kawaguchi
*/
public class StackedAreaRenderer2 extends StackedAreaRenderer
implements CategoryToolTipGenerator, CategoryURLGenerator {
public StackedAreaRenderer2() {
setEndType(AreaRendererEndType.TRUNCATE);
setItemURLGenerator(this);
setToolTipGenerator(this);
}
/**
* Override this method to specify the hyperlink target of the given data point.
*/
public String generateURL(CategoryDataset dataset, int row, int column) {
return null;
}
/**
* Override this method to specify the tool tip text of the given data point.
*/
public String generateToolTip(CategoryDataset dataset, int row, int column) {
return null;
}
/**
* Override this method to specify the color to draw the given area.
*/
public Paint getItemPaint(int row, int column) {
return super.getItemPaint(row, column);
}
// Mostly copied from the base class.
@Override
public void drawItem(Graphics2D g2,
CategoryItemRendererState state,
Rectangle2D dataArea,
CategoryPlot plot,
CategoryAxis domainAxis,
ValueAxis rangeAxis,
CategoryDataset dataset,
int row,
int column,
int pass) {
// plot non-null values...
Number dataValue = dataset.getValue(row, column);
if (dataValue == null) {
return;
}
double value = dataValue.doubleValue();
// leave the y values (y1, y0) untranslated as it is going to be be
// stacked up later by previous series values, after this it will be
// translated.
double xx1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
dataArea, plot.getDomainAxisEdge());
double previousHeightx1 = getPreviousHeight(dataset, row, column);
double y1 = value + previousHeightx1;
RectangleEdge location = plot.getRangeAxisEdge();
double yy1 = rangeAxis.valueToJava2D(y1, dataArea, location);
g2.setPaint(getItemPaint(row, column));
g2.setStroke(getItemStroke(row, column));
// add an item entity, if this information is being collected
EntityCollection entities = state.getEntityCollection();
// in column zero, the only job to do is draw any visible item labels
// and this is done in the second pass...
if (column == 0) {
if (pass == 1) {
// draw item labels, if visible
if (isItemLabelVisible(row, column)) {
drawItemLabel(g2, plot.getOrientation(), dataset, row, column,
xx1, yy1, (y1 < 0.0));
}
}
} else {
Number previousValue = dataset.getValue(row, column - 1);
if (previousValue != null) {
double xx0 = domainAxis.getCategoryMiddle(column - 1,
getColumnCount(), dataArea, plot.getDomainAxisEdge());
double y0 = previousValue.doubleValue();
// Get the previous height, but this will be different for both
// y0 and y1 as the previous series values could differ.
double previousHeightx0 = getPreviousHeight(dataset, row,
column - 1);
// Now stack the current y values on top of the previous values.
y0 += previousHeightx0;
// Now translate the previous heights
double previousHeightxx0 = rangeAxis.valueToJava2D(
previousHeightx0, dataArea, location);
double previousHeightxx1 = rangeAxis.valueToJava2D(
previousHeightx1, dataArea, location);
// Now translate the current y values.
double yy0 = rangeAxis.valueToJava2D(y0, dataArea, location);
if (pass == 0) {
// left half
Polygon p = new Polygon();
p.addPoint((int) xx0, (int) yy0);
p.addPoint((int) (xx0+xx1)/2, (int) (yy0+yy1)/2);
p.addPoint((int) (xx0+xx1)/2, (int) (previousHeightxx0+previousHeightxx1)/2);
p.addPoint((int) xx0, (int) previousHeightxx0);
g2.setPaint(getItemPaint(row, column-1));
g2.setStroke(getItemStroke(row, column-1));
g2.fill(p);
if (entities != null)
addItemEntity(entities, dataset, row, column-1, p);
// right half
p = new Polygon();
p.addPoint((int) xx1, (int) yy1);
p.addPoint((int) (xx0+xx1)/2, (int) (yy0+yy1)/2);
p.addPoint((int) (xx0+xx1)/2, (int) (previousHeightxx0+previousHeightxx1)/2);
p.addPoint((int) xx1, (int) previousHeightxx1);
g2.setPaint(getItemPaint(row, column));
g2.setStroke(getItemStroke(row, column));
g2.fill(p);
if (entities != null)
addItemEntity(entities, dataset, row, column, p);
} else {
if (isItemLabelVisible(row, column)) {
drawItemLabel(g2, plot.getOrientation(), dataset, row,
column, xx1, yy1, (y1 < 0.0));
}
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册