1:
81:
82: package ;
83:
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96:
97: import ;
98: import ;
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:
118:
121: public class BarRenderer extends AbstractCategoryItemRenderer
122: implements Cloneable, PublicCloneable, Serializable {
123:
124:
125: private static final long serialVersionUID = 6000649414965887481L;
126:
127:
128: public static final double DEFAULT_ITEM_MARGIN = 0.20;
129:
130:
134: public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0;
135:
136:
137: private double itemMargin;
138:
139:
140: private boolean drawBarOutline;
141:
142:
143: private double maximumBarWidth;
144:
145:
146: private double minimumBarLength;
147:
148:
152: private GradientPaintTransformer gradientPaintTransformer;
153:
154:
158: private ItemLabelPosition positiveItemLabelPositionFallback;
159:
160:
164: private ItemLabelPosition negativeItemLabelPositionFallback;
165:
166:
167: private double upperClip;
168:
169:
170:
171: private double lowerClip;
172:
173:
174:
175: private double base;
176:
177:
181: private boolean includeBaseInRange;
182:
183:
186: public BarRenderer() {
187: super();
188: this.base = 0.0;
189: this.includeBaseInRange = true;
190: this.itemMargin = DEFAULT_ITEM_MARGIN;
191: this.drawBarOutline = true;
192: this.maximumBarWidth = 1.0;
193:
194: this.positiveItemLabelPositionFallback = null;
195: this.negativeItemLabelPositionFallback = null;
196: this.gradientPaintTransformer = new StandardGradientPaintTransformer();
197: this.minimumBarLength = 0.0;
198: }
199:
200:
205: public double getBase() {
206: return this.base;
207: }
208:
209:
215: public void setBase(double base) {
216: this.base = base;
217: notifyListeners(new RendererChangeEvent(this));
218: }
219:
220:
226: public double getItemMargin() {
227: return this.itemMargin;
228: }
229:
230:
238: public void setItemMargin(double percent) {
239: this.itemMargin = percent;
240: notifyListeners(new RendererChangeEvent(this));
241: }
242:
243:
248: public boolean isDrawBarOutline() {
249: return this.drawBarOutline;
250: }
251:
252:
258: public void setDrawBarOutline(boolean draw) {
259: this.drawBarOutline = draw;
260: notifyListeners(new RendererChangeEvent(this));
261: }
262:
263:
269: public double getMaximumBarWidth() {
270: return this.maximumBarWidth;
271: }
272:
273:
280: public void setMaximumBarWidth(double percent) {
281: this.maximumBarWidth = percent;
282: notifyListeners(new RendererChangeEvent(this));
283: }
284:
285:
290: public double getMinimumBarLength() {
291: return this.minimumBarLength;
292: }
293:
294:
302: public void setMinimumBarLength(double min) {
303: this.minimumBarLength = min;
304: notifyListeners(new RendererChangeEvent(this));
305: }
306:
307:
313: public GradientPaintTransformer getGradientPaintTransformer() {
314: return this.gradientPaintTransformer;
315: }
316:
317:
323: public void setGradientPaintTransformer(
324: GradientPaintTransformer transformer) {
325: this.gradientPaintTransformer = transformer;
326: notifyListeners(new RendererChangeEvent(this));
327: }
328:
329:
335: public ItemLabelPosition getPositiveItemLabelPositionFallback() {
336: return this.positiveItemLabelPositionFallback;
337: }
338:
339:
346: public void setPositiveItemLabelPositionFallback(
347: ItemLabelPosition position) {
348: this.positiveItemLabelPositionFallback = position;
349: notifyListeners(new RendererChangeEvent(this));
350: }
351:
352:
358: public ItemLabelPosition getNegativeItemLabelPositionFallback() {
359: return this.negativeItemLabelPositionFallback;
360: }
361:
362:
369: public void setNegativeItemLabelPositionFallback(
370: ItemLabelPosition position) {
371: this.negativeItemLabelPositionFallback = position;
372: notifyListeners(new RendererChangeEvent(this));
373: }
374:
375:
385: public boolean getIncludeBaseInRange() {
386: return this.includeBaseInRange;
387: }
388:
389:
399: public void setIncludeBaseInRange(boolean include) {
400: if (this.includeBaseInRange != include) {
401: this.includeBaseInRange = include;
402: notifyListeners(new RendererChangeEvent(this));
403: }
404: }
405:
406:
412: public double getLowerClip() {
413:
414: return this.lowerClip;
415: }
416:
417:
423: public double getUpperClip() {
424:
425: return this.upperClip;
426: }
427:
428:
441: public CategoryItemRendererState initialise(Graphics2D g2,
442: Rectangle2D dataArea,
443: CategoryPlot plot,
444: int rendererIndex,
445: PlotRenderingInfo info) {
446:
447: CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
448: rendererIndex, info);
449:
450:
451: ValueAxis rangeAxis = getRangeAxis(plot, rendererIndex);
452: this.lowerClip = rangeAxis.getRange().getLowerBound();
453: this.upperClip = rangeAxis.getRange().getUpperBound();
454:
455:
456: calculateBarWidth(plot, dataArea, rendererIndex, state);
457:
458: return state;
459:
460: }
461:
462:
470: protected void calculateBarWidth(CategoryPlot plot,
471: Rectangle2D dataArea,
472: int rendererIndex,
473: CategoryItemRendererState state) {
474:
475: CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
476: CategoryDataset dataset = plot.getDataset(rendererIndex);
477: if (dataset != null) {
478: int columns = dataset.getColumnCount();
479: int rows = dataset.getRowCount();
480: double space = 0.0;
481: PlotOrientation orientation = plot.getOrientation();
482: if (orientation == PlotOrientation.HORIZONTAL) {
483: space = dataArea.getHeight();
484: }
485: else if (orientation == PlotOrientation.VERTICAL) {
486: space = dataArea.getWidth();
487: }
488: double maxWidth = space * getMaximumBarWidth();
489: double categoryMargin = 0.0;
490: double currentItemMargin = 0.0;
491: if (columns > 1) {
492: categoryMargin = domainAxis.getCategoryMargin();
493: }
494: if (rows > 1) {
495: currentItemMargin = getItemMargin();
496: }
497: double used = space * (1 - domainAxis.getLowerMargin()
498: - domainAxis.getUpperMargin()
499: - categoryMargin - currentItemMargin);
500: if ((rows * columns) > 0) {
501: state.setBarWidth(Math.min(used / (rows * columns), maxWidth));
502: }
503: else {
504: state.setBarWidth(Math.min(used, maxWidth));
505: }
506: }
507: }
508:
509:
524: protected double calculateBarW0(CategoryPlot plot,
525: PlotOrientation orientation,
526: Rectangle2D dataArea,
527: CategoryAxis domainAxis,
528: CategoryItemRendererState state,
529: int row,
530: int column) {
531:
532: double space = 0.0;
533: if (orientation == PlotOrientation.HORIZONTAL) {
534: space = dataArea.getHeight();
535: }
536: else {
537: space = dataArea.getWidth();
538: }
539: double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
540: dataArea, plot.getDomainAxisEdge());
541: int seriesCount = getRowCount();
542: int categoryCount = getColumnCount();
543: if (seriesCount > 1) {
544: double seriesGap = space * getItemMargin()
545: / (categoryCount * (seriesCount - 1));
546: double seriesW = calculateSeriesWidth(space, domainAxis,
547: categoryCount, seriesCount);
548: barW0 = barW0 + row * (seriesW + seriesGap)
549: + (seriesW / 2.0) - (state.getBarWidth() / 2.0);
550: }
551: else {
552: barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(),
553: dataArea, plot.getDomainAxisEdge()) - state.getBarWidth()
554: / 2.0;
555: }
556: return barW0;
557: }
558:
559:
567: protected double[] calculateBarL0L1(double value) {
568: double lclip = getLowerClip();
569: double uclip = getUpperClip();
570: double barLow = Math.min(this.base, value);
571: double barHigh = Math.max(this.base, value);
572: if (barHigh < lclip) {
573: return null;
574: }
575: if (barLow > uclip) {
576: return null;
577: }
578: barLow = Math.max(barLow, lclip);
579: barHigh = Math.min(barHigh, uclip);
580: return new double[] {barLow, barHigh};
581: }
582:
583:
594: public Range findRangeBounds(CategoryDataset dataset) {
595: Range result = DatasetUtilities.findRangeBounds(dataset);
596: if (result != null) {
597: if (this.includeBaseInRange) {
598: result = Range.expandToInclude(result, this.base);
599: }
600: }
601: return result;
602: }
603:
604:
612: public LegendItem getLegendItem(int datasetIndex, int series) {
613:
614: CategoryPlot cp = getPlot();
615: if (cp == null) {
616: return null;
617: }
618:
619: CategoryDataset dataset;
620: dataset = cp.getDataset(datasetIndex);
621: String label = getLegendItemLabelGenerator().generateLabel(dataset,
622: series);
623: String description = label;
624: String toolTipText = null;
625: if (getLegendItemToolTipGenerator() != null) {
626: toolTipText = getLegendItemToolTipGenerator().generateLabel(
627: dataset, series);
628: }
629: String urlText = null;
630: if (getLegendItemURLGenerator() != null) {
631: urlText = getLegendItemURLGenerator().generateLabel(dataset,
632: series);
633: }
634: Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
635: Paint paint = getSeriesPaint(series);
636: Paint outlinePaint = getSeriesOutlinePaint(series);
637: Stroke outlineStroke = getSeriesOutlineStroke(series);
638:
639: return new LegendItem(label, description, toolTipText, urlText,
640: true, shape, true, paint,
641: isDrawBarOutline(), outlinePaint, outlineStroke,
642: false, new Line2D.Float(), new BasicStroke(1.0f),
643: Color.black);
644: }
645:
646:
660: public void drawItem(Graphics2D g2,
661: CategoryItemRendererState state,
662: Rectangle2D dataArea,
663: CategoryPlot plot,
664: CategoryAxis domainAxis,
665: ValueAxis rangeAxis,
666: CategoryDataset dataset,
667: int row,
668: int column,
669: int pass) {
670:
671:
672: Number dataValue = dataset.getValue(row, column);
673: if (dataValue == null) {
674: return;
675: }
676:
677: double value = dataValue.doubleValue();
678:
679: PlotOrientation orientation = plot.getOrientation();
680: double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis,
681: state, row, column);
682: double[] barL0L1 = calculateBarL0L1(value);
683: if (barL0L1 == null) {
684: return;
685: }
686:
687: RectangleEdge edge = plot.getRangeAxisEdge();
688: double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge);
689: double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge);
690: double barL0 = Math.min(transL0, transL1);
691: double barLength = Math.max(Math.abs(transL1 - transL0),
692: getMinimumBarLength());
693:
694:
695: Rectangle2D bar = null;
696: if (orientation == PlotOrientation.HORIZONTAL) {
697: bar = new Rectangle2D.Double(barL0, barW0, barLength,
698: state.getBarWidth());
699: }
700: else {
701: bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
702: barLength);
703: }
704: Paint itemPaint = getItemPaint(row, column);
705: GradientPaintTransformer t = getGradientPaintTransformer();
706: if (t != null && itemPaint instanceof GradientPaint) {
707: itemPaint = t.transform((GradientPaint) itemPaint, bar);
708: }
709: g2.setPaint(itemPaint);
710: g2.fill(bar);
711:
712:
713: if (isDrawBarOutline()
714: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
715: Stroke stroke = getItemOutlineStroke(row, column);
716: Paint paint = getItemOutlinePaint(row, column);
717: if (stroke != null && paint != null) {
718: g2.setStroke(stroke);
719: g2.setPaint(paint);
720: g2.draw(bar);
721: }
722: }
723:
724: CategoryItemLabelGenerator generator
725: = getItemLabelGenerator(row, column);
726: if (generator != null && isItemLabelVisible(row, column)) {
727: drawItemLabel(g2, dataset, row, column, plot, generator, bar,
728: (value < 0.0));
729: }
730:
731:
732: EntityCollection entities = state.getEntityCollection();
733: if (entities != null) {
734: addItemEntity(entities, dataset, row, column, bar);
735: }
736:
737: }
738:
739:
749: protected double calculateSeriesWidth(double space, CategoryAxis axis,
750: int categories, int series) {
751: double factor = 1.0 - getItemMargin() - axis.getLowerMargin()
752: - axis.getUpperMargin();
753: if (categories > 1) {
754: factor = factor - axis.getCategoryMargin();
755: }
756: return (space * factor) / (categories * series);
757: }
758:
759:
772: protected void drawItemLabel(Graphics2D g2,
773: CategoryDataset data,
774: int row,
775: int column,
776: CategoryPlot plot,
777: CategoryItemLabelGenerator generator,
778: Rectangle2D bar,
779: boolean negative) {
780:
781: String label = generator.generateLabel(data, row, column);
782: if (label == null) {
783: return;
784: }
785:
786: Font labelFont = getItemLabelFont(row, column);
787: g2.setFont(labelFont);
788: Paint paint = getItemLabelPaint(row, column);
789: g2.setPaint(paint);
790:
791:
792: ItemLabelPosition position = null;
793: if (!negative) {
794: position = getPositiveItemLabelPosition(row, column);
795: }
796: else {
797: position = getNegativeItemLabelPosition(row, column);
798: }
799:
800:
801: Point2D anchorPoint = calculateLabelAnchorPoint(
802: position.getItemLabelAnchor(), bar, plot.getOrientation());
803:
804: if (isInternalAnchor(position.getItemLabelAnchor())) {
805: Shape bounds = TextUtilities.calculateRotatedStringBounds(label,
806: g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(),
807: position.getTextAnchor(), position.getAngle(),
808: position.getRotationAnchor());
809:
810: if (bounds != null) {
811: if (!bar.contains(bounds.getBounds2D())) {
812: if (!negative) {
813: position = getPositiveItemLabelPositionFallback();
814: }
815: else {
816: position = getNegativeItemLabelPositionFallback();
817: }
818: if (position != null) {
819: anchorPoint = calculateLabelAnchorPoint(
820: position.getItemLabelAnchor(), bar,
821: plot.getOrientation());
822: }
823: }
824: }
825:
826: }
827:
828: if (position != null) {
829: TextUtilities.drawRotatedString(label, g2,
830: (float) anchorPoint.getX(), (float) anchorPoint.getY(),
831: position.getTextAnchor(), position.getAngle(),
832: position.getRotationAnchor());
833: }
834: }
835:
836:
845: private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
846: Rectangle2D bar,
847: PlotOrientation orientation) {
848:
849: Point2D result = null;
850: double offset = getItemLabelAnchorOffset();
851: double x0 = bar.getX() - offset;
852: double x1 = bar.getX();
853: double x2 = bar.getX() + offset;
854: double x3 = bar.getCenterX();
855: double x4 = bar.getMaxX() - offset;
856: double x5 = bar.getMaxX();
857: double x6 = bar.getMaxX() + offset;
858:
859: double y0 = bar.getMaxY() + offset;
860: double y1 = bar.getMaxY();
861: double y2 = bar.getMaxY() - offset;
862: double y3 = bar.getCenterY();
863: double y4 = bar.getMinY() + offset;
864: double y5 = bar.getMinY();
865: double y6 = bar.getMinY() - offset;
866:
867: if (anchor == ItemLabelAnchor.CENTER) {
868: result = new Point2D.Double(x3, y3);
869: }
870: else if (anchor == ItemLabelAnchor.INSIDE1) {
871: result = new Point2D.Double(x4, y4);
872: }
873: else if (anchor == ItemLabelAnchor.INSIDE2) {
874: result = new Point2D.Double(x4, y4);
875: }
876: else if (anchor == ItemLabelAnchor.INSIDE3) {
877: result = new Point2D.Double(x4, y3);
878: }
879: else if (anchor == ItemLabelAnchor.INSIDE4) {
880: result = new Point2D.Double(x4, y2);
881: }
882: else if (anchor == ItemLabelAnchor.INSIDE5) {
883: result = new Point2D.Double(x4, y2);
884: }
885: else if (anchor == ItemLabelAnchor.INSIDE6) {
886: result = new Point2D.Double(x3, y2);
887: }
888: else if (anchor == ItemLabelAnchor.INSIDE7) {
889: result = new Point2D.Double(x2, y2);
890: }
891: else if (anchor == ItemLabelAnchor.INSIDE8) {
892: result = new Point2D.Double(x2, y2);
893: }
894: else if (anchor == ItemLabelAnchor.INSIDE9) {
895: result = new Point2D.Double(x2, y3);
896: }
897: else if (anchor == ItemLabelAnchor.INSIDE10) {
898: result = new Point2D.Double(x2, y4);
899: }
900: else if (anchor == ItemLabelAnchor.INSIDE11) {
901: result = new Point2D.Double(x2, y4);
902: }
903: else if (anchor == ItemLabelAnchor.INSIDE12) {
904: result = new Point2D.Double(x3, y4);
905: }
906: else if (anchor == ItemLabelAnchor.OUTSIDE1) {
907: result = new Point2D.Double(x5, y6);
908: }
909: else if (anchor == ItemLabelAnchor.OUTSIDE2) {
910: result = new Point2D.Double(x6, y5);
911: }
912: else if (anchor == ItemLabelAnchor.OUTSIDE3) {
913: result = new Point2D.Double(x6, y3);
914: }
915: else if (anchor == ItemLabelAnchor.OUTSIDE4) {
916: result = new Point2D.Double(x6, y1);
917: }
918: else if (anchor == ItemLabelAnchor.OUTSIDE5) {
919: result = new Point2D.Double(x5, y0);
920: }
921: else if (anchor == ItemLabelAnchor.OUTSIDE6) {
922: result = new Point2D.Double(x3, y0);
923: }
924: else if (anchor == ItemLabelAnchor.OUTSIDE7) {
925: result = new Point2D.Double(x1, y0);
926: }
927: else if (anchor == ItemLabelAnchor.OUTSIDE8) {
928: result = new Point2D.Double(x0, y1);
929: }
930: else if (anchor == ItemLabelAnchor.OUTSIDE9) {
931: result = new Point2D.Double(x0, y3);
932: }
933: else if (anchor == ItemLabelAnchor.OUTSIDE10) {
934: result = new Point2D.Double(x0, y5);
935: }
936: else if (anchor == ItemLabelAnchor.OUTSIDE11) {
937: result = new Point2D.Double(x1, y6);
938: }
939: else if (anchor == ItemLabelAnchor.OUTSIDE12) {
940: result = new Point2D.Double(x3, y6);
941: }
942:
943: return result;
944:
945: }
946:
947:
954: private boolean isInternalAnchor(ItemLabelAnchor anchor) {
955: return anchor == ItemLabelAnchor.CENTER
956: || anchor == ItemLabelAnchor.INSIDE1
957: || anchor == ItemLabelAnchor.INSIDE2
958: || anchor == ItemLabelAnchor.INSIDE3
959: || anchor == ItemLabelAnchor.INSIDE4
960: || anchor == ItemLabelAnchor.INSIDE5
961: || anchor == ItemLabelAnchor.INSIDE6
962: || anchor == ItemLabelAnchor.INSIDE7
963: || anchor == ItemLabelAnchor.INSIDE8
964: || anchor == ItemLabelAnchor.INSIDE9
965: || anchor == ItemLabelAnchor.INSIDE10
966: || anchor == ItemLabelAnchor.INSIDE11
967: || anchor == ItemLabelAnchor.INSIDE12;
968: }
969:
970:
977: public boolean equals(Object obj) {
978:
979: if (obj == this) {
980: return true;
981: }
982: if (!(obj instanceof BarRenderer)) {
983: return false;
984: }
985: if (!super.equals(obj)) {
986: return false;
987: }
988: BarRenderer that = (BarRenderer) obj;
989: if (this.base != that.base) {
990: return false;
991: }
992: if (this.itemMargin != that.itemMargin) {
993: return false;
994: }
995: if (this.drawBarOutline != that.drawBarOutline) {
996: return false;
997: }
998: if (this.maximumBarWidth != that.maximumBarWidth) {
999: return false;
1000: }
1001: if (this.minimumBarLength != that.minimumBarLength) {
1002: return false;
1003: }
1004: if (!ObjectUtilities.equal(this.gradientPaintTransformer,
1005: that.gradientPaintTransformer)) {
1006: return false;
1007: }
1008: if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback,
1009: that.positiveItemLabelPositionFallback)) {
1010: return false;
1011: }
1012: if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback,
1013: that.negativeItemLabelPositionFallback)) {
1014: return false;
1015: }
1016: return true;
1017:
1018: }
1019:
1020: }