1:
78:
79: package ;
80:
81: import ;
82: import ;
83: import ;
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: import ;
97: import ;
98: import ;
99: import ;
100:
101: import ;
102: import ;
103: import ;
104: import ;
105: import ;
106: import ;
107: import ;
108:
109:
116: public class PiePlot3D extends PiePlot implements Serializable {
117:
118:
119: private static final long serialVersionUID = 3408984188945161432L;
120:
121:
122: private double depthFactor = 0.2;
123:
124:
127: public PiePlot3D() {
128: this(null);
129: }
130:
131:
137: public PiePlot3D(PieDataset dataset) {
138: super(dataset);
139: setCircular(false, false);
140: }
141:
142:
149: public double getDepthFactor() {
150: return this.depthFactor;
151: }
152:
153:
161: public void setDepthFactor(double factor) {
162: this.depthFactor = factor;
163: notifyListeners(new PlotChangeEvent(this));
164: }
165:
166:
179: public void draw(Graphics2D g2, Rectangle2D plotArea, Point2D anchor,
180: PlotState parentState,
181: PlotRenderingInfo info) {
182:
183:
184: RectangleInsets insets = getInsets();
185: insets.trim(plotArea);
186:
187: Rectangle2D originalPlotArea = (Rectangle2D) plotArea.clone();
188: if (info != null) {
189: info.setPlotArea(plotArea);
190: info.setDataArea(plotArea);
191: }
192:
193: Shape savedClip = g2.getClip();
194: g2.clip(plotArea);
195:
196:
197: double gapPercent = getInteriorGap();
198: double labelPercent = 0.0;
199: if (getLabelGenerator() != null) {
200: labelPercent = getLabelGap() + getMaximumLabelWidth()
201: + getLabelLinkMargin();
202: }
203: double gapHorizontal = plotArea.getWidth()
204: * (gapPercent + labelPercent);
205: double gapVertical = plotArea.getHeight() * gapPercent;
206:
207: double linkX = plotArea.getX() + gapHorizontal / 2;
208: double linkY = plotArea.getY() + gapVertical / 2;
209: double linkW = plotArea.getWidth() - gapHorizontal;
210: double linkH = plotArea.getHeight() - gapVertical;
211:
212:
213: if (isCircular()) {
214: double min = Math.min(linkW, linkH) / 2;
215: linkX = (linkX + linkX + linkW) / 2 - min;
216: linkY = (linkY + linkY + linkH) / 2 - min;
217: linkW = 2 * min;
218: linkH = 2 * min;
219: }
220:
221: PiePlotState state = initialise(g2, plotArea, this, null, info);
222:
223:
224:
225: double hh = linkW * getLabelLinkMargin();
226: double vv = linkH * getLabelLinkMargin();
227: Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0,
228: linkY + vv / 2.0, linkW - hh, linkH - vv);
229:
230: state.setExplodedPieArea(explodeArea);
231:
232:
233:
234:
235: double maximumExplodePercent = getMaximumExplodePercent();
236: double percent = maximumExplodePercent / (1.0 + maximumExplodePercent);
237:
238: double h1 = explodeArea.getWidth() * percent;
239: double v1 = explodeArea.getHeight() * percent;
240: Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX()
241: + h1 / 2.0, explodeArea.getY() + v1 / 2.0,
242: explodeArea.getWidth() - h1, explodeArea.getHeight() - v1);
243:
244: int depth = (int) (pieArea.getHeight() * this.depthFactor);
245:
246:
247: Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW,
248: linkH - depth);
249: state.setLinkArea(linkArea);
250:
251: state.setPieArea(pieArea);
252: state.setPieCenterX(pieArea.getCenterX());
253: state.setPieCenterY(pieArea.getCenterY() - depth / 2.0);
254: state.setPieWRadius(pieArea.getWidth() / 2.0);
255: state.setPieHRadius((pieArea.getHeight() - depth) / 2.0);
256:
257: drawBackground(g2, plotArea);
258:
259: PieDataset dataset = getDataset();
260: if (DatasetUtilities.isEmptyOrNull(getDataset())) {
261: drawNoDataMessage(g2, plotArea);
262: g2.setClip(savedClip);
263: drawOutline(g2, plotArea);
264: return;
265: }
266:
267:
268: if (dataset.getKeys().size() > plotArea.getWidth()) {
269: String text = "Too many elements";
270: Font sfont = new Font("dialog", Font.BOLD, 10);
271: g2.setFont(sfont);
272: FontMetrics fm = g2.getFontMetrics(sfont);
273: int stringWidth = fm.stringWidth(text);
274:
275: g2.drawString(text, (int) (plotArea.getX() + (plotArea.getWidth()
276: - stringWidth) / 2), (int) (plotArea.getY()
277: + (plotArea.getHeight() / 2)));
278: return;
279: }
280:
281:
282:
283: if (isCircular()) {
284: double min = Math.min(plotArea.getWidth(),
285: plotArea.getHeight()) / 2;
286: plotArea = new Rectangle2D.Double(plotArea.getCenterX() - min,
287: plotArea.getCenterY() - min, 2 * min, 2 * min);
288: }
289:
290: List sectionKeys = dataset.getKeys();
291:
292: if (sectionKeys.size() == 0) {
293: return;
294: }
295:
296:
297: double arcX = pieArea.getX();
298: double arcY = pieArea.getY();
299:
300:
301: Composite originalComposite = g2.getComposite();
302: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
303: getForegroundAlpha()));
304:
305: double totalValue = DatasetUtilities.calculatePieDatasetTotal(dataset);
306: double runningTotal = 0;
307: if (depth < 0) {
308: return;
309: }
310:
311: ArrayList arcList = new ArrayList();
312: Arc2D.Double arc;
313: Paint paint;
314: Paint outlinePaint;
315: Stroke outlineStroke;
316:
317: Iterator iterator = sectionKeys.iterator();
318: while (iterator.hasNext()) {
319:
320: Comparable currentKey = (Comparable) iterator.next();
321: Number dataValue = dataset.getValue(currentKey);
322: if (dataValue == null) {
323: arcList.add(null);
324: continue;
325: }
326: double value = dataValue.doubleValue();
327: if (value <= 0) {
328: arcList.add(null);
329: continue;
330: }
331: double startAngle = getStartAngle();
332: double direction = getDirection().getFactor();
333: double angle1 = startAngle + (direction * (runningTotal * 360))
334: / totalValue;
335: double angle2 = startAngle + (direction * (runningTotal + value)
336: * 360) / totalValue;
337: if (Math.abs(angle2 - angle1) > getMinimumArcAngleToDraw()) {
338: arcList.add(new Arc2D.Double(arcX, arcY + depth,
339: pieArea.getWidth(), pieArea.getHeight() - depth,
340: angle1, angle2 - angle1, Arc2D.PIE));
341: }
342: else {
343: arcList.add(null);
344: }
345: runningTotal += value;
346: }
347:
348: Shape oldClip = g2.getClip();
349:
350: Ellipse2D top = new Ellipse2D.Double(pieArea.getX(), pieArea.getY(),
351: pieArea.getWidth(), pieArea.getHeight() - depth);
352:
353: Ellipse2D bottom = new Ellipse2D.Double(pieArea.getX(), pieArea.getY()
354: + depth, pieArea.getWidth(), pieArea.getHeight() - depth);
355:
356: Rectangle2D lower = new Rectangle2D.Double(top.getX(),
357: top.getCenterY(), pieArea.getWidth(), bottom.getMaxY()
358: - top.getCenterY());
359:
360: Rectangle2D upper = new Rectangle2D.Double(pieArea.getX(), top.getY(),
361: pieArea.getWidth(), bottom.getCenterY() - top.getY());
362:
363: Area a = new Area(top);
364: a.add(new Area(lower));
365: Area b = new Area(bottom);
366: b.add(new Area(upper));
367: Area pie = new Area(a);
368: pie.intersect(b);
369:
370: Area front = new Area(pie);
371: front.subtract(new Area(top));
372:
373: Area back = new Area(pie);
374: back.subtract(new Area(bottom));
375:
376:
377: int[] xs;
378: int[] ys;
379: arc = new Arc2D.Double(arcX, arcY + depth, pieArea.getWidth(),
380: pieArea.getHeight() - depth, 0, 360, Arc2D.PIE);
381:
382: int categoryCount = arcList.size();
383: for (int categoryIndex = 0; categoryIndex < categoryCount;
384: categoryIndex++) {
385: arc = (Arc2D.Double) arcList.get(categoryIndex);
386: if (arc == null) {
387: continue;
388: }
389: Comparable key = getSectionKey(categoryIndex);
390: paint = lookupSectionPaint(key, true);
391: outlinePaint = lookupSectionOutlinePaint(key);
392: outlineStroke = lookupSectionOutlineStroke(key);
393: g2.setPaint(paint);
394: g2.fill(arc);
395: g2.setPaint(outlinePaint);
396: g2.setStroke(outlineStroke);
397: g2.draw(arc);
398: g2.setPaint(paint);
399:
400: Point2D p1 = arc.getStartPoint();
401:
402:
403: xs = new int[] {(int) arc.getCenterX(), (int) arc.getCenterX(),
404: (int) p1.getX(), (int) p1.getX()};
405: ys = new int[] {(int) arc.getCenterY(), (int) arc.getCenterY()
406: - depth, (int) p1.getY() - depth, (int) p1.getY()};
407: Polygon polygon = new Polygon(xs, ys, 4);
408: g2.setPaint(java.awt.Color.lightGray);
409: g2.fill(polygon);
410: g2.setPaint(outlinePaint);
411: g2.setStroke(outlineStroke);
412: g2.draw(polygon);
413: g2.setPaint(paint);
414:
415: }
416:
417: g2.setPaint(Color.gray);
418: g2.fill(back);
419: g2.fill(front);
420:
421:
422: int cat = 0;
423: iterator = arcList.iterator();
424: while (iterator.hasNext()) {
425: Arc2D segment = (Arc2D) iterator.next();
426: if (segment != null) {
427: Comparable key = getSectionKey(cat);
428: paint = lookupSectionPaint(key, true);
429: outlinePaint = lookupSectionOutlinePaint(key);
430: outlineStroke = lookupSectionOutlineStroke(key);
431: drawSide(g2, pieArea, segment, front, back, paint,
432: outlinePaint, outlineStroke, false, true);
433: }
434: cat++;
435: }
436:
437:
438: cat = 0;
439: iterator = arcList.iterator();
440: while (iterator.hasNext()) {
441: Arc2D segment = (Arc2D) iterator.next();
442: if (segment != null) {
443: Comparable key = getSectionKey(cat);
444: paint = lookupSectionPaint(key);
445: outlinePaint = lookupSectionOutlinePaint(key);
446: outlineStroke = lookupSectionOutlineStroke(key);
447: drawSide(g2, pieArea, segment, front, back, paint,
448: outlinePaint, outlineStroke, true, false);
449: }
450: cat++;
451: }
452:
453: g2.setClip(oldClip);
454:
455:
456: Arc2D upperArc;
457: for (int sectionIndex = 0; sectionIndex < categoryCount;
458: sectionIndex++) {
459: arc = (Arc2D.Double) arcList.get(sectionIndex);
460: if (arc == null) {
461: continue;
462: }
463: upperArc = new Arc2D.Double(arcX, arcY, pieArea.getWidth(),
464: pieArea.getHeight() - depth, arc.getAngleStart(),
465: arc.getAngleExtent(), Arc2D.PIE);
466:
467: Comparable currentKey = (Comparable) sectionKeys.get(sectionIndex);
468: paint = lookupSectionPaint(currentKey, true);
469: outlinePaint = lookupSectionOutlinePaint(currentKey);
470: outlineStroke = lookupSectionOutlineStroke(currentKey);
471: g2.setPaint(paint);
472: g2.fill(upperArc);
473: g2.setStroke(outlineStroke);
474: g2.setPaint(outlinePaint);
475: g2.draw(upperArc);
476:
477:
478: if (info != null) {
479: EntityCollection entities
480: = info.getOwner().getEntityCollection();
481: if (entities != null) {
482: String tip = null;
483: PieToolTipGenerator tipster = getToolTipGenerator();
484: if (tipster != null) {
485:
486: tip = tipster.generateToolTip(dataset, currentKey);
487: }
488: String url = null;
489: if (getURLGenerator() != null) {
490: url = getURLGenerator().generateURL(dataset, currentKey,
491: getPieIndex());
492: }
493: PieSectionEntity entity = new PieSectionEntity(
494: upperArc, dataset, getPieIndex(), sectionIndex,
495: currentKey, tip, url);
496: entities.add(entity);
497: }
498: }
499: List keys = dataset.getKeys();
500: Rectangle2D adjustedPlotArea = new Rectangle2D.Double(
501: originalPlotArea.getX(), originalPlotArea.getY(),
502: originalPlotArea.getWidth(), originalPlotArea.getHeight()
503: - depth);
504: drawLabels(g2, keys, totalValue, adjustedPlotArea, linkArea, state);
505: }
506:
507: g2.setClip(savedClip);
508: g2.setComposite(originalComposite);
509: drawOutline(g2, originalPlotArea);
510:
511: }
512:
513:
527: protected void drawSide(Graphics2D g2,
528: Rectangle2D plotArea,
529: Arc2D arc,
530: Area front,
531: Area back,
532: Paint paint,
533: Paint outlinePaint,
534: Stroke outlineStroke,
535: boolean drawFront,
536: boolean drawBack) {
537:
538: double start = arc.getAngleStart();
539: double extent = arc.getAngleExtent();
540: double end = start + extent;
541:
542: g2.setStroke(outlineStroke);
543:
544:
545: if (extent < 0.0) {
546:
547: if (isAngleAtFront(start)) {
548:
549: if (!isAngleAtBack(end)) {
550:
551: if (extent > -180.0) {
552:
553: if (drawFront) {
554: Area side = new Area(new Rectangle2D.Double(
555: arc.getEndPoint().getX(), plotArea.getY(),
556: arc.getStartPoint().getX()
557: - arc.getEndPoint().getX(),
558: plotArea.getHeight()));
559: side.intersect(front);
560: g2.setPaint(paint);
561: g2.fill(side);
562: g2.setPaint(outlinePaint);
563: g2.draw(side);
564: }
565: }
566: else {
567:
568:
569: Area side1 = new Area(new Rectangle2D.Double(
570: plotArea.getX(), plotArea.getY(),
571: arc.getStartPoint().getX() - plotArea.getX(),
572: plotArea.getHeight()));
573: side1.intersect(front);
574:
575: Area side2 = new Area(new Rectangle2D.Double(
576: arc.getEndPoint().getX(), plotArea.getY(),
577: plotArea.getMaxX() - arc.getEndPoint().getX(),
578: plotArea.getHeight()));
579:
580: side2.intersect(front);
581: g2.setPaint(paint);
582: if (drawFront) {
583: g2.fill(side1);
584: g2.fill(side2);
585: }
586:
587: if (drawBack) {
588: g2.fill(back);
589: }
590:
591: g2.setPaint(outlinePaint);
592: if (drawFront) {
593: g2.draw(side1);
594: g2.draw(side2);
595: }
596:
597: if (drawBack) {
598: g2.draw(back);
599: }
600:
601: }
602: }
603: else {
604:
605:
606: if (drawBack) {
607: Area side2 = new Area(new Rectangle2D.Double(
608: plotArea.getX(), plotArea.getY(),
609: arc.getEndPoint().getX() - plotArea.getX(),
610: plotArea.getHeight()));
611: side2.intersect(back);
612: g2.setPaint(paint);
613: g2.fill(side2);
614: g2.setPaint(outlinePaint);
615: g2.draw(side2);
616: }
617:
618: if (drawFront) {
619: Area side1 = new Area(new Rectangle2D.Double(
620: plotArea.getX(), plotArea.getY(),
621: arc.getStartPoint().getX() - plotArea.getX(),
622: plotArea.getHeight()));
623: side1.intersect(front);
624: g2.setPaint(paint);
625: g2.fill(side1);
626: g2.setPaint(outlinePaint);
627: g2.draw(side1);
628: }
629: }
630: }
631: else {
632:
633:
634: if (!isAngleAtFront(end)) {
635: if (extent > -180.0) {
636: if (drawBack) {
637: Area side = new Area(new Rectangle2D.Double(
638: arc.getStartPoint().getX(), plotArea.getY(),
639: arc.getEndPoint().getX()
640: - arc.getStartPoint().getX(),
641: plotArea.getHeight()));
642: side.intersect(back);
643: g2.setPaint(paint);
644: g2.fill(side);
645: g2.setPaint(outlinePaint);
646: g2.draw(side);
647: }
648: }
649: else {
650:
651: Area side1 = new Area(new Rectangle2D.Double(
652: arc.getStartPoint().getX(), plotArea.getY(),
653: plotArea.getMaxX() - arc.getStartPoint().getX(),
654: plotArea.getHeight()));
655: side1.intersect(back);
656:
657: Area side2 = new Area(new Rectangle2D.Double(
658: plotArea.getX(), plotArea.getY(),
659: arc.getEndPoint().getX() - plotArea.getX(),
660: plotArea.getHeight()));
661:
662: side2.intersect(back);
663:
664: g2.setPaint(paint);
665: if (drawBack) {
666: g2.fill(side1);
667: g2.fill(side2);
668: }
669:
670: if (drawFront) {
671: g2.fill(front);
672: }
673:
674: g2.setPaint(outlinePaint);
675: if (drawBack) {
676: g2.draw(side1);
677: g2.draw(side2);
678: }
679:
680: if (drawFront) {
681: g2.draw(front);
682: }
683:
684: }
685: }
686: else {
687:
688: if (drawBack) {
689: Area side1 = new Area(new Rectangle2D.Double(
690: arc.getStartPoint().getX(), plotArea.getY(),
691: plotArea.getMaxX() - arc.getStartPoint().getX(),
692: plotArea.getHeight()));
693: side1.intersect(back);
694: g2.setPaint(paint);
695: g2.fill(side1);
696: g2.setPaint(outlinePaint);
697: g2.draw(side1);
698: }
699:
700: if (drawFront) {
701: Area side2 = new Area(new Rectangle2D.Double(
702: arc.getEndPoint().getX(), plotArea.getY(),
703: plotArea.getMaxX() - arc.getEndPoint().getX(),
704: plotArea.getHeight()));
705: side2.intersect(front);
706: g2.setPaint(paint);
707: g2.fill(side2);
708: g2.setPaint(outlinePaint);
709: g2.draw(side2);
710: }
711:
712: }
713: }
714: }
715: else if (extent > 0.0) {
716:
717: if (isAngleAtFront(start)) {
718:
719: if (!isAngleAtBack(end)) {
720:
721: if (extent < 180.0) {
722: if (drawFront) {
723: Area side = new Area(new Rectangle2D.Double(
724: arc.getStartPoint().getX(), plotArea.getY(),
725: arc.getEndPoint().getX()
726: - arc.getStartPoint().getX(),
727: plotArea.getHeight()));
728: side.intersect(front);
729: g2.setPaint(paint);
730: g2.fill(side);
731: g2.setPaint(outlinePaint);
732: g2.draw(side);
733: }
734: }
735: else {
736: Area side1 = new Area(new Rectangle2D.Double(
737: arc.getStartPoint().getX(), plotArea.getY(),
738: plotArea.getMaxX() - arc.getStartPoint().getX(),
739: plotArea.getHeight()));
740: side1.intersect(front);
741:
742: Area side2 = new Area(new Rectangle2D.Double(
743: plotArea.getX(), plotArea.getY(),
744: arc.getEndPoint().getX() - plotArea.getX(),
745: plotArea.getHeight()));
746: side2.intersect(front);
747:
748: g2.setPaint(paint);
749: if (drawFront) {
750: g2.fill(side1);
751: g2.fill(side2);
752: }
753:
754: if (drawBack) {
755: g2.fill(back);
756: }
757:
758: g2.setPaint(outlinePaint);
759: if (drawFront) {
760: g2.draw(side1);
761: g2.draw(side2);
762: }
763:
764: if (drawBack) {
765: g2.draw(back);
766: }
767:
768: }
769: }
770: else {
771: if (drawBack) {
772: Area side2 = new Area(new Rectangle2D.Double(
773: arc.getEndPoint().getX(), plotArea.getY(),
774: plotArea.getMaxX() - arc.getEndPoint().getX(),
775: plotArea.getHeight()));
776: side2.intersect(back);
777: g2.setPaint(paint);
778: g2.fill(side2);
779: g2.setPaint(outlinePaint);
780: g2.draw(side2);
781: }
782:
783: if (drawFront) {
784: Area side1 = new Area(new Rectangle2D.Double(
785: arc.getStartPoint().getX(), plotArea.getY(),
786: plotArea.getMaxX() - arc.getStartPoint().getX(),
787: plotArea.getHeight()));
788: side1.intersect(front);
789: g2.setPaint(paint);
790: g2.fill(side1);
791: g2.setPaint(outlinePaint);
792: g2.draw(side1);
793: }
794: }
795: }
796: else {
797:
798: if (!isAngleAtFront(end)) {
799: if (extent < 180.0) {
800: if (drawBack) {
801: Area side = new Area(new Rectangle2D.Double(
802: arc.getEndPoint().getX(), plotArea.getY(),
803: arc.getStartPoint().getX()
804: - arc.getEndPoint().getX(),
805: plotArea.getHeight()));
806: side.intersect(back);
807: g2.setPaint(paint);
808: g2.fill(side);
809: g2.setPaint(outlinePaint);
810: g2.draw(side);
811: }
812: }
813: else {
814:
815: Area side1 = new Area(new Rectangle2D.Double(
816: arc.getStartPoint().getX(), plotArea.getY(),
817: plotArea.getX() - arc.getStartPoint().getX(),
818: plotArea.getHeight()));
819: side1.intersect(back);
820:
821: Area side2 = new Area(new Rectangle2D.Double(
822: arc.getEndPoint().getX(), plotArea.getY(),
823: plotArea.getMaxX() - arc.getEndPoint().getX(),
824: plotArea.getHeight()));
825: side2.intersect(back);
826:
827: g2.setPaint(paint);
828: if (drawBack) {
829: g2.fill(side1);
830: g2.fill(side2);
831: }
832:
833: if (drawFront) {
834: g2.fill(front);
835: }
836:
837: g2.setPaint(outlinePaint);
838: if (drawBack) {
839: g2.draw(side1);
840: g2.draw(side2);
841: }
842:
843: if (drawFront) {
844: g2.draw(front);
845: }
846:
847: }
848: }
849: else {
850:
851: if (drawBack) {
852: Area side1 = new Area(new Rectangle2D.Double(
853: plotArea.getX(), plotArea.getY(),
854: arc.getStartPoint().getX() - plotArea.getX(),
855: plotArea.getHeight()));
856: side1.intersect(back);
857: g2.setPaint(paint);
858: g2.fill(side1);
859: g2.setPaint(outlinePaint);
860: g2.draw(side1);
861: }
862:
863: if (drawFront) {
864: Area side2 = new Area(new Rectangle2D.Double(
865: plotArea.getX(), plotArea.getY(),
866: arc.getEndPoint().getX() - plotArea.getX(),
867: plotArea.getHeight()));
868: side2.intersect(front);
869: g2.setPaint(paint);
870: g2.fill(side2);
871: g2.setPaint(outlinePaint);
872: g2.draw(side2);
873: }
874: }
875: }
876:
877: }
878:
879: }
880:
881:
886: public String getPlotType() {
887: return localizationResources.getString("Pie_3D_Plot");
888: }
889:
890:
899: private boolean isAngleAtFront(double angle) {
900: return (Math.sin(Math.toRadians(angle)) < 0.0);
901: }
902:
903:
912: private boolean isAngleAtBack(double angle) {
913: return (Math.sin(Math.toRadians(angle)) > 0.0);
914: }
915:
916:
923: public boolean equals(Object obj) {
924: if (obj == this) {
925: return true;
926: }
927: if (!(obj instanceof PiePlot3D)) {
928: return false;
929: }
930: PiePlot3D that = (PiePlot3D) obj;
931: if (this.depthFactor != that.depthFactor) {
932: return false;
933: }
934: return super.equals(obj);
935: }
936:
937: }