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