1:
85:
86: package ;
87:
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96: import ;
97:
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:
112:
116: public class StackedBarRenderer3D extends BarRenderer3D
117: implements Cloneable, PublicCloneable,
118: 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: notifyListeners(new RendererChangeEvent(this));
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 outlinePaint = getItemOutlinePaint(series, column);
444: g2.setStroke(getItemOutlineStroke(series, column));
445:
446: for (int f = 0; f < 6; f++) {
447: g2.setPaint(fillPaint);
448: g2.fill(faces[f]);
449: g2.setPaint(outlinePaint);
450: g2.draw(faces[f]);
451: }
452:
453: itemLabelList.add(new Object[] {new Integer(series),
454: faces[5].getBounds2D(),
455: BooleanUtilities.valueOf(v0 < getBase())});
456:
457:
458: EntityCollection entities = state.getEntityCollection();
459: if (entities != null) {
460: addItemEntity(entities, dataset, series, column, faces[5]);
461: }
462:
463: }
464:
465: for (int i = 0; i < itemLabelList.size(); i++) {
466: Object[] record = (Object[]) itemLabelList.get(i);
467: int series = ((Integer) record[0]).intValue();
468: Rectangle2D bar = (Rectangle2D) record[1];
469: boolean neg = ((Boolean) record[2]).booleanValue();
470: CategoryItemLabelGenerator generator
471: = getItemLabelGenerator(series, column);
472: if (generator != null && isItemLabelVisible(series, column)) {
473: drawItemLabel(g2, dataset, series, column, plot, generator,
474: bar, neg);
475: }
476:
477: }
478: }
479:
480:
493: private Shape[] createHorizontalBlock(double x0, double width, double y0,
494: double y1, boolean inverted) {
495: Shape[] result = new Shape[6];
496: Point2D p00 = new Point2D.Double(y0, x0);
497: Point2D p01 = new Point2D.Double(y0, x0 + width);
498: Point2D p02 = new Point2D.Double(p01.getX() + getXOffset(),
499: p01.getY() - getYOffset());
500: Point2D p03 = new Point2D.Double(p00.getX() + getXOffset(),
501: p00.getY() - getYOffset());
502:
503: Point2D p0 = new Point2D.Double(y1, x0);
504: Point2D p1 = new Point2D.Double(y1, x0 + width);
505: Point2D p2 = new Point2D.Double(p1.getX() + getXOffset(),
506: p1.getY() - getYOffset());
507: Point2D p3 = new Point2D.Double(p0.getX() + getXOffset(),
508: p0.getY() - getYOffset());
509:
510: GeneralPath bottom = new GeneralPath();
511: bottom.moveTo((float) p1.getX(), (float) p1.getY());
512: bottom.lineTo((float) p01.getX(), (float) p01.getY());
513: bottom.lineTo((float) p02.getX(), (float) p02.getY());
514: bottom.lineTo((float) p2.getX(), (float) p2.getY());
515: bottom.closePath();
516:
517: GeneralPath top = new GeneralPath();
518: top.moveTo((float) p0.getX(), (float) p0.getY());
519: top.lineTo((float) p00.getX(), (float) p00.getY());
520: top.lineTo((float) p03.getX(), (float) p03.getY());
521: top.lineTo((float) p3.getX(), (float) p3.getY());
522: top.closePath();
523:
524: GeneralPath back = new GeneralPath();
525: back.moveTo((float) p2.getX(), (float) p2.getY());
526: back.lineTo((float) p02.getX(), (float) p02.getY());
527: back.lineTo((float) p03.getX(), (float) p03.getY());
528: back.lineTo((float) p3.getX(), (float) p3.getY());
529: back.closePath();
530:
531: GeneralPath front = new GeneralPath();
532: front.moveTo((float) p0.getX(), (float) p0.getY());
533: front.lineTo((float) p1.getX(), (float) p1.getY());
534: front.lineTo((float) p01.getX(), (float) p01.getY());
535: front.lineTo((float) p00.getX(), (float) p00.getY());
536: front.closePath();
537:
538: GeneralPath left = new GeneralPath();
539: left.moveTo((float) p0.getX(), (float) p0.getY());
540: left.lineTo((float) p1.getX(), (float) p1.getY());
541: left.lineTo((float) p2.getX(), (float) p2.getY());
542: left.lineTo((float) p3.getX(), (float) p3.getY());
543: left.closePath();
544:
545: GeneralPath right = new GeneralPath();
546: right.moveTo((float) p00.getX(), (float) p00.getY());
547: right.lineTo((float) p01.getX(), (float) p01.getY());
548: right.lineTo((float) p02.getX(), (float) p02.getY());
549: right.lineTo((float) p03.getX(), (float) p03.getY());
550: right.closePath();
551: result[0] = bottom;
552: result[1] = back;
553: if (inverted) {
554: result[2] = right;
555: result[3] = left;
556: }
557: else {
558: result[2] = left;
559: result[3] = right;
560: }
561: result[4] = top;
562: result[5] = front;
563: return result;
564: }
565:
566:
581: protected void drawStackVertical(List values, Comparable category,
582: Graphics2D g2, CategoryItemRendererState state,
583: Rectangle2D dataArea, CategoryPlot plot,
584: CategoryAxis domainAxis, ValueAxis rangeAxis,
585: CategoryDataset dataset) {
586:
587: int column = dataset.getColumnIndex(category);
588: double barX0 = domainAxis.getCategoryMiddle(column,
589: dataset.getColumnCount(), dataArea, plot.getDomainAxisEdge())
590: - state.getBarWidth() / 2.0;
591: double barW = state.getBarWidth();
592:
593:
594:
595: List itemLabelList = new ArrayList();
596:
597:
598: boolean inverted = rangeAxis.isInverted();
599: int blockCount = values.size() - 1;
600: for (int k = 0; k < blockCount; k++) {
601: int index = (inverted ? blockCount - k - 1 : k);
602: Object[] prev = (Object[]) values.get(index);
603: Object[] curr = (Object[]) values.get(index + 1);
604: int series = 0;
605: if (curr[0] == null) {
606: series = -((Integer) prev[0]).intValue();
607: }
608: else {
609: series = ((Integer) curr[0]).intValue();
610: if (series < 0) {
611: series = -((Integer) prev[0]).intValue();
612: }
613: }
614: double v0 = ((Double) prev[1]).doubleValue();
615: double vv0 = rangeAxis.valueToJava2D(v0, dataArea,
616: plot.getRangeAxisEdge());
617:
618: double v1 = ((Double) curr[1]).doubleValue();
619: double vv1 = rangeAxis.valueToJava2D(v1, dataArea,
620: plot.getRangeAxisEdge());
621:
622: Shape[] faces = createVerticalBlock(barX0, barW, vv0, vv1,
623: inverted);
624: Paint fillPaint = getItemPaint(series, column);
625: Paint outlinePaint = getItemOutlinePaint(series, column);
626: g2.setStroke(getItemOutlineStroke(series, column));
627:
628: for (int f = 0; f < 6; f++) {
629: g2.setPaint(fillPaint);
630: g2.fill(faces[f]);
631: g2.setPaint(outlinePaint);
632: g2.draw(faces[f]);
633: }
634:
635: itemLabelList.add(new Object[] {new Integer(series),
636: faces[5].getBounds2D(),
637: BooleanUtilities.valueOf(v0 < getBase())});
638:
639:
640: EntityCollection entities = state.getEntityCollection();
641: if (entities != null) {
642: addItemEntity(entities, dataset, series, column, faces[5]);
643: }
644:
645: }
646:
647: for (int i = 0; i < itemLabelList.size(); i++) {
648: Object[] record = (Object[]) itemLabelList.get(i);
649: int series = ((Integer) record[0]).intValue();
650: Rectangle2D bar = (Rectangle2D) record[1];
651: boolean neg = ((Boolean) record[2]).booleanValue();
652: CategoryItemLabelGenerator generator
653: = getItemLabelGenerator(series, column);
654: if (generator != null && isItemLabelVisible(series, column)) {
655: drawItemLabel(g2, dataset, series, column, plot, generator,
656: bar, neg);
657: }
658:
659: }
660: }
661:
662:
675: private Shape[] createVerticalBlock(double x0, double width, double y0,
676: double y1, boolean inverted) {
677: Shape[] result = new Shape[6];
678: Point2D p00 = new Point2D.Double(x0, y0);
679: Point2D p01 = new Point2D.Double(x0 + width, y0);
680: Point2D p02 = new Point2D.Double(p01.getX() + getXOffset(),
681: p01.getY() - getYOffset());
682: Point2D p03 = new Point2D.Double(p00.getX() + getXOffset(),
683: p00.getY() - getYOffset());
684:
685:
686: Point2D p0 = new Point2D.Double(x0, y1);
687: Point2D p1 = new Point2D.Double(x0 + width, y1);
688: Point2D p2 = new Point2D.Double(p1.getX() + getXOffset(),
689: p1.getY() - getYOffset());
690: Point2D p3 = new Point2D.Double(p0.getX() + getXOffset(),
691: p0.getY() - getYOffset());
692:
693: GeneralPath right = new GeneralPath();
694: right.moveTo((float) p1.getX(), (float) p1.getY());
695: right.lineTo((float) p01.getX(), (float) p01.getY());
696: right.lineTo((float) p02.getX(), (float) p02.getY());
697: right.lineTo((float) p2.getX(), (float) p2.getY());
698: right.closePath();
699:
700: GeneralPath left = new GeneralPath();
701: left.moveTo((float) p0.getX(), (float) p0.getY());
702: left.lineTo((float) p00.getX(), (float) p00.getY());
703: left.lineTo((float) p03.getX(), (float) p03.getY());
704: left.lineTo((float) p3.getX(), (float) p3.getY());
705: left.closePath();
706:
707: GeneralPath back = new GeneralPath();
708: back.moveTo((float) p2.getX(), (float) p2.getY());
709: back.lineTo((float) p02.getX(), (float) p02.getY());
710: back.lineTo((float) p03.getX(), (float) p03.getY());
711: back.lineTo((float) p3.getX(), (float) p3.getY());
712: back.closePath();
713:
714: GeneralPath front = new GeneralPath();
715: front.moveTo((float) p0.getX(), (float) p0.getY());
716: front.lineTo((float) p1.getX(), (float) p1.getY());
717: front.lineTo((float) p01.getX(), (float) p01.getY());
718: front.lineTo((float) p00.getX(), (float) p00.getY());
719: front.closePath();
720:
721: GeneralPath top = new GeneralPath();
722: top.moveTo((float) p0.getX(), (float) p0.getY());
723: top.lineTo((float) p1.getX(), (float) p1.getY());
724: top.lineTo((float) p2.getX(), (float) p2.getY());
725: top.lineTo((float) p3.getX(), (float) p3.getY());
726: top.closePath();
727:
728: GeneralPath bottom = new GeneralPath();
729: bottom.moveTo((float) p00.getX(), (float) p00.getY());
730: bottom.lineTo((float) p01.getX(), (float) p01.getY());
731: bottom.lineTo((float) p02.getX(), (float) p02.getY());
732: bottom.lineTo((float) p03.getX(), (float) p03.getY());
733: bottom.closePath();
734:
735: result[0] = bottom;
736: result[1] = back;
737: result[2] = left;
738: result[3] = right;
739: result[4] = top;
740: result[5] = front;
741: if (inverted) {
742: result[0] = top;
743: result[4] = bottom;
744: }
745: return result;
746: }
747:
748:
755: public boolean equals(Object obj) {
756: if (obj == this) {
757: return true;
758: }
759: if (!(obj instanceof StackedBarRenderer3D)) {
760: return false;
761: }
762: if (!super.equals(obj)) {
763: return false;
764: }
765: StackedBarRenderer3D that = (StackedBarRenderer3D) obj;
766: if (this.renderAsPercentages != that.getRenderAsPercentages()) {
767: return false;
768: }
769: return true;
770: }
771:
772: }