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