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:
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:
113:
117: public class StackedBarRenderer3D extends BarRenderer3D
118: implements Cloneable, PublicCloneable, Serializable {
119:
120:
121: private static final long serialVersionUID = -5832945916493247123L;
122:
123:
124: private boolean renderAsPercentages;
125:
126:
134: public StackedBarRenderer3D() {
135: this(false);
136: }
137:
138:
144: public StackedBarRenderer3D(double xOffset, double yOffset) {
145: super(xOffset, yOffset);
146: }
147:
148:
156: public StackedBarRenderer3D(boolean renderAsPercentages) {
157: super();
158: this.renderAsPercentages = renderAsPercentages;
159: }
160:
161:
171: public StackedBarRenderer3D(double xOffset, double yOffset,
172: boolean renderAsPercentages) {
173: super(xOffset, yOffset);
174: this.renderAsPercentages = renderAsPercentages;
175: }
176:
177:
186: public boolean getRenderAsPercentages() {
187: return this.renderAsPercentages;
188: }
189:
190:
199: public void setRenderAsPercentages(boolean asPercentages) {
200: this.renderAsPercentages = asPercentages;
201: fireChangeEvent();
202: }
203:
204:
212: public Range findRangeBounds(CategoryDataset dataset) {
213: if (this.renderAsPercentages) {
214: return new Range(0.0, 1.0);
215: }
216: else {
217: return DatasetUtilities.findStackedRangeBounds(dataset);
218: }
219: }
220:
221:
229: protected void calculateBarWidth(CategoryPlot plot,
230: Rectangle2D dataArea,
231: int rendererIndex,
232: CategoryItemRendererState state) {
233:
234:
235: CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
236: CategoryDataset data = plot.getDataset(rendererIndex);
237: if (data != null) {
238: PlotOrientation orientation = plot.getOrientation();
239: double space = 0.0;
240: if (orientation == PlotOrientation.HORIZONTAL) {
241: space = dataArea.getHeight();
242: }
243: else if (orientation == PlotOrientation.VERTICAL) {
244: space = dataArea.getWidth();
245: }
246: double maxWidth = space * getMaximumBarWidth();
247: int columns = data.getColumnCount();
248: double categoryMargin = 0.0;
249: if (columns > 1) {
250: categoryMargin = domainAxis.getCategoryMargin();
251: }
252:
253: double used = space * (1 - domainAxis.getLowerMargin()
254: - domainAxis.getUpperMargin()
255: - categoryMargin);
256: if (columns > 0) {
257: state.setBarWidth(Math.min(used / columns, maxWidth));
258: }
259: else {
260: state.setBarWidth(Math.min(used, maxWidth));
261: }
262: }
263:
264: }
265:
266:
280: protected static List createStackedValueList(CategoryDataset dataset,
281: Comparable category, double base, boolean asPercentages) {
282:
283: List result = new ArrayList();
284: double posBase = base;
285: double negBase = base;
286: double total = 0.0;
287: if (asPercentages) {
288: total = DataUtilities.calculateColumnTotal(dataset,
289: dataset.getColumnIndex(category));
290: }
291:
292: int baseIndex = -1;
293: int seriesCount = dataset.getRowCount();
294: for (int s = 0; s < seriesCount; s++) {
295: Number n = dataset.getValue(dataset.getRowKey(s), category);
296: if (n == null) {
297: continue;
298: }
299: double v = n.doubleValue();
300: if (asPercentages) {
301: v = v / total;
302: }
303: if (v >= 0.0) {
304: if (baseIndex < 0) {
305: result.add(new Object[] {null, new Double(base)});
306: baseIndex = 0;
307: }
308: posBase = posBase + v;
309: result.add(new Object[] {new Integer(s), new Double(posBase)});
310: }
311: else if (v < 0.0) {
312: if (baseIndex < 0) {
313: result.add(new Object[] {null, new Double(base)});
314: baseIndex = 0;
315: }
316: negBase = negBase + v;
317: result.add(0, new Object[] {new Integer(-s),
318: new Double(negBase)});
319: baseIndex++;
320: }
321: }
322: return result;
323:
324: }
325:
326:
342: public void drawItem(Graphics2D g2,
343: CategoryItemRendererState state,
344: Rectangle2D dataArea,
345: CategoryPlot plot,
346: CategoryAxis domainAxis,
347: ValueAxis rangeAxis,
348: CategoryDataset dataset,
349: int row,
350: int column,
351: int pass) {
352:
353:
354:
355: if (row < dataset.getRowCount() - 1) {
356: return;
357: }
358: Comparable category = dataset.getColumnKey(column);
359:
360: List values = createStackedValueList(dataset,
361: dataset.getColumnKey(column), getBase(),
362: this.renderAsPercentages);
363:
364: Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
365: dataArea.getY() + getYOffset(),
366: dataArea.getWidth() - getXOffset(),
367: dataArea.getHeight() - getYOffset());
368:
369:
370: PlotOrientation orientation = plot.getOrientation();
371:
372:
373: if (orientation == PlotOrientation.HORIZONTAL) {
374: drawStackHorizontal(values, category, g2, state, adjusted, plot,
375: domainAxis, rangeAxis, dataset);
376: }
377: else {
378: drawStackVertical(values, category, g2, state, adjusted, plot,
379: domainAxis, rangeAxis, dataset);
380: }
381:
382: }
383:
384:
399: protected void drawStackHorizontal(List values, Comparable category,
400: Graphics2D g2, CategoryItemRendererState state,
401: Rectangle2D dataArea, CategoryPlot plot,
402: CategoryAxis domainAxis, ValueAxis rangeAxis,
403: CategoryDataset dataset) {
404:
405: int column = dataset.getColumnIndex(category);
406: double barX0 = domainAxis.getCategoryMiddle(column,
407: dataset.getColumnCount(), dataArea, plot.getDomainAxisEdge())
408: - state.getBarWidth() / 2.0;
409: double barW = state.getBarWidth();
410:
411:
412:
413: List itemLabelList = new ArrayList();
414:
415:
416: boolean inverted = rangeAxis.isInverted();
417: int blockCount = values.size() - 1;
418: for (int k = 0; k < blockCount; k++) {
419: int index = (inverted ? blockCount - k - 1 : k);
420: Object[] prev = (Object[]) values.get(index);
421: Object[] curr = (Object[]) values.get(index + 1);
422: int series = 0;
423: if (curr[0] == null) {
424: series = -((Integer) prev[0]).intValue();
425: }
426: else {
427: series = ((Integer) curr[0]).intValue();
428: if (series < 0) {
429: series = -((Integer) prev[0]).intValue();
430: }
431: }
432: double v0 = ((Double) prev[1]).doubleValue();
433: double vv0 = rangeAxis.valueToJava2D(v0, dataArea,
434: plot.getRangeAxisEdge());
435:
436: double v1 = ((Double) curr[1]).doubleValue();
437: double vv1 = rangeAxis.valueToJava2D(v1, dataArea,
438: plot.getRangeAxisEdge());
439:
440: Shape[] faces = createHorizontalBlock(barX0, barW, vv0, vv1,
441: inverted);
442: Paint fillPaint = getItemPaint(series, column);
443: Paint fillPaintDark = fillPaint;
444: if (fillPaintDark instanceof Color) {
445: fillPaintDark = ((Color) fillPaint).darker();
446: }
447: boolean drawOutlines = isDrawBarOutline();
448: Paint outlinePaint = fillPaint;
449: if (drawOutlines) {
450: outlinePaint = getItemOutlinePaint(series, column);
451: g2.setStroke(getItemOutlineStroke(series, column));
452: }
453: for (int f = 0; f < 6; f++) {
454: if (f == 5) {
455: g2.setPaint(fillPaint);
456: }
457: else {
458: g2.setPaint(fillPaintDark);
459: }
460: g2.fill(faces[f]);
461: if (drawOutlines) {
462: g2.setPaint(outlinePaint);
463: g2.draw(faces[f]);
464: }
465: }
466:
467: itemLabelList.add(new Object[] {new Integer(series),
468: faces[5].getBounds2D(),
469: BooleanUtilities.valueOf(v0 < getBase())});
470:
471:
472: EntityCollection entities = state.getEntityCollection();
473: if (entities != null) {
474: addItemEntity(entities, dataset, series, column, faces[5]);
475: }
476:
477: }
478:
479: for (int i = 0; i < itemLabelList.size(); i++) {
480: Object[] record = (Object[]) itemLabelList.get(i);
481: int series = ((Integer) record[0]).intValue();
482: Rectangle2D bar = (Rectangle2D) record[1];
483: boolean neg = ((Boolean) record[2]).booleanValue();
484: CategoryItemLabelGenerator generator
485: = getItemLabelGenerator(series, column);
486: if (generator != null && isItemLabelVisible(series, column)) {
487: drawItemLabel(g2, dataset, series, column, plot, generator,
488: bar, neg);
489: }
490:
491: }
492: }
493:
494:
507: private Shape[] createHorizontalBlock(double x0, double width, double y0,
508: double y1, boolean inverted) {
509: Shape[] result = new Shape[6];
510: Point2D p00 = new Point2D.Double(y0, x0);
511: Point2D p01 = new Point2D.Double(y0, x0 + width);
512: Point2D p02 = new Point2D.Double(p01.getX() + getXOffset(),
513: p01.getY() - getYOffset());
514: Point2D p03 = new Point2D.Double(p00.getX() + getXOffset(),
515: p00.getY() - getYOffset());
516:
517: Point2D p0 = new Point2D.Double(y1, x0);
518: Point2D p1 = new Point2D.Double(y1, x0 + width);
519: Point2D p2 = new Point2D.Double(p1.getX() + getXOffset(),
520: p1.getY() - getYOffset());
521: Point2D p3 = new Point2D.Double(p0.getX() + getXOffset(),
522: p0.getY() - getYOffset());
523:
524: GeneralPath bottom = new GeneralPath();
525: bottom.moveTo((float) p1.getX(), (float) p1.getY());
526: bottom.lineTo((float) p01.getX(), (float) p01.getY());
527: bottom.lineTo((float) p02.getX(), (float) p02.getY());
528: bottom.lineTo((float) p2.getX(), (float) p2.getY());
529: bottom.closePath();
530:
531: GeneralPath top = new GeneralPath();
532: top.moveTo((float) p0.getX(), (float) p0.getY());
533: top.lineTo((float) p00.getX(), (float) p00.getY());
534: top.lineTo((float) p03.getX(), (float) p03.getY());
535: top.lineTo((float) p3.getX(), (float) p3.getY());
536: top.closePath();
537:
538: GeneralPath back = new GeneralPath();
539: back.moveTo((float) p2.getX(), (float) p2.getY());
540: back.lineTo((float) p02.getX(), (float) p02.getY());
541: back.lineTo((float) p03.getX(), (float) p03.getY());
542: back.lineTo((float) p3.getX(), (float) p3.getY());
543: back.closePath();
544:
545: GeneralPath front = new GeneralPath();
546: front.moveTo((float) p0.getX(), (float) p0.getY());
547: front.lineTo((float) p1.getX(), (float) p1.getY());
548: front.lineTo((float) p01.getX(), (float) p01.getY());
549: front.lineTo((float) p00.getX(), (float) p00.getY());
550: front.closePath();
551:
552: GeneralPath left = new GeneralPath();
553: left.moveTo((float) p0.getX(), (float) p0.getY());
554: left.lineTo((float) p1.getX(), (float) p1.getY());
555: left.lineTo((float) p2.getX(), (float) p2.getY());
556: left.lineTo((float) p3.getX(), (float) p3.getY());
557: left.closePath();
558:
559: GeneralPath right = new GeneralPath();
560: right.moveTo((float) p00.getX(), (float) p00.getY());
561: right.lineTo((float) p01.getX(), (float) p01.getY());
562: right.lineTo((float) p02.getX(), (float) p02.getY());
563: right.lineTo((float) p03.getX(), (float) p03.getY());
564: right.closePath();
565: result[0] = bottom;
566: result[1] = back;
567: if (inverted) {
568: result[2] = right;
569: result[3] = left;
570: }
571: else {
572: result[2] = left;
573: result[3] = right;
574: }
575: result[4] = top;
576: result[5] = front;
577: return result;
578: }
579:
580:
595: protected void drawStackVertical(List values, Comparable category,
596: Graphics2D g2, CategoryItemRendererState state,
597: Rectangle2D dataArea, CategoryPlot plot,
598: CategoryAxis domainAxis, ValueAxis rangeAxis,
599: CategoryDataset dataset) {
600:
601: int column = dataset.getColumnIndex(category);
602: double barX0 = domainAxis.getCategoryMiddle(column,
603: dataset.getColumnCount(), dataArea, plot.getDomainAxisEdge())
604: - state.getBarWidth() / 2.0;
605: double barW = state.getBarWidth();
606:
607:
608:
609: List itemLabelList = new ArrayList();
610:
611:
612: boolean inverted = rangeAxis.isInverted();
613: int blockCount = values.size() - 1;
614: for (int k = 0; k < blockCount; k++) {
615: int index = (inverted ? blockCount - k - 1 : k);
616: Object[] prev = (Object[]) values.get(index);
617: Object[] curr = (Object[]) values.get(index + 1);
618: int series = 0;
619: if (curr[0] == null) {
620: series = -((Integer) prev[0]).intValue();
621: }
622: else {
623: series = ((Integer) curr[0]).intValue();
624: if (series < 0) {
625: series = -((Integer) prev[0]).intValue();
626: }
627: }
628: double v0 = ((Double) prev[1]).doubleValue();
629: double vv0 = rangeAxis.valueToJava2D(v0, dataArea,
630: plot.getRangeAxisEdge());
631:
632: double v1 = ((Double) curr[1]).doubleValue();
633: double vv1 = rangeAxis.valueToJava2D(v1, dataArea,
634: plot.getRangeAxisEdge());
635:
636: Shape[] faces = createVerticalBlock(barX0, barW, vv0, vv1,
637: inverted);
638: Paint fillPaint = getItemPaint(series, column);
639: Paint fillPaintDark = fillPaint;
640: if (fillPaintDark instanceof Color) {
641: fillPaintDark = ((Color) fillPaint).darker();
642: }
643: boolean drawOutlines = isDrawBarOutline();
644: Paint outlinePaint = fillPaint;
645: if (drawOutlines) {
646: outlinePaint = getItemOutlinePaint(series, column);
647: g2.setStroke(getItemOutlineStroke(series, column));
648: }
649:
650: for (int f = 0; f < 6; f++) {
651: if (f == 5) {
652: g2.setPaint(fillPaint);
653: }
654: else {
655: g2.setPaint(fillPaintDark);
656: }
657: g2.fill(faces[f]);
658: if (drawOutlines) {
659: g2.setPaint(outlinePaint);
660: g2.draw(faces[f]);
661: }
662: }
663:
664: itemLabelList.add(new Object[] {new Integer(series),
665: faces[5].getBounds2D(),
666: BooleanUtilities.valueOf(v0 < getBase())});
667:
668:
669: EntityCollection entities = state.getEntityCollection();
670: if (entities != null) {
671: addItemEntity(entities, dataset, series, column, faces[5]);
672: }
673:
674: }
675:
676: for (int i = 0; i < itemLabelList.size(); i++) {
677: Object[] record = (Object[]) itemLabelList.get(i);
678: int series = ((Integer) record[0]).intValue();
679: Rectangle2D bar = (Rectangle2D) record[1];
680: boolean neg = ((Boolean) record[2]).booleanValue();
681: CategoryItemLabelGenerator generator
682: = getItemLabelGenerator(series, column);
683: if (generator != null && isItemLabelVisible(series, column)) {
684: drawItemLabel(g2, dataset, series, column, plot, generator,
685: bar, neg);
686: }
687:
688: }
689: }
690:
691:
704: private Shape[] createVerticalBlock(double x0, double width, double y0,
705: double y1, boolean inverted) {
706: Shape[] result = new Shape[6];
707: Point2D p00 = new Point2D.Double(x0, y0);
708: Point2D p01 = new Point2D.Double(x0 + width, y0);
709: Point2D p02 = new Point2D.Double(p01.getX() + getXOffset(),
710: p01.getY() - getYOffset());
711: Point2D p03 = new Point2D.Double(p00.getX() + getXOffset(),
712: p00.getY() - getYOffset());
713:
714:
715: Point2D p0 = new Point2D.Double(x0, y1);
716: Point2D p1 = new Point2D.Double(x0 + width, y1);
717: Point2D p2 = new Point2D.Double(p1.getX() + getXOffset(),
718: p1.getY() - getYOffset());
719: Point2D p3 = new Point2D.Double(p0.getX() + getXOffset(),
720: p0.getY() - getYOffset());
721:
722: GeneralPath right = new GeneralPath();
723: right.moveTo((float) p1.getX(), (float) p1.getY());
724: right.lineTo((float) p01.getX(), (float) p01.getY());
725: right.lineTo((float) p02.getX(), (float) p02.getY());
726: right.lineTo((float) p2.getX(), (float) p2.getY());
727: right.closePath();
728:
729: GeneralPath left = new GeneralPath();
730: left.moveTo((float) p0.getX(), (float) p0.getY());
731: left.lineTo((float) p00.getX(), (float) p00.getY());
732: left.lineTo((float) p03.getX(), (float) p03.getY());
733: left.lineTo((float) p3.getX(), (float) p3.getY());
734: left.closePath();
735:
736: GeneralPath back = new GeneralPath();
737: back.moveTo((float) p2.getX(), (float) p2.getY());
738: back.lineTo((float) p02.getX(), (float) p02.getY());
739: back.lineTo((float) p03.getX(), (float) p03.getY());
740: back.lineTo((float) p3.getX(), (float) p3.getY());
741: back.closePath();
742:
743: GeneralPath front = new GeneralPath();
744: front.moveTo((float) p0.getX(), (float) p0.getY());
745: front.lineTo((float) p1.getX(), (float) p1.getY());
746: front.lineTo((float) p01.getX(), (float) p01.getY());
747: front.lineTo((float) p00.getX(), (float) p00.getY());
748: front.closePath();
749:
750: GeneralPath top = new GeneralPath();
751: top.moveTo((float) p0.getX(), (float) p0.getY());
752: top.lineTo((float) p1.getX(), (float) p1.getY());
753: top.lineTo((float) p2.getX(), (float) p2.getY());
754: top.lineTo((float) p3.getX(), (float) p3.getY());
755: top.closePath();
756:
757: GeneralPath bottom = new GeneralPath();
758: bottom.moveTo((float) p00.getX(), (float) p00.getY());
759: bottom.lineTo((float) p01.getX(), (float) p01.getY());
760: bottom.lineTo((float) p02.getX(), (float) p02.getY());
761: bottom.lineTo((float) p03.getX(), (float) p03.getY());
762: bottom.closePath();
763:
764: result[0] = bottom;
765: result[1] = back;
766: result[2] = left;
767: result[3] = right;
768: result[4] = top;
769: result[5] = front;
770: if (inverted) {
771: result[0] = top;
772: result[4] = bottom;
773: }
774: return result;
775: }
776:
777:
784: public boolean equals(Object obj) {
785: if (obj == this) {
786: return true;
787: }
788: if (!(obj instanceof StackedBarRenderer3D)) {
789: return false;
790: }
791: if (!super.equals(obj)) {
792: return false;
793: }
794: StackedBarRenderer3D that = (StackedBarRenderer3D) obj;
795: if (this.renderAsPercentages != that.getRenderAsPercentages()) {
796: return false;
797: }
798: return true;
799: }
800:
801: }