1:
85:
86: package ;
87:
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96: import ;
97: import ;
98: import ;
99: import ;
100:
101: import ;
102: import ;
103: import ;
104: import ;
105: import ;
106: import ;
107: import ;
108: import ;
109: import ;
110: import ;
111: import ;
112: import ;
113: import ;
114: import ;
115: import ;
116: import ;
117: import ;
118: import ;
119: import ;
120: import ;
121:
122:
125: public class BarRenderer extends AbstractCategoryItemRenderer
126: implements Cloneable, PublicCloneable, Serializable {
127:
128:
129: private static final long serialVersionUID = 6000649414965887481L;
130:
131:
132: public static final double DEFAULT_ITEM_MARGIN = 0.20;
133:
134:
138: public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0;
139:
140:
141: private double itemMargin;
142:
143:
144: private boolean drawBarOutline;
145:
146:
147: private double maximumBarWidth;
148:
149:
150: private double minimumBarLength;
151:
152:
156: private GradientPaintTransformer gradientPaintTransformer;
157:
158:
162: private ItemLabelPosition positiveItemLabelPositionFallback;
163:
164:
168: private ItemLabelPosition negativeItemLabelPositionFallback;
169:
170:
171: private double upperClip;
172:
173:
174:
175: private double lowerClip;
176:
177:
178:
179: private double base;
180:
181:
185: private boolean includeBaseInRange;
186:
187:
190: public BarRenderer() {
191: super();
192: this.base = 0.0;
193: this.includeBaseInRange = true;
194: this.itemMargin = DEFAULT_ITEM_MARGIN;
195: this.drawBarOutline = false;
196: this.maximumBarWidth = 1.0;
197:
198: this.positiveItemLabelPositionFallback = null;
199: this.negativeItemLabelPositionFallback = null;
200: this.gradientPaintTransformer = new StandardGradientPaintTransformer();
201: this.minimumBarLength = 0.0;
202: }
203:
204:
212: public double getBase() {
213: return this.base;
214: }
215:
216:
224: public void setBase(double base) {
225: this.base = base;
226: notifyListeners(new RendererChangeEvent(this));
227: }
228:
229:
237: public double getItemMargin() {
238: return this.itemMargin;
239: }
240:
241:
251: public void setItemMargin(double percent) {
252: this.itemMargin = percent;
253: notifyListeners(new RendererChangeEvent(this));
254: }
255:
256:
263: public boolean isDrawBarOutline() {
264: return this.drawBarOutline;
265: }
266:
267:
275: public void setDrawBarOutline(boolean draw) {
276: this.drawBarOutline = draw;
277: notifyListeners(new RendererChangeEvent(this));
278: }
279:
280:
288: public double getMaximumBarWidth() {
289: return this.maximumBarWidth;
290: }
291:
292:
301: public void setMaximumBarWidth(double percent) {
302: this.maximumBarWidth = percent;
303: notifyListeners(new RendererChangeEvent(this));
304: }
305:
306:
313: public double getMinimumBarLength() {
314: return this.minimumBarLength;
315: }
316:
317:
327: public void setMinimumBarLength(double min) {
328: this.minimumBarLength = min;
329: notifyListeners(new RendererChangeEvent(this));
330: }
331:
332:
340: public GradientPaintTransformer getGradientPaintTransformer() {
341: return this.gradientPaintTransformer;
342: }
343:
344:
352: public void setGradientPaintTransformer(
353: GradientPaintTransformer transformer) {
354: this.gradientPaintTransformer = transformer;
355: notifyListeners(new RendererChangeEvent(this));
356: }
357:
358:
366: public ItemLabelPosition getPositiveItemLabelPositionFallback() {
367: return this.positiveItemLabelPositionFallback;
368: }
369:
370:
379: public void setPositiveItemLabelPositionFallback(
380: ItemLabelPosition position) {
381: this.positiveItemLabelPositionFallback = position;
382: notifyListeners(new RendererChangeEvent(this));
383: }
384:
385:
393: public ItemLabelPosition getNegativeItemLabelPositionFallback() {
394: return this.negativeItemLabelPositionFallback;
395: }
396:
397:
406: public void setNegativeItemLabelPositionFallback(
407: ItemLabelPosition position) {
408: this.negativeItemLabelPositionFallback = position;
409: notifyListeners(new RendererChangeEvent(this));
410: }
411:
412:
424: public boolean getIncludeBaseInRange() {
425: return this.includeBaseInRange;
426: }
427:
428:
440: public void setIncludeBaseInRange(boolean include) {
441: if (this.includeBaseInRange != include) {
442: this.includeBaseInRange = include;
443: notifyListeners(new RendererChangeEvent(this));
444: }
445: }
446:
447:
453: public double getLowerClip() {
454:
455: return this.lowerClip;
456: }
457:
458:
464: public double getUpperClip() {
465:
466: return this.upperClip;
467: }
468:
469:
482: public CategoryItemRendererState initialise(Graphics2D g2,
483: Rectangle2D dataArea,
484: CategoryPlot plot,
485: int rendererIndex,
486: PlotRenderingInfo info) {
487:
488: CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
489: rendererIndex, info);
490:
491:
492: ValueAxis rangeAxis = plot.getRangeAxisForDataset(rendererIndex);
493: this.lowerClip = rangeAxis.getRange().getLowerBound();
494: this.upperClip = rangeAxis.getRange().getUpperBound();
495:
496:
497: calculateBarWidth(plot, dataArea, rendererIndex, state);
498:
499: return state;
500:
501: }
502:
503:
511: protected void calculateBarWidth(CategoryPlot plot,
512: Rectangle2D dataArea,
513: int rendererIndex,
514: CategoryItemRendererState state) {
515:
516: CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
517: CategoryDataset dataset = plot.getDataset(rendererIndex);
518: if (dataset != null) {
519: int columns = dataset.getColumnCount();
520: int rows = dataset.getRowCount();
521: double space = 0.0;
522: PlotOrientation orientation = plot.getOrientation();
523: if (orientation == PlotOrientation.HORIZONTAL) {
524: space = dataArea.getHeight();
525: }
526: else if (orientation == PlotOrientation.VERTICAL) {
527: space = dataArea.getWidth();
528: }
529: double maxWidth = space * getMaximumBarWidth();
530: double categoryMargin = 0.0;
531: double currentItemMargin = 0.0;
532: if (columns > 1) {
533: categoryMargin = domainAxis.getCategoryMargin();
534: }
535: if (rows > 1) {
536: currentItemMargin = getItemMargin();
537: }
538: double used = space * (1 - domainAxis.getLowerMargin()
539: - domainAxis.getUpperMargin()
540: - categoryMargin - currentItemMargin);
541: if ((rows * columns) > 0) {
542: state.setBarWidth(Math.min(used / (rows * columns), maxWidth));
543: }
544: else {
545: state.setBarWidth(Math.min(used, maxWidth));
546: }
547: }
548: }
549:
550:
565: protected double calculateBarW0(CategoryPlot plot,
566: PlotOrientation orientation,
567: Rectangle2D dataArea,
568: CategoryAxis domainAxis,
569: CategoryItemRendererState state,
570: int row,
571: int column) {
572:
573: double space = 0.0;
574: if (orientation == PlotOrientation.HORIZONTAL) {
575: space = dataArea.getHeight();
576: }
577: else {
578: space = dataArea.getWidth();
579: }
580: double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
581: dataArea, plot.getDomainAxisEdge());
582: int seriesCount = getRowCount();
583: int categoryCount = getColumnCount();
584: if (seriesCount > 1) {
585: double seriesGap = space * getItemMargin()
586: / (categoryCount * (seriesCount - 1));
587: double seriesW = calculateSeriesWidth(space, domainAxis,
588: categoryCount, seriesCount);
589: barW0 = barW0 + row * (seriesW + seriesGap)
590: + (seriesW / 2.0) - (state.getBarWidth() / 2.0);
591: }
592: else {
593: barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
594: dataArea, plot.getDomainAxisEdge()) - state.getBarWidth()
595: / 2.0;
596: }
597: return barW0;
598: }
599:
600:
608: protected double[] calculateBarL0L1(double value) {
609: double lclip = getLowerClip();
610: double uclip = getUpperClip();
611: double barLow = Math.min(this.base, value);
612: double barHigh = Math.max(this.base, value);
613: if (barHigh < lclip) {
614: return null;
615: }
616: if (barLow > uclip) {
617: return null;
618: }
619: barLow = Math.max(barLow, lclip);
620: barHigh = Math.min(barHigh, uclip);
621: return new double[] {barLow, barHigh};
622: }
623:
624:
635: public Range findRangeBounds(CategoryDataset dataset) {
636: Range result = DatasetUtilities.findRangeBounds(dataset);
637: if (result != null) {
638: if (this.includeBaseInRange) {
639: result = Range.expandToInclude(result, this.base);
640: }
641: }
642: return result;
643: }
644:
645:
653: public LegendItem getLegendItem(int datasetIndex, int series) {
654:
655: CategoryPlot cp = getPlot();
656: if (cp == null) {
657: return null;
658: }
659:
660:
661: if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
662: return null;
663: }
664:
665: CategoryDataset dataset = cp.getDataset(datasetIndex);
666: String label = getLegendItemLabelGenerator().generateLabel(dataset,
667: series);
668: String description = label;
669: String toolTipText = null;
670: if (getLegendItemToolTipGenerator() != null) {
671: toolTipText = getLegendItemToolTipGenerator().generateLabel(
672: dataset, series);
673: }
674: String urlText = null;
675: if (getLegendItemURLGenerator() != null) {
676: urlText = getLegendItemURLGenerator().generateLabel(dataset,
677: series);
678: }
679: Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
680: Paint paint = lookupSeriesPaint(series);
681: Paint outlinePaint = lookupSeriesOutlinePaint(series);
682: Stroke outlineStroke = lookupSeriesOutlineStroke(series);
683:
684: LegendItem result = new LegendItem(label, description, toolTipText,
685: urlText, true, shape, true, paint, isDrawBarOutline(),
686: outlinePaint, outlineStroke, false, new Line2D.Float(),
687: new BasicStroke(1.0f), Color.black);
688: result.setDataset(dataset);
689: result.setDatasetIndex(datasetIndex);
690: result.setSeriesKey(dataset.getRowKey(series));
691: result.setSeriesIndex(series);
692: if (this.gradientPaintTransformer != null) {
693: result.setFillPaintTransformer(this.gradientPaintTransformer);
694: }
695: return result;
696: }
697:
698:
712: public void drawItem(Graphics2D g2,
713: CategoryItemRendererState state,
714: Rectangle2D dataArea,
715: CategoryPlot plot,
716: CategoryAxis domainAxis,
717: ValueAxis rangeAxis,
718: CategoryDataset dataset,
719: int row,
720: int column,
721: int pass) {
722:
723:
724: Number dataValue = dataset.getValue(row, column);
725: if (dataValue == null) {
726: return;
727: }
728:
729: double value = dataValue.doubleValue();
730:
731: PlotOrientation orientation = plot.getOrientation();
732: double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis,
733: state, row, column);
734: double[] barL0L1 = calculateBarL0L1(value);
735: if (barL0L1 == null) {
736: return;
737: }
738:
739: RectangleEdge edge = plot.getRangeAxisEdge();
740: double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge);
741: double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge);
742: double barL0 = Math.min(transL0, transL1);
743: double barLength = Math.max(Math.abs(transL1 - transL0),
744: getMinimumBarLength());
745:
746:
747: Rectangle2D bar = null;
748: if (orientation == PlotOrientation.HORIZONTAL) {
749: bar = new Rectangle2D.Double(barL0, barW0, barLength,
750: state.getBarWidth());
751: }
752: else {
753: bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
754: barLength);
755: }
756: Paint itemPaint = getItemPaint(row, column);
757: GradientPaintTransformer t = getGradientPaintTransformer();
758: if (t != null && itemPaint instanceof GradientPaint) {
759: itemPaint = t.transform((GradientPaint) itemPaint, bar);
760: }
761: g2.setPaint(itemPaint);
762: g2.fill(bar);
763:
764:
765: if (isDrawBarOutline()
766: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
767: Stroke stroke = getItemOutlineStroke(row, column);
768: Paint paint = getItemOutlinePaint(row, column);
769: if (stroke != null && paint != null) {
770: g2.setStroke(stroke);
771: g2.setPaint(paint);
772: g2.draw(bar);
773: }
774: }
775:
776: CategoryItemLabelGenerator generator
777: = getItemLabelGenerator(row, column);
778: if (generator != null && isItemLabelVisible(row, column)) {
779: drawItemLabel(g2, dataset, row, column, plot, generator, bar,
780: (value < 0.0));
781: }
782:
783:
784: EntityCollection entities = state.getEntityCollection();
785: if (entities != null) {
786: addItemEntity(entities, dataset, row, column, bar);
787: }
788:
789: }
790:
791:
801: protected double calculateSeriesWidth(double space, CategoryAxis axis,
802: int categories, int series) {
803: double factor = 1.0 - getItemMargin() - axis.getLowerMargin()
804: - axis.getUpperMargin();
805: if (categories > 1) {
806: factor = factor - axis.getCategoryMargin();
807: }
808: return (space * factor) / (categories * series);
809: }
810:
811:
824: protected void drawItemLabel(Graphics2D g2,
825: CategoryDataset data,
826: int row,
827: int column,
828: CategoryPlot plot,
829: CategoryItemLabelGenerator generator,
830: Rectangle2D bar,
831: boolean negative) {
832:
833: String label = generator.generateLabel(data, row, column);
834: if (label == null) {
835: return;
836: }
837:
838: Font labelFont = getItemLabelFont(row, column);
839: g2.setFont(labelFont);
840: Paint paint = getItemLabelPaint(row, column);
841: g2.setPaint(paint);
842:
843:
844: ItemLabelPosition position = null;
845: if (!negative) {
846: position = getPositiveItemLabelPosition(row, column);
847: }
848: else {
849: position = getNegativeItemLabelPosition(row, column);
850: }
851:
852:
853: Point2D anchorPoint = calculateLabelAnchorPoint(
854: position.getItemLabelAnchor(), bar, plot.getOrientation());
855:
856: if (isInternalAnchor(position.getItemLabelAnchor())) {
857: Shape bounds = TextUtilities.calculateRotatedStringBounds(label,
858: g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
859: position.getTextAnchor(), position.getAngle(),
860: position.getRotationAnchor());
861:
862: if (bounds != null) {
863: if (!bar.contains(bounds.getBounds2D())) {
864: if (!negative) {
865: position = getPositiveItemLabelPositionFallback();
866: }
867: else {
868: position = getNegativeItemLabelPositionFallback();
869: }
870: if (position != null) {
871: anchorPoint = calculateLabelAnchorPoint(
872: position.getItemLabelAnchor(), bar,
873: plot.getOrientation());
874: }
875: }
876: }
877:
878: }
879:
880: if (position != null) {
881: TextUtilities.drawRotatedString(label, g2,
882: (float) anchorPoint.getX(), (float) anchorPoint.getY(),
883: position.getTextAnchor(), position.getAngle(),
884: position.getRotationAnchor());
885: }
886: }
887:
888:
897: private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
898: Rectangle2D bar,
899: PlotOrientation orientation) {
900:
901: Point2D result = null;
902: double offset = getItemLabelAnchorOffset();
903: double x0 = bar.getX() - offset;
904: double x1 = bar.getX();
905: double x2 = bar.getX() + offset;
906: double x3 = bar.getCenterX();
907: double x4 = bar.getMaxX() - offset;
908: double x5 = bar.getMaxX();
909: double x6 = bar.getMaxX() + offset;
910:
911: double y0 = bar.getMaxY() + offset;
912: double y1 = bar.getMaxY();
913: double y2 = bar.getMaxY() - offset;
914: double y3 = bar.getCenterY();
915: double y4 = bar.getMinY() + offset;
916: double y5 = bar.getMinY();
917: double y6 = bar.getMinY() - offset;
918:
919: if (anchor == ItemLabelAnchor.CENTER) {
920: result = new Point2D.Double(x3, y3);
921: }
922: else if (anchor == ItemLabelAnchor.INSIDE1) {
923: result = new Point2D.Double(x4, y4);
924: }
925: else if (anchor == ItemLabelAnchor.INSIDE2) {
926: result = new Point2D.Double(x4, y4);
927: }
928: else if (anchor == ItemLabelAnchor.INSIDE3) {
929: result = new Point2D.Double(x4, y3);
930: }
931: else if (anchor == ItemLabelAnchor.INSIDE4) {
932: result = new Point2D.Double(x4, y2);
933: }
934: else if (anchor == ItemLabelAnchor.INSIDE5) {
935: result = new Point2D.Double(x4, y2);
936: }
937: else if (anchor == ItemLabelAnchor.INSIDE6) {
938: result = new Point2D.Double(x3, y2);
939: }
940: else if (anchor == ItemLabelAnchor.INSIDE7) {
941: result = new Point2D.Double(x2, y2);
942: }
943: else if (anchor == ItemLabelAnchor.INSIDE8) {
944: result = new Point2D.Double(x2, y2);
945: }
946: else if (anchor == ItemLabelAnchor.INSIDE9) {
947: result = new Point2D.Double(x2, y3);
948: }
949: else if (anchor == ItemLabelAnchor.INSIDE10) {
950: result = new Point2D.Double(x2, y4);
951: }
952: else if (anchor == ItemLabelAnchor.INSIDE11) {
953: result = new Point2D.Double(x2, y4);
954: }
955: else if (anchor == ItemLabelAnchor.INSIDE12) {
956: result = new Point2D.Double(x3, y4);
957: }
958: else if (anchor == ItemLabelAnchor.OUTSIDE1) {
959: result = new Point2D.Double(x5, y6);
960: }
961: else if (anchor == ItemLabelAnchor.OUTSIDE2) {
962: result = new Point2D.Double(x6, y5);
963: }
964: else if (anchor == ItemLabelAnchor.OUTSIDE3) {
965: result = new Point2D.Double(x6, y3);
966: }
967: else if (anchor == ItemLabelAnchor.OUTSIDE4) {
968: result = new Point2D.Double(x6, y1);
969: }
970: else if (anchor == ItemLabelAnchor.OUTSIDE5) {
971: result = new Point2D.Double(x5, y0);
972: }
973: else if (anchor == ItemLabelAnchor.OUTSIDE6) {
974: result = new Point2D.Double(x3, y0);
975: }
976: else if (anchor == ItemLabelAnchor.OUTSIDE7) {
977: result = new Point2D.Double(x1, y0);
978: }
979: else if (anchor == ItemLabelAnchor.OUTSIDE8) {
980: result = new Point2D.Double(x0, y1);
981: }
982: else if (anchor == ItemLabelAnchor.OUTSIDE9) {
983: result = new Point2D.Double(x0, y3);
984: }
985: else if (anchor == ItemLabelAnchor.OUTSIDE10) {
986: result = new Point2D.Double(x0, y5);
987: }
988: else if (anchor == ItemLabelAnchor.OUTSIDE11) {
989: result = new Point2D.Double(x1, y6);
990: }
991: else if (anchor == ItemLabelAnchor.OUTSIDE12) {
992: result = new Point2D.Double(x3, y6);
993: }
994:
995: return result;
996:
997: }
998:
999:
1006: private boolean isInternalAnchor(ItemLabelAnchor anchor) {
1007: return anchor == ItemLabelAnchor.CENTER
1008: || anchor == ItemLabelAnchor.INSIDE1
1009: || anchor == ItemLabelAnchor.INSIDE2
1010: || anchor == ItemLabelAnchor.INSIDE3
1011: || anchor == ItemLabelAnchor.INSIDE4
1012: || anchor == ItemLabelAnchor.INSIDE5
1013: || anchor == ItemLabelAnchor.INSIDE6
1014: || anchor == ItemLabelAnchor.INSIDE7
1015: || anchor == ItemLabelAnchor.INSIDE8
1016: || anchor == ItemLabelAnchor.INSIDE9
1017: || anchor == ItemLabelAnchor.INSIDE10
1018: || anchor == ItemLabelAnchor.INSIDE11
1019: || anchor == ItemLabelAnchor.INSIDE12;
1020: }
1021:
1022:
1029: public boolean equals(Object obj) {
1030:
1031: if (obj == this) {
1032: return true;
1033: }
1034: if (!(obj instanceof BarRenderer)) {
1035: return false;
1036: }
1037: if (!super.equals(obj)) {
1038: return false;
1039: }
1040: BarRenderer that = (BarRenderer) obj;
1041: if (this.base != that.base) {
1042: return false;
1043: }
1044: if (this.itemMargin != that.itemMargin) {
1045: return false;
1046: }
1047: if (this.drawBarOutline != that.drawBarOutline) {
1048: return false;
1049: }
1050: if (this.maximumBarWidth != that.maximumBarWidth) {
1051: return false;
1052: }
1053: if (this.minimumBarLength != that.minimumBarLength) {
1054: return false;
1055: }
1056: if (!ObjectUtilities.equal(this.gradientPaintTransformer,
1057: that.gradientPaintTransformer)) {
1058: return false;
1059: }
1060: if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback,
1061: that.positiveItemLabelPositionFallback)) {
1062: return false;
1063: }
1064: if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback,
1065: that.negativeItemLabelPositionFallback)) {
1066: return false;
1067: }
1068: return true;
1069:
1070: }
1071:
1072: }