1:
83:
84: package ;
85:
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96: import ;
97: import ;
98:
99: import ;
100: import ;
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:
120:
123: public class BarRenderer extends AbstractCategoryItemRenderer
124: implements Cloneable, PublicCloneable, Serializable {
125:
126:
127: private static final long serialVersionUID = 6000649414965887481L;
128:
129:
130: public static final double DEFAULT_ITEM_MARGIN = 0.20;
131:
132:
136: public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0;
137:
138:
139: private double itemMargin;
140:
141:
142: private boolean drawBarOutline;
143:
144:
145: private double maximumBarWidth;
146:
147:
148: private double minimumBarLength;
149:
150:
154: private GradientPaintTransformer gradientPaintTransformer;
155:
156:
160: private ItemLabelPosition positiveItemLabelPositionFallback;
161:
162:
166: private ItemLabelPosition negativeItemLabelPositionFallback;
167:
168:
169: private double upperClip;
170:
171:
172:
173: private double lowerClip;
174:
175:
176:
177: private double base;
178:
179:
183: private boolean includeBaseInRange;
184:
185:
188: public BarRenderer() {
189: super();
190: this.base = 0.0;
191: this.includeBaseInRange = true;
192: this.itemMargin = DEFAULT_ITEM_MARGIN;
193: this.drawBarOutline = true;
194: this.maximumBarWidth = 1.0;
195:
196: this.positiveItemLabelPositionFallback = null;
197: this.negativeItemLabelPositionFallback = null;
198: this.gradientPaintTransformer = new StandardGradientPaintTransformer();
199: this.minimumBarLength = 0.0;
200: }
201:
202:
210: public double getBase() {
211: return this.base;
212: }
213:
214:
222: public void setBase(double base) {
223: this.base = base;
224: notifyListeners(new RendererChangeEvent(this));
225: }
226:
227:
235: public double getItemMargin() {
236: return this.itemMargin;
237: }
238:
239:
249: public void setItemMargin(double percent) {
250: this.itemMargin = percent;
251: notifyListeners(new RendererChangeEvent(this));
252: }
253:
254:
261: public boolean isDrawBarOutline() {
262: return this.drawBarOutline;
263: }
264:
265:
273: public void setDrawBarOutline(boolean draw) {
274: this.drawBarOutline = draw;
275: notifyListeners(new RendererChangeEvent(this));
276: }
277:
278:
286: public double getMaximumBarWidth() {
287: return this.maximumBarWidth;
288: }
289:
290:
299: public void setMaximumBarWidth(double percent) {
300: this.maximumBarWidth = percent;
301: notifyListeners(new RendererChangeEvent(this));
302: }
303:
304:
311: public double getMinimumBarLength() {
312: return this.minimumBarLength;
313: }
314:
315:
325: public void setMinimumBarLength(double min) {
326: this.minimumBarLength = min;
327: notifyListeners(new RendererChangeEvent(this));
328: }
329:
330:
338: public GradientPaintTransformer getGradientPaintTransformer() {
339: return this.gradientPaintTransformer;
340: }
341:
342:
350: public void setGradientPaintTransformer(
351: GradientPaintTransformer transformer) {
352: this.gradientPaintTransformer = transformer;
353: notifyListeners(new RendererChangeEvent(this));
354: }
355:
356:
364: public ItemLabelPosition getPositiveItemLabelPositionFallback() {
365: return this.positiveItemLabelPositionFallback;
366: }
367:
368:
377: public void setPositiveItemLabelPositionFallback(
378: ItemLabelPosition position) {
379: this.positiveItemLabelPositionFallback = position;
380: notifyListeners(new RendererChangeEvent(this));
381: }
382:
383:
391: public ItemLabelPosition getNegativeItemLabelPositionFallback() {
392: return this.negativeItemLabelPositionFallback;
393: }
394:
395:
404: public void setNegativeItemLabelPositionFallback(
405: ItemLabelPosition position) {
406: this.negativeItemLabelPositionFallback = position;
407: notifyListeners(new RendererChangeEvent(this));
408: }
409:
410:
422: public boolean getIncludeBaseInRange() {
423: return this.includeBaseInRange;
424: }
425:
426:
438: public void setIncludeBaseInRange(boolean include) {
439: if (this.includeBaseInRange != include) {
440: this.includeBaseInRange = include;
441: notifyListeners(new RendererChangeEvent(this));
442: }
443: }
444:
445:
451: public double getLowerClip() {
452:
453: return this.lowerClip;
454: }
455:
456:
462: public double getUpperClip() {
463:
464: return this.upperClip;
465: }
466:
467:
480: public CategoryItemRendererState initialise(Graphics2D g2,
481: Rectangle2D dataArea,
482: CategoryPlot plot,
483: int rendererIndex,
484: PlotRenderingInfo info) {
485:
486: CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
487: rendererIndex, info);
488:
489:
490: ValueAxis rangeAxis = plot.getRangeAxisForDataset(rendererIndex);
491: this.lowerClip = rangeAxis.getRange().getLowerBound();
492: this.upperClip = rangeAxis.getRange().getUpperBound();
493:
494:
495: calculateBarWidth(plot, dataArea, rendererIndex, state);
496:
497: return state;
498:
499: }
500:
501:
509: protected void calculateBarWidth(CategoryPlot plot,
510: Rectangle2D dataArea,
511: int rendererIndex,
512: CategoryItemRendererState state) {
513:
514: CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
515: CategoryDataset dataset = plot.getDataset(rendererIndex);
516: if (dataset != null) {
517: int columns = dataset.getColumnCount();
518: int rows = dataset.getRowCount();
519: double space = 0.0;
520: PlotOrientation orientation = plot.getOrientation();
521: if (orientation == PlotOrientation.HORIZONTAL) {
522: space = dataArea.getHeight();
523: }
524: else if (orientation == PlotOrientation.VERTICAL) {
525: space = dataArea.getWidth();
526: }
527: double maxWidth = space * getMaximumBarWidth();
528: double categoryMargin = 0.0;
529: double currentItemMargin = 0.0;
530: if (columns > 1) {
531: categoryMargin = domainAxis.getCategoryMargin();
532: }
533: if (rows > 1) {
534: currentItemMargin = getItemMargin();
535: }
536: double used = space * (1 - domainAxis.getLowerMargin()
537: - domainAxis.getUpperMargin()
538: - categoryMargin - currentItemMargin);
539: if ((rows * columns) > 0) {
540: state.setBarWidth(Math.min(used / (rows * columns), maxWidth));
541: }
542: else {
543: state.setBarWidth(Math.min(used, maxWidth));
544: }
545: }
546: }
547:
548:
563: protected double calculateBarW0(CategoryPlot plot,
564: PlotOrientation orientation,
565: Rectangle2D dataArea,
566: CategoryAxis domainAxis,
567: CategoryItemRendererState state,
568: int row,
569: int column) {
570:
571: double space = 0.0;
572: if (orientation == PlotOrientation.HORIZONTAL) {
573: space = dataArea.getHeight();
574: }
575: else {
576: space = dataArea.getWidth();
577: }
578: double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
579: dataArea, plot.getDomainAxisEdge());
580: int seriesCount = getRowCount();
581: int categoryCount = getColumnCount();
582: if (seriesCount > 1) {
583: double seriesGap = space * getItemMargin()
584: / (categoryCount * (seriesCount - 1));
585: double seriesW = calculateSeriesWidth(space, domainAxis,
586: categoryCount, seriesCount);
587: barW0 = barW0 + row * (seriesW + seriesGap)
588: + (seriesW / 2.0) - (state.getBarWidth() / 2.0);
589: }
590: else {
591: barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
592: dataArea, plot.getDomainAxisEdge()) - state.getBarWidth()
593: / 2.0;
594: }
595: return barW0;
596: }
597:
598:
606: protected double[] calculateBarL0L1(double value) {
607: double lclip = getLowerClip();
608: double uclip = getUpperClip();
609: double barLow = Math.min(this.base, value);
610: double barHigh = Math.max(this.base, value);
611: if (barHigh < lclip) {
612: return null;
613: }
614: if (barLow > uclip) {
615: return null;
616: }
617: barLow = Math.max(barLow, lclip);
618: barHigh = Math.min(barHigh, uclip);
619: return new double[] {barLow, barHigh};
620: }
621:
622:
633: public Range findRangeBounds(CategoryDataset dataset) {
634: Range result = DatasetUtilities.findRangeBounds(dataset);
635: if (result != null) {
636: if (this.includeBaseInRange) {
637: result = Range.expandToInclude(result, this.base);
638: }
639: }
640: return result;
641: }
642:
643:
651: public LegendItem getLegendItem(int datasetIndex, int series) {
652:
653: CategoryPlot cp = getPlot();
654: if (cp == null) {
655: return null;
656: }
657:
658: CategoryDataset dataset;
659: dataset = cp.getDataset(datasetIndex);
660: String label = getLegendItemLabelGenerator().generateLabel(dataset,
661: series);
662: String description = label;
663: String toolTipText = null;
664: if (getLegendItemToolTipGenerator() != null) {
665: toolTipText = getLegendItemToolTipGenerator().generateLabel(
666: dataset, series);
667: }
668: String urlText = null;
669: if (getLegendItemURLGenerator() != null) {
670: urlText = getLegendItemURLGenerator().generateLabel(dataset,
671: series);
672: }
673: Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
674: Paint paint = getSeriesPaint(series);
675: Paint outlinePaint = getSeriesOutlinePaint(series);
676: Stroke outlineStroke = getSeriesOutlineStroke(series);
677:
678: LegendItem result = new LegendItem(label, description, toolTipText,
679: urlText, true, shape, true, paint, isDrawBarOutline(),
680: outlinePaint, outlineStroke, false, new Line2D.Float(),
681: new BasicStroke(1.0f), Color.black);
682: if (this.gradientPaintTransformer != null) {
683: result.setFillPaintTransformer(this.gradientPaintTransformer);
684: }
685: return result;
686: }
687:
688:
702: public void drawItem(Graphics2D g2,
703: CategoryItemRendererState state,
704: Rectangle2D dataArea,
705: CategoryPlot plot,
706: CategoryAxis domainAxis,
707: ValueAxis rangeAxis,
708: CategoryDataset dataset,
709: int row,
710: int column,
711: int pass) {
712:
713:
714: Number dataValue = dataset.getValue(row, column);
715: if (dataValue == null) {
716: return;
717: }
718:
719: double value = dataValue.doubleValue();
720:
721: PlotOrientation orientation = plot.getOrientation();
722: double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis,
723: state, row, column);
724: double[] barL0L1 = calculateBarL0L1(value);
725: if (barL0L1 == null) {
726: return;
727: }
728:
729: RectangleEdge edge = plot.getRangeAxisEdge();
730: double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge);
731: double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge);
732: double barL0 = Math.min(transL0, transL1);
733: double barLength = Math.max(Math.abs(transL1 - transL0),
734: getMinimumBarLength());
735:
736:
737: Rectangle2D bar = null;
738: if (orientation == PlotOrientation.HORIZONTAL) {
739: bar = new Rectangle2D.Double(barL0, barW0, barLength,
740: state.getBarWidth());
741: }
742: else {
743: bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
744: barLength);
745: }
746: Paint itemPaint = getItemPaint(row, column);
747: GradientPaintTransformer t = getGradientPaintTransformer();
748: if (t != null && itemPaint instanceof GradientPaint) {
749: itemPaint = t.transform((GradientPaint) itemPaint, bar);
750: }
751: g2.setPaint(itemPaint);
752: g2.fill(bar);
753:
754:
755: if (isDrawBarOutline()
756: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
757: Stroke stroke = getItemOutlineStroke(row, column);
758: Paint paint = getItemOutlinePaint(row, column);
759: if (stroke != null && paint != null) {
760: g2.setStroke(stroke);
761: g2.setPaint(paint);
762: g2.draw(bar);
763: }
764: }
765:
766: CategoryItemLabelGenerator generator
767: = getItemLabelGenerator(row, column);
768: if (generator != null && isItemLabelVisible(row, column)) {
769: drawItemLabel(g2, dataset, row, column, plot, generator, bar,
770: (value < 0.0));
771: }
772:
773:
774: EntityCollection entities = state.getEntityCollection();
775: if (entities != null) {
776: addItemEntity(entities, dataset, row, column, bar);
777: }
778:
779: }
780:
781:
791: protected double calculateSeriesWidth(double space, CategoryAxis axis,
792: int categories, int series) {
793: double factor = 1.0 - getItemMargin() - axis.getLowerMargin()
794: - axis.getUpperMargin();
795: if (categories > 1) {
796: factor = factor - axis.getCategoryMargin();
797: }
798: return (space * factor) / (categories * series);
799: }
800:
801:
814: protected void drawItemLabel(Graphics2D g2,
815: CategoryDataset data,
816: int row,
817: int column,
818: CategoryPlot plot,
819: CategoryItemLabelGenerator generator,
820: Rectangle2D bar,
821: boolean negative) {
822:
823: String label = generator.generateLabel(data, row, column);
824: if (label == null) {
825: return;
826: }
827:
828: Font labelFont = getItemLabelFont(row, column);
829: g2.setFont(labelFont);
830: Paint paint = getItemLabelPaint(row, column);
831: g2.setPaint(paint);
832:
833:
834: ItemLabelPosition position = null;
835: if (!negative) {
836: position = getPositiveItemLabelPosition(row, column);
837: }
838: else {
839: position = getNegativeItemLabelPosition(row, column);
840: }
841:
842:
843: Point2D anchorPoint = calculateLabelAnchorPoint(
844: position.getItemLabelAnchor(), bar, plot.getOrientation());
845:
846: if (isInternalAnchor(position.getItemLabelAnchor())) {
847: Shape bounds = TextUtilities.calculateRotatedStringBounds(label,
848: g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
849: position.getTextAnchor(), position.getAngle(),
850: position.getRotationAnchor());
851:
852: if (bounds != null) {
853: if (!bar.contains(bounds.getBounds2D())) {
854: if (!negative) {
855: position = getPositiveItemLabelPositionFallback();
856: }
857: else {
858: position = getNegativeItemLabelPositionFallback();
859: }
860: if (position != null) {
861: anchorPoint = calculateLabelAnchorPoint(
862: position.getItemLabelAnchor(), bar,
863: plot.getOrientation());
864: }
865: }
866: }
867:
868: }
869:
870: if (position != null) {
871: TextUtilities.drawRotatedString(label, g2,
872: (float) anchorPoint.getX(), (float) anchorPoint.getY(),
873: position.getTextAnchor(), position.getAngle(),
874: position.getRotationAnchor());
875: }
876: }
877:
878:
887: private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
888: Rectangle2D bar,
889: PlotOrientation orientation) {
890:
891: Point2D result = null;
892: double offset = getItemLabelAnchorOffset();
893: double x0 = bar.getX() - offset;
894: double x1 = bar.getX();
895: double x2 = bar.getX() + offset;
896: double x3 = bar.getCenterX();
897: double x4 = bar.getMaxX() - offset;
898: double x5 = bar.getMaxX();
899: double x6 = bar.getMaxX() + offset;
900:
901: double y0 = bar.getMaxY() + offset;
902: double y1 = bar.getMaxY();
903: double y2 = bar.getMaxY() - offset;
904: double y3 = bar.getCenterY();
905: double y4 = bar.getMinY() + offset;
906: double y5 = bar.getMinY();
907: double y6 = bar.getMinY() - offset;
908:
909: if (anchor == ItemLabelAnchor.CENTER) {
910: result = new Point2D.Double(x3, y3);
911: }
912: else if (anchor == ItemLabelAnchor.INSIDE1) {
913: result = new Point2D.Double(x4, y4);
914: }
915: else if (anchor == ItemLabelAnchor.INSIDE2) {
916: result = new Point2D.Double(x4, y4);
917: }
918: else if (anchor == ItemLabelAnchor.INSIDE3) {
919: result = new Point2D.Double(x4, y3);
920: }
921: else if (anchor == ItemLabelAnchor.INSIDE4) {
922: result = new Point2D.Double(x4, y2);
923: }
924: else if (anchor == ItemLabelAnchor.INSIDE5) {
925: result = new Point2D.Double(x4, y2);
926: }
927: else if (anchor == ItemLabelAnchor.INSIDE6) {
928: result = new Point2D.Double(x3, y2);
929: }
930: else if (anchor == ItemLabelAnchor.INSIDE7) {
931: result = new Point2D.Double(x2, y2);
932: }
933: else if (anchor == ItemLabelAnchor.INSIDE8) {
934: result = new Point2D.Double(x2, y2);
935: }
936: else if (anchor == ItemLabelAnchor.INSIDE9) {
937: result = new Point2D.Double(x2, y3);
938: }
939: else if (anchor == ItemLabelAnchor.INSIDE10) {
940: result = new Point2D.Double(x2, y4);
941: }
942: else if (anchor == ItemLabelAnchor.INSIDE11) {
943: result = new Point2D.Double(x2, y4);
944: }
945: else if (anchor == ItemLabelAnchor.INSIDE12) {
946: result = new Point2D.Double(x3, y4);
947: }
948: else if (anchor == ItemLabelAnchor.OUTSIDE1) {
949: result = new Point2D.Double(x5, y6);
950: }
951: else if (anchor == ItemLabelAnchor.OUTSIDE2) {
952: result = new Point2D.Double(x6, y5);
953: }
954: else if (anchor == ItemLabelAnchor.OUTSIDE3) {
955: result = new Point2D.Double(x6, y3);
956: }
957: else if (anchor == ItemLabelAnchor.OUTSIDE4) {
958: result = new Point2D.Double(x6, y1);
959: }
960: else if (anchor == ItemLabelAnchor.OUTSIDE5) {
961: result = new Point2D.Double(x5, y0);
962: }
963: else if (anchor == ItemLabelAnchor.OUTSIDE6) {
964: result = new Point2D.Double(x3, y0);
965: }
966: else if (anchor == ItemLabelAnchor.OUTSIDE7) {
967: result = new Point2D.Double(x1, y0);
968: }
969: else if (anchor == ItemLabelAnchor.OUTSIDE8) {
970: result = new Point2D.Double(x0, y1);
971: }
972: else if (anchor == ItemLabelAnchor.OUTSIDE9) {
973: result = new Point2D.Double(x0, y3);
974: }
975: else if (anchor == ItemLabelAnchor.OUTSIDE10) {
976: result = new Point2D.Double(x0, y5);
977: }
978: else if (anchor == ItemLabelAnchor.OUTSIDE11) {
979: result = new Point2D.Double(x1, y6);
980: }
981: else if (anchor == ItemLabelAnchor.OUTSIDE12) {
982: result = new Point2D.Double(x3, y6);
983: }
984:
985: return result;
986:
987: }
988:
989:
996: private boolean isInternalAnchor(ItemLabelAnchor anchor) {
997: return anchor == ItemLabelAnchor.CENTER
998: || anchor == ItemLabelAnchor.INSIDE1
999: || anchor == ItemLabelAnchor.INSIDE2
1000: || anchor == ItemLabelAnchor.INSIDE3
1001: || anchor == ItemLabelAnchor.INSIDE4
1002: || anchor == ItemLabelAnchor.INSIDE5
1003: || anchor == ItemLabelAnchor.INSIDE6
1004: || anchor == ItemLabelAnchor.INSIDE7
1005: || anchor == ItemLabelAnchor.INSIDE8
1006: || anchor == ItemLabelAnchor.INSIDE9
1007: || anchor == ItemLabelAnchor.INSIDE10
1008: || anchor == ItemLabelAnchor.INSIDE11
1009: || anchor == ItemLabelAnchor.INSIDE12;
1010: }
1011:
1012:
1019: public boolean equals(Object obj) {
1020:
1021: if (obj == this) {
1022: return true;
1023: }
1024: if (!(obj instanceof BarRenderer)) {
1025: return false;
1026: }
1027: if (!super.equals(obj)) {
1028: return false;
1029: }
1030: BarRenderer that = (BarRenderer) obj;
1031: if (this.base != that.base) {
1032: return false;
1033: }
1034: if (this.itemMargin != that.itemMargin) {
1035: return false;
1036: }
1037: if (this.drawBarOutline != that.drawBarOutline) {
1038: return false;
1039: }
1040: if (this.maximumBarWidth != that.maximumBarWidth) {
1041: return false;
1042: }
1043: if (this.minimumBarLength != that.minimumBarLength) {
1044: return false;
1045: }
1046: if (!ObjectUtilities.equal(this.gradientPaintTransformer,
1047: that.gradientPaintTransformer)) {
1048: return false;
1049: }
1050: if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback,
1051: that.positiveItemLabelPositionFallback)) {
1052: return false;
1053: }
1054: if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback,
1055: that.negativeItemLabelPositionFallback)) {
1056: return false;
1057: }
1058: return true;
1059:
1060: }
1061:
1062: }