1:
87:
88: package ;
89:
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96: import ;
97: import ;
98: import ;
99: import ;
100: import ;
101: import ;
102:
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: import ;
122: import ;
123:
124:
127: public class BarRenderer extends AbstractCategoryItemRenderer
128: implements Cloneable, PublicCloneable, Serializable {
129:
130:
131: private static final long serialVersionUID = 6000649414965887481L;
132:
133:
134: public static final double DEFAULT_ITEM_MARGIN = 0.20;
135:
136:
140: public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0;
141:
142:
143: private double itemMargin;
144:
145:
146: private boolean drawBarOutline;
147:
148:
149: private double maximumBarWidth;
150:
151:
152: private double minimumBarLength;
153:
154:
158: private GradientPaintTransformer gradientPaintTransformer;
159:
160:
164: private ItemLabelPosition positiveItemLabelPositionFallback;
165:
166:
170: private ItemLabelPosition negativeItemLabelPositionFallback;
171:
172:
173: private double upperClip;
174:
175:
176:
177: private double lowerClip;
178:
179:
180:
181: private double base;
182:
183:
187: private boolean includeBaseInRange;
188:
189:
192: public BarRenderer() {
193: super();
194: this.base = 0.0;
195: this.includeBaseInRange = true;
196: this.itemMargin = DEFAULT_ITEM_MARGIN;
197: this.drawBarOutline = false;
198: this.maximumBarWidth = 1.0;
199:
200: this.positiveItemLabelPositionFallback = null;
201: this.negativeItemLabelPositionFallback = null;
202: this.gradientPaintTransformer = new StandardGradientPaintTransformer();
203: this.minimumBarLength = 0.0;
204: }
205:
206:
214: public double getBase() {
215: return this.base;
216: }
217:
218:
226: public void setBase(double base) {
227: this.base = base;
228: fireChangeEvent();
229: }
230:
231:
239: public double getItemMargin() {
240: return this.itemMargin;
241: }
242:
243:
253: public void setItemMargin(double percent) {
254: this.itemMargin = percent;
255: fireChangeEvent();
256: }
257:
258:
265: public boolean isDrawBarOutline() {
266: return this.drawBarOutline;
267: }
268:
269:
277: public void setDrawBarOutline(boolean draw) {
278: this.drawBarOutline = draw;
279: fireChangeEvent();
280: }
281:
282:
290: public double getMaximumBarWidth() {
291: return this.maximumBarWidth;
292: }
293:
294:
303: public void setMaximumBarWidth(double percent) {
304: this.maximumBarWidth = percent;
305: fireChangeEvent();
306: }
307:
308:
316: public double getMinimumBarLength() {
317: return this.minimumBarLength;
318: }
319:
320:
334: public void setMinimumBarLength(double min) {
335: if (min < 0.0) {
336: throw new IllegalArgumentException("Requires 'min' >= 0.0");
337: }
338: this.minimumBarLength = min;
339: fireChangeEvent();
340: }
341:
342:
350: public GradientPaintTransformer getGradientPaintTransformer() {
351: return this.gradientPaintTransformer;
352: }
353:
354:
362: public void setGradientPaintTransformer(
363: GradientPaintTransformer transformer) {
364: this.gradientPaintTransformer = transformer;
365: fireChangeEvent();
366: }
367:
368:
376: public ItemLabelPosition getPositiveItemLabelPositionFallback() {
377: return this.positiveItemLabelPositionFallback;
378: }
379:
380:
389: public void setPositiveItemLabelPositionFallback(
390: ItemLabelPosition position) {
391: this.positiveItemLabelPositionFallback = position;
392: fireChangeEvent();
393: }
394:
395:
403: public ItemLabelPosition getNegativeItemLabelPositionFallback() {
404: return this.negativeItemLabelPositionFallback;
405: }
406:
407:
416: public void setNegativeItemLabelPositionFallback(
417: ItemLabelPosition position) {
418: this.negativeItemLabelPositionFallback = position;
419: fireChangeEvent();
420: }
421:
422:
434: public boolean getIncludeBaseInRange() {
435: return this.includeBaseInRange;
436: }
437:
438:
450: public void setIncludeBaseInRange(boolean include) {
451: if (this.includeBaseInRange != include) {
452: this.includeBaseInRange = include;
453: fireChangeEvent();
454: }
455: }
456:
457:
463: public double getLowerClip() {
464:
465: return this.lowerClip;
466: }
467:
468:
474: public double getUpperClip() {
475:
476: return this.upperClip;
477: }
478:
479:
492: public CategoryItemRendererState initialise(Graphics2D g2,
493: Rectangle2D dataArea,
494: CategoryPlot plot,
495: int rendererIndex,
496: PlotRenderingInfo info) {
497:
498: CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
499: rendererIndex, info);
500:
501:
502: ValueAxis rangeAxis = plot.getRangeAxisForDataset(rendererIndex);
503: this.lowerClip = rangeAxis.getRange().getLowerBound();
504: this.upperClip = rangeAxis.getRange().getUpperBound();
505:
506:
507: calculateBarWidth(plot, dataArea, rendererIndex, state);
508:
509: return state;
510:
511: }
512:
513:
521: protected void calculateBarWidth(CategoryPlot plot,
522: Rectangle2D dataArea,
523: int rendererIndex,
524: CategoryItemRendererState state) {
525:
526: CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
527: CategoryDataset dataset = plot.getDataset(rendererIndex);
528: if (dataset != null) {
529: int columns = dataset.getColumnCount();
530: int rows = dataset.getRowCount();
531: double space = 0.0;
532: PlotOrientation orientation = plot.getOrientation();
533: if (orientation == PlotOrientation.HORIZONTAL) {
534: space = dataArea.getHeight();
535: }
536: else if (orientation == PlotOrientation.VERTICAL) {
537: space = dataArea.getWidth();
538: }
539: double maxWidth = space * getMaximumBarWidth();
540: double categoryMargin = 0.0;
541: double currentItemMargin = 0.0;
542: if (columns > 1) {
543: categoryMargin = domainAxis.getCategoryMargin();
544: }
545: if (rows > 1) {
546: currentItemMargin = getItemMargin();
547: }
548: double used = space * (1 - domainAxis.getLowerMargin()
549: - domainAxis.getUpperMargin()
550: - categoryMargin - currentItemMargin);
551: if ((rows * columns) > 0) {
552: state.setBarWidth(Math.min(used / (rows * columns), maxWidth));
553: }
554: else {
555: state.setBarWidth(Math.min(used, maxWidth));
556: }
557: }
558: }
559:
560:
575: protected double calculateBarW0(CategoryPlot plot,
576: PlotOrientation orientation,
577: Rectangle2D dataArea,
578: CategoryAxis domainAxis,
579: CategoryItemRendererState state,
580: int row,
581: int column) {
582:
583: double space = 0.0;
584: if (orientation == PlotOrientation.HORIZONTAL) {
585: space = dataArea.getHeight();
586: }
587: else {
588: space = dataArea.getWidth();
589: }
590: double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
591: dataArea, plot.getDomainAxisEdge());
592: int seriesCount = getRowCount();
593: int categoryCount = getColumnCount();
594: if (seriesCount > 1) {
595: double seriesGap = space * getItemMargin()
596: / (categoryCount * (seriesCount - 1));
597: double seriesW = calculateSeriesWidth(space, domainAxis,
598: categoryCount, seriesCount);
599: barW0 = barW0 + row * (seriesW + seriesGap)
600: + (seriesW / 2.0) - (state.getBarWidth() / 2.0);
601: }
602: else {
603: barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
604: dataArea, plot.getDomainAxisEdge()) - state.getBarWidth()
605: / 2.0;
606: }
607: return barW0;
608: }
609:
610:
618: protected double[] calculateBarL0L1(double value) {
619: double lclip = getLowerClip();
620: double uclip = getUpperClip();
621: double barLow = Math.min(this.base, value);
622: double barHigh = Math.max(this.base, value);
623: if (barHigh < lclip) {
624: return null;
625: }
626: if (barLow > uclip) {
627: return null;
628: }
629: barLow = Math.max(barLow, lclip);
630: barHigh = Math.min(barHigh, uclip);
631: return new double[] {barLow, barHigh};
632: }
633:
634:
645: public Range findRangeBounds(CategoryDataset dataset) {
646: Range result = DatasetUtilities.findRangeBounds(dataset);
647: if (result != null) {
648: if (this.includeBaseInRange) {
649: result = Range.expandToInclude(result, this.base);
650: }
651: }
652: return result;
653: }
654:
655:
663: public LegendItem getLegendItem(int datasetIndex, int series) {
664:
665: CategoryPlot cp = getPlot();
666: if (cp == null) {
667: return null;
668: }
669:
670:
671: if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
672: return null;
673: }
674:
675: CategoryDataset dataset = cp.getDataset(datasetIndex);
676: String label = getLegendItemLabelGenerator().generateLabel(dataset,
677: series);
678: String description = label;
679: String toolTipText = null;
680: if (getLegendItemToolTipGenerator() != null) {
681: toolTipText = getLegendItemToolTipGenerator().generateLabel(
682: dataset, series);
683: }
684: String urlText = null;
685: if (getLegendItemURLGenerator() != null) {
686: urlText = getLegendItemURLGenerator().generateLabel(dataset,
687: series);
688: }
689: Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
690: Paint paint = lookupSeriesPaint(series);
691: Paint outlinePaint = lookupSeriesOutlinePaint(series);
692: Stroke outlineStroke = lookupSeriesOutlineStroke(series);
693:
694: LegendItem result = new LegendItem(label, description, toolTipText,
695: urlText, true, shape, true, paint, isDrawBarOutline(),
696: outlinePaint, outlineStroke, false, new Line2D.Float(),
697: new BasicStroke(1.0f), Color.black);
698: result.setDataset(dataset);
699: result.setDatasetIndex(datasetIndex);
700: result.setSeriesKey(dataset.getRowKey(series));
701: result.setSeriesIndex(series);
702: if (this.gradientPaintTransformer != null) {
703: result.setFillPaintTransformer(this.gradientPaintTransformer);
704: }
705: return result;
706: }
707:
708:
722: public void drawItem(Graphics2D g2,
723: CategoryItemRendererState state,
724: Rectangle2D dataArea,
725: CategoryPlot plot,
726: CategoryAxis domainAxis,
727: ValueAxis rangeAxis,
728: CategoryDataset dataset,
729: int row,
730: int column,
731: int pass) {
732:
733:
734: Number dataValue = dataset.getValue(row, column);
735: if (dataValue == null) {
736: return;
737: }
738:
739: double value = dataValue.doubleValue();
740: PlotOrientation orientation = plot.getOrientation();
741: double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis,
742: state, row, column);
743: double[] barL0L1 = calculateBarL0L1(value);
744: if (barL0L1 == null) {
745: return;
746: }
747:
748: RectangleEdge edge = plot.getRangeAxisEdge();
749: double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge);
750: double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge);
751:
752:
753:
754:
755:
756:
757:
758:
759: boolean positive = (value >= this.base);
760: boolean inverted = rangeAxis.isInverted();
761: double barL0 = Math.min(transL0, transL1);
762: double barLength = Math.abs(transL1 - transL0);
763: double barLengthAdj = 0.0;
764: if (barLength > 0.0 && barLength < getMinimumBarLength()) {
765: barLengthAdj = getMinimumBarLength() - barLength;
766: }
767: double barL0Adj = 0.0;
768: if (orientation == PlotOrientation.HORIZONTAL) {
769: if (positive && inverted || !positive && !inverted) {
770: barL0Adj = barLengthAdj;
771: }
772: }
773: else {
774: if (positive && !inverted || !positive && inverted) {
775: barL0Adj = barLengthAdj;
776: }
777: }
778:
779:
780: Rectangle2D bar = null;
781: if (orientation == PlotOrientation.HORIZONTAL) {
782: bar = new Rectangle2D.Double(barL0 - barL0Adj, barW0,
783: barLength + barLengthAdj, state.getBarWidth());
784: }
785: else {
786: bar = new Rectangle2D.Double(barW0, barL0 - barL0Adj,
787: state.getBarWidth(), barLength + barLengthAdj);
788: }
789: Paint itemPaint = getItemPaint(row, column);
790: GradientPaintTransformer t = getGradientPaintTransformer();
791: if (t != null && itemPaint instanceof GradientPaint) {
792: itemPaint = t.transform((GradientPaint) itemPaint, bar);
793: }
794: g2.setPaint(itemPaint);
795: g2.fill(bar);
796:
797:
798: if (isDrawBarOutline()
799: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
800: Stroke stroke = getItemOutlineStroke(row, column);
801: Paint paint = getItemOutlinePaint(row, column);
802: if (stroke != null && paint != null) {
803: g2.setStroke(stroke);
804: g2.setPaint(paint);
805: g2.draw(bar);
806: }
807: }
808:
809: CategoryItemLabelGenerator generator
810: = getItemLabelGenerator(row, column);
811: if (generator != null && isItemLabelVisible(row, column)) {
812: drawItemLabel(g2, dataset, row, column, plot, generator, bar,
813: (value < 0.0));
814: }
815:
816:
817: EntityCollection entities = state.getEntityCollection();
818: if (entities != null) {
819: addItemEntity(entities, dataset, row, column, bar);
820: }
821:
822: }
823:
824:
834: protected double calculateSeriesWidth(double space, CategoryAxis axis,
835: int categories, int series) {
836: double factor = 1.0 - getItemMargin() - axis.getLowerMargin()
837: - axis.getUpperMargin();
838: if (categories > 1) {
839: factor = factor - axis.getCategoryMargin();
840: }
841: return (space * factor) / (categories * series);
842: }
843:
844:
857: protected void drawItemLabel(Graphics2D g2,
858: CategoryDataset data,
859: int row,
860: int column,
861: CategoryPlot plot,
862: CategoryItemLabelGenerator generator,
863: Rectangle2D bar,
864: boolean negative) {
865:
866: String label = generator.generateLabel(data, row, column);
867: if (label == null) {
868: return;
869: }
870:
871: Font labelFont = getItemLabelFont(row, column);
872: g2.setFont(labelFont);
873: Paint paint = getItemLabelPaint(row, column);
874: g2.setPaint(paint);
875:
876:
877: ItemLabelPosition position = null;
878: if (!negative) {
879: position = getPositiveItemLabelPosition(row, column);
880: }
881: else {
882: position = getNegativeItemLabelPosition(row, column);
883: }
884:
885:
886: Point2D anchorPoint = calculateLabelAnchorPoint(
887: position.getItemLabelAnchor(), bar, plot.getOrientation());
888:
889: if (isInternalAnchor(position.getItemLabelAnchor())) {
890: Shape bounds = TextUtilities.calculateRotatedStringBounds(label,
891: g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
892: position.getTextAnchor(), position.getAngle(),
893: position.getRotationAnchor());
894:
895: if (bounds != null) {
896: if (!bar.contains(bounds.getBounds2D())) {
897: if (!negative) {
898: position = getPositiveItemLabelPositionFallback();
899: }
900: else {
901: position = getNegativeItemLabelPositionFallback();
902: }
903: if (position != null) {
904: anchorPoint = calculateLabelAnchorPoint(
905: position.getItemLabelAnchor(), bar,
906: plot.getOrientation());
907: }
908: }
909: }
910:
911: }
912:
913: if (position != null) {
914: TextUtilities.drawRotatedString(label, g2,
915: (float) anchorPoint.getX(), (float) anchorPoint.getY(),
916: position.getTextAnchor(), position.getAngle(),
917: position.getRotationAnchor());
918: }
919: }
920:
921:
930: private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
931: Rectangle2D bar,
932: PlotOrientation orientation) {
933:
934: Point2D result = null;
935: double offset = getItemLabelAnchorOffset();
936: double x0 = bar.getX() - offset;
937: double x1 = bar.getX();
938: double x2 = bar.getX() + offset;
939: double x3 = bar.getCenterX();
940: double x4 = bar.getMaxX() - offset;
941: double x5 = bar.getMaxX();
942: double x6 = bar.getMaxX() + offset;
943:
944: double y0 = bar.getMaxY() + offset;
945: double y1 = bar.getMaxY();
946: double y2 = bar.getMaxY() - offset;
947: double y3 = bar.getCenterY();
948: double y4 = bar.getMinY() + offset;
949: double y5 = bar.getMinY();
950: double y6 = bar.getMinY() - offset;
951:
952: if (anchor == ItemLabelAnchor.CENTER) {
953: result = new Point2D.Double(x3, y3);
954: }
955: else if (anchor == ItemLabelAnchor.INSIDE1) {
956: result = new Point2D.Double(x4, y4);
957: }
958: else if (anchor == ItemLabelAnchor.INSIDE2) {
959: result = new Point2D.Double(x4, y4);
960: }
961: else if (anchor == ItemLabelAnchor.INSIDE3) {
962: result = new Point2D.Double(x4, y3);
963: }
964: else if (anchor == ItemLabelAnchor.INSIDE4) {
965: result = new Point2D.Double(x4, y2);
966: }
967: else if (anchor == ItemLabelAnchor.INSIDE5) {
968: result = new Point2D.Double(x4, y2);
969: }
970: else if (anchor == ItemLabelAnchor.INSIDE6) {
971: result = new Point2D.Double(x3, y2);
972: }
973: else if (anchor == ItemLabelAnchor.INSIDE7) {
974: result = new Point2D.Double(x2, y2);
975: }
976: else if (anchor == ItemLabelAnchor.INSIDE8) {
977: result = new Point2D.Double(x2, y2);
978: }
979: else if (anchor == ItemLabelAnchor.INSIDE9) {
980: result = new Point2D.Double(x2, y3);
981: }
982: else if (anchor == ItemLabelAnchor.INSIDE10) {
983: result = new Point2D.Double(x2, y4);
984: }
985: else if (anchor == ItemLabelAnchor.INSIDE11) {
986: result = new Point2D.Double(x2, y4);
987: }
988: else if (anchor == ItemLabelAnchor.INSIDE12) {
989: result = new Point2D.Double(x3, y4);
990: }
991: else if (anchor == ItemLabelAnchor.OUTSIDE1) {
992: result = new Point2D.Double(x5, y6);
993: }
994: else if (anchor == ItemLabelAnchor.OUTSIDE2) {
995: result = new Point2D.Double(x6, y5);
996: }
997: else if (anchor == ItemLabelAnchor.OUTSIDE3) {
998: result = new Point2D.Double(x6, y3);
999: }
1000: else if (anchor == ItemLabelAnchor.OUTSIDE4) {
1001: result = new Point2D.Double(x6, y1);
1002: }
1003: else if (anchor == ItemLabelAnchor.OUTSIDE5) {
1004: result = new Point2D.Double(x5, y0);
1005: }
1006: else if (anchor == ItemLabelAnchor.OUTSIDE6) {
1007: result = new Point2D.Double(x3, y0);
1008: }
1009: else if (anchor == ItemLabelAnchor.OUTSIDE7) {
1010: result = new Point2D.Double(x1, y0);
1011: }
1012: else if (anchor == ItemLabelAnchor.OUTSIDE8) {
1013: result = new Point2D.Double(x0, y1);
1014: }
1015: else if (anchor == ItemLabelAnchor.OUTSIDE9) {
1016: result = new Point2D.Double(x0, y3);
1017: }
1018: else if (anchor == ItemLabelAnchor.OUTSIDE10) {
1019: result = new Point2D.Double(x0, y5);
1020: }
1021: else if (anchor == ItemLabelAnchor.OUTSIDE11) {
1022: result = new Point2D.Double(x1, y6);
1023: }
1024: else if (anchor == ItemLabelAnchor.OUTSIDE12) {
1025: result = new Point2D.Double(x3, y6);
1026: }
1027:
1028: return result;
1029:
1030: }
1031:
1032:
1039: private boolean isInternalAnchor(ItemLabelAnchor anchor) {
1040: return anchor == ItemLabelAnchor.CENTER
1041: || anchor == ItemLabelAnchor.INSIDE1
1042: || anchor == ItemLabelAnchor.INSIDE2
1043: || anchor == ItemLabelAnchor.INSIDE3
1044: || anchor == ItemLabelAnchor.INSIDE4
1045: || anchor == ItemLabelAnchor.INSIDE5
1046: || anchor == ItemLabelAnchor.INSIDE6
1047: || anchor == ItemLabelAnchor.INSIDE7
1048: || anchor == ItemLabelAnchor.INSIDE8
1049: || anchor == ItemLabelAnchor.INSIDE9
1050: || anchor == ItemLabelAnchor.INSIDE10
1051: || anchor == ItemLabelAnchor.INSIDE11
1052: || anchor == ItemLabelAnchor.INSIDE12;
1053: }
1054:
1055:
1062: public boolean equals(Object obj) {
1063:
1064: if (obj == this) {
1065: return true;
1066: }
1067: if (!(obj instanceof BarRenderer)) {
1068: return false;
1069: }
1070: if (!super.equals(obj)) {
1071: return false;
1072: }
1073: BarRenderer that = (BarRenderer) obj;
1074: if (this.base != that.base) {
1075: return false;
1076: }
1077: if (this.itemMargin != that.itemMargin) {
1078: return false;
1079: }
1080: if (this.drawBarOutline != that.drawBarOutline) {
1081: return false;
1082: }
1083: if (this.maximumBarWidth != that.maximumBarWidth) {
1084: return false;
1085: }
1086: if (this.minimumBarLength != that.minimumBarLength) {
1087: return false;
1088: }
1089: if (!ObjectUtilities.equal(this.gradientPaintTransformer,
1090: that.gradientPaintTransformer)) {
1091: return false;
1092: }
1093: if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback,
1094: that.positiveItemLabelPositionFallback)) {
1095: return false;
1096: }
1097: if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback,
1098: that.negativeItemLabelPositionFallback)) {
1099: return false;
1100: }
1101: return true;
1102:
1103: }
1104:
1105: }