1:
152:
153: package ;
154:
155: import ;
156: import ;
157: import ;
158: import ;
159: import ;
160: import ;
161: import ;
162: import ;
163: import ;
164: import ;
165: import ;
166: import ;
167: import ;
168: import ;
169: import ;
170: import ;
171: import ;
172: import ;
173: import ;
174: import ;
175: import ;
176: import ;
177: import ;
178: import ;
179:
180: import ;
181: import ;
182: import ;
183: import ;
184: import ;
185: import ;
186: import ;
187: import ;
188: import ;
189: import ;
190: import ;
191: import ;
192: import ;
193: import ;
194: import ;
195: import ;
196: import ;
197: import ;
198: import ;
199: import ;
200: import ;
201: import ;
202: import ;
203: import ;
204: import ;
205: import ;
206: import ;
207: import ;
208: import ;
209: import ;
210:
211:
227: public class PiePlot extends Plot implements Cloneable, Serializable {
228:
229:
230: private static final long serialVersionUID = -795612466005590431L;
231:
232:
233: public static final double DEFAULT_INTERIOR_GAP = 0.08;
234:
235:
236: public static final double MAX_INTERIOR_GAP = 0.40;
237:
238:
239: public static final double DEFAULT_START_ANGLE = 90.0;
240:
241:
242: public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
243: Font.PLAIN, 10);
244:
245:
246: public static final Paint DEFAULT_LABEL_PAINT = Color.black;
247:
248:
249: public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255,
250: 255, 192);
251:
252:
253: public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
254:
255:
256: public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke(
257: 0.5f);
258:
259:
260: public static final Paint DEFAULT_LABEL_SHADOW_PAINT = new Color(151, 151,
261: 151, 128);
262:
263:
264: public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;
265:
266:
267: private PieDataset dataset;
268:
269:
270: private int pieIndex;
271:
272:
276: private double interiorGap;
277:
278:
279: private boolean circular;
280:
281:
282: private double startAngle;
283:
284:
285: private Rotation direction;
286:
287:
294: private transient Paint sectionPaint;
295:
296:
297: private PaintMap sectionPaintMap;
298:
299:
300: private transient Paint baseSectionPaint;
301:
302:
306: private boolean sectionOutlinesVisible;
307:
308:
315: private transient Paint sectionOutlinePaint;
316:
317:
318: private PaintMap sectionOutlinePaintMap;
319:
320:
321: private transient Paint baseSectionOutlinePaint;
322:
323:
330: private transient Stroke sectionOutlineStroke;
331:
332:
333: private StrokeMap sectionOutlineStrokeMap;
334:
335:
336: private transient Stroke baseSectionOutlineStroke;
337:
338:
339: private transient Paint shadowPaint = Color.gray;
340:
341:
342: private double shadowXOffset = 4.0f;
343:
344:
345: private double shadowYOffset = 4.0f;
346:
347:
348: private Map explodePercentages;
349:
350:
351: private PieSectionLabelGenerator labelGenerator;
352:
353:
354: private Font labelFont;
355:
356:
357: private transient Paint labelPaint;
358:
359:
363: private transient Paint labelBackgroundPaint;
364:
365:
369: private transient Paint labelOutlinePaint;
370:
371:
375: private transient Stroke labelOutlineStroke;
376:
377:
381: private transient Paint labelShadowPaint;
382:
383:
388: private boolean simpleLabels = true;
389:
390:
396: private RectangleInsets labelPadding;
397:
398:
403: private RectangleInsets simpleLabelOffset;
404:
405:
406: private double maximumLabelWidth = 0.14;
407:
408:
412: private double labelGap = 0.025;
413:
414:
415: private boolean labelLinksVisible;
416:
417:
418: private double labelLinkMargin = 0.025;
419:
420:
421: private transient Paint labelLinkPaint = Color.black;
422:
423:
424: private transient Stroke labelLinkStroke = new BasicStroke(0.5f);
425:
426:
431: private AbstractPieLabelDistributor labelDistributor;
432:
433:
434: private PieToolTipGenerator toolTipGenerator;
435:
436:
437: private PieURLGenerator urlGenerator;
438:
439:
440: private PieSectionLabelGenerator legendLabelGenerator;
441:
442:
443: private PieSectionLabelGenerator legendLabelToolTipGenerator;
444:
445:
450: private PieURLGenerator legendLabelURLGenerator;
451:
452:
455: private boolean ignoreNullValues;
456:
457:
460: private boolean ignoreZeroValues;
461:
462:
463: private transient Shape legendItemShape;
464:
465:
476: private double minimumArcAngleToDraw;
477:
478:
479: protected static ResourceBundle localizationResources =
480: ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
481:
482:
487: static final boolean DEBUG_DRAW_INTERIOR = false;
488:
489:
494: static final boolean DEBUG_DRAW_LINK_AREA = false;
495:
496:
500: static final boolean DEBUG_DRAW_PIE_AREA = false;
501:
502:
505: public PiePlot() {
506: this(null);
507: }
508:
509:
514: public PiePlot(PieDataset dataset) {
515: super();
516: this.dataset = dataset;
517: if (dataset != null) {
518: dataset.addChangeListener(this);
519: }
520: this.pieIndex = 0;
521:
522: this.interiorGap = DEFAULT_INTERIOR_GAP;
523: this.circular = true;
524: this.startAngle = DEFAULT_START_ANGLE;
525: this.direction = Rotation.CLOCKWISE;
526: this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;
527:
528: this.sectionPaint = null;
529: this.sectionPaintMap = new PaintMap();
530: this.baseSectionPaint = Color.gray;
531:
532: this.sectionOutlinesVisible = true;
533: this.sectionOutlinePaint = null;
534: this.sectionOutlinePaintMap = new PaintMap();
535: this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;
536:
537: this.sectionOutlineStroke = null;
538: this.sectionOutlineStrokeMap = new StrokeMap();
539: this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;
540:
541: this.explodePercentages = new TreeMap();
542:
543: this.labelGenerator = new StandardPieSectionLabelGenerator();
544: this.labelFont = DEFAULT_LABEL_FONT;
545: this.labelPaint = DEFAULT_LABEL_PAINT;
546: this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
547: this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
548: this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
549: this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
550: this.labelLinksVisible = true;
551: this.labelDistributor = new PieLabelDistributor(0);
552:
553: this.simpleLabels = false;
554: this.simpleLabelOffset = new RectangleInsets(UnitType.RELATIVE, 0.18,
555: 0.18, 0.18, 0.18);
556: this.labelPadding = new RectangleInsets(2, 2, 2, 2);
557:
558: this.toolTipGenerator = null;
559: this.urlGenerator = null;
560: this.legendLabelGenerator = new StandardPieSectionLabelGenerator();
561: this.legendLabelToolTipGenerator = null;
562: this.legendLabelURLGenerator = null;
563: this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;
564:
565: this.ignoreNullValues = false;
566: this.ignoreZeroValues = false;
567: }
568:
569:
576: public PieDataset getDataset() {
577: return this.dataset;
578: }
579:
580:
587: public void setDataset(PieDataset dataset) {
588:
589:
590: PieDataset existing = this.dataset;
591: if (existing != null) {
592: existing.removeChangeListener(this);
593: }
594:
595:
596: this.dataset = dataset;
597: if (dataset != null) {
598: setDatasetGroup(dataset.getGroup());
599: dataset.addChangeListener(this);
600: }
601:
602:
603: DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
604: datasetChanged(event);
605: }
606:
607:
615: public int getPieIndex() {
616: return this.pieIndex;
617: }
618:
619:
627: public void setPieIndex(int index) {
628: this.pieIndex = index;
629: }
630:
631:
639: public double getStartAngle() {
640: return this.startAngle;
641: }
642:
643:
653: public void setStartAngle(double angle) {
654: this.startAngle = angle;
655: notifyListeners(new PlotChangeEvent(this));
656: }
657:
658:
666: public Rotation getDirection() {
667: return this.direction;
668: }
669:
670:
678: public void setDirection(Rotation direction) {
679: if (direction == null) {
680: throw new IllegalArgumentException("Null 'direction' argument.");
681: }
682: this.direction = direction;
683: notifyListeners(new PlotChangeEvent(this));
684:
685: }
686:
687:
695: public double getInteriorGap() {
696: return this.interiorGap;
697: }
698:
699:
709: public void setInteriorGap(double percent) {
710:
711: if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
712: throw new IllegalArgumentException(
713: "Invalid 'percent' (" + percent + ") argument.");
714: }
715:
716: if (this.interiorGap != percent) {
717: this.interiorGap = percent;
718: notifyListeners(new PlotChangeEvent(this));
719: }
720:
721: }
722:
723:
731: public boolean isCircular() {
732: return this.circular;
733: }
734:
735:
743: public void setCircular(boolean flag) {
744: setCircular(flag, true);
745: }
746:
747:
756: public void setCircular(boolean circular, boolean notify) {
757: this.circular = circular;
758: if (notify) {
759: notifyListeners(new PlotChangeEvent(this));
760: }
761: }
762:
763:
771: public boolean getIgnoreNullValues() {
772: return this.ignoreNullValues;
773: }
774:
775:
786: public void setIgnoreNullValues(boolean flag) {
787: this.ignoreNullValues = flag;
788: notifyListeners(new PlotChangeEvent(this));
789: }
790:
791:
799: public boolean getIgnoreZeroValues() {
800: return this.ignoreZeroValues;
801: }
802:
803:
814: public void setIgnoreZeroValues(boolean flag) {
815: this.ignoreZeroValues = flag;
816: notifyListeners(new PlotChangeEvent(this));
817: }
818:
819:
820:
821:
833: protected Paint lookupSectionPaint(Comparable key) {
834: return lookupSectionPaint(key, false);
835: }
836:
837:
860: protected Paint lookupSectionPaint(Comparable key, boolean autoPopulate) {
861:
862:
863: Paint result = getSectionPaint();
864: if (result != null) {
865: return result;
866: }
867:
868:
869: result = this.sectionPaintMap.getPaint(key);
870: if (result != null) {
871: return result;
872: }
873:
874:
875: if (autoPopulate) {
876: DrawingSupplier ds = getDrawingSupplier();
877: if (ds != null) {
878: result = ds.getNextPaint();
879: this.sectionPaintMap.put(key, result);
880: }
881: else {
882: result = this.baseSectionPaint;
883: }
884: }
885: else {
886: result = this.baseSectionPaint;
887: }
888: return result;
889: }
890:
891:
901: public Paint getSectionPaint() {
902: return this.sectionPaint;
903: }
904:
905:
917: public void setSectionPaint(Paint paint) {
918: this.sectionPaint = paint;
919: notifyListeners(new PlotChangeEvent(this));
920: }
921:
922:
936: protected Comparable getSectionKey(int section) {
937: Comparable key = null;
938: if (this.dataset != null) {
939: if (section >= 0 && section < this.dataset.getItemCount()) {
940: key = this.dataset.getKey(section);
941: }
942: }
943: if (key == null) {
944: key = new Integer(section);
945: }
946: return key;
947: }
948:
949:
965: public Paint getSectionPaint(Comparable key) {
966:
967: return this.sectionPaintMap.getPaint(key);
968: }
969:
970:
984: public void setSectionPaint(Comparable key, Paint paint) {
985:
986: this.sectionPaintMap.put(key, paint);
987: notifyListeners(new PlotChangeEvent(this));
988: }
989:
990:
998: public Paint getBaseSectionPaint() {
999: return this.baseSectionPaint;
1000: }
1001:
1002:
1010: public void setBaseSectionPaint(Paint paint) {
1011: if (paint == null) {
1012: throw new IllegalArgumentException("Null 'paint' argument.");
1013: }
1014: this.baseSectionPaint = paint;
1015: notifyListeners(new PlotChangeEvent(this));
1016: }
1017:
1018:
1019:
1020:
1029: public boolean getSectionOutlinesVisible() {
1030: return this.sectionOutlinesVisible;
1031: }
1032:
1033:
1042: public void setSectionOutlinesVisible(boolean visible) {
1043: this.sectionOutlinesVisible = visible;
1044: notifyListeners(new PlotChangeEvent(this));
1045: }
1046:
1047:
1059: protected Paint lookupSectionOutlinePaint(Comparable key) {
1060: return lookupSectionOutlinePaint(key, false);
1061: }
1062:
1063:
1086: protected Paint lookupSectionOutlinePaint(Comparable key,
1087: boolean autoPopulate) {
1088:
1089:
1090: Paint result = getSectionOutlinePaint();
1091: if (result != null) {
1092: return result;
1093: }
1094:
1095:
1096: result = this.sectionOutlinePaintMap.getPaint(key);
1097: if (result != null) {
1098: return result;
1099: }
1100:
1101:
1102: if (autoPopulate) {
1103: DrawingSupplier ds = getDrawingSupplier();
1104: if (ds != null) {
1105: result = ds.getNextOutlinePaint();
1106: this.sectionOutlinePaintMap.put(key, result);
1107: }
1108: else {
1109: result = this.baseSectionOutlinePaint;
1110: }
1111: }
1112: else {
1113: result = this.baseSectionOutlinePaint;
1114: }
1115: return result;
1116: }
1117:
1118:
1129: public Paint getSectionOutlinePaint() {
1130: return this.sectionOutlinePaint;
1131: }
1132:
1133:
1146: public void setSectionOutlinePaint(Paint paint) {
1147: this.sectionOutlinePaint = paint;
1148: notifyListeners(new PlotChangeEvent(this));
1149: }
1150:
1151:
1167: public Paint getSectionOutlinePaint(Comparable key) {
1168:
1169: return this.sectionOutlinePaintMap.getPaint(key);
1170: }
1171:
1172:
1186: public void setSectionOutlinePaint(Comparable key, Paint paint) {
1187:
1188: this.sectionOutlinePaintMap.put(key, paint);
1189: notifyListeners(new PlotChangeEvent(this));
1190: }
1191:
1192:
1200: public Paint getBaseSectionOutlinePaint() {
1201: return this.baseSectionOutlinePaint;
1202: }
1203:
1204:
1211: public void setBaseSectionOutlinePaint(Paint paint) {
1212: if (paint == null) {
1213: throw new IllegalArgumentException("Null 'paint' argument.");
1214: }
1215: this.baseSectionOutlinePaint = paint;
1216: notifyListeners(new PlotChangeEvent(this));
1217: }
1218:
1219:
1220:
1221:
1233: protected Stroke lookupSectionOutlineStroke(Comparable key) {
1234: return lookupSectionOutlineStroke(key, false);
1235: }
1236:
1237:
1260: protected Stroke lookupSectionOutlineStroke(Comparable key,
1261: boolean autoPopulate) {
1262:
1263:
1264: Stroke result = getSectionOutlineStroke();
1265: if (result != null) {
1266: return result;
1267: }
1268:
1269:
1270: result = this.sectionOutlineStrokeMap.getStroke(key);
1271: if (result != null) {
1272: return result;
1273: }
1274:
1275:
1276: if (autoPopulate) {
1277: DrawingSupplier ds = getDrawingSupplier();
1278: if (ds != null) {
1279: result = ds.getNextOutlineStroke();
1280: this.sectionOutlineStrokeMap.put(key, result);
1281: }
1282: else {
1283: result = this.baseSectionOutlineStroke;
1284: }
1285: }
1286: else {
1287: result = this.baseSectionOutlineStroke;
1288: }
1289: return result;
1290: }
1291:
1292:
1303: public Stroke getSectionOutlineStroke() {
1304: return this.sectionOutlineStroke;
1305: }
1306:
1307:
1320: public void setSectionOutlineStroke(Stroke stroke) {
1321: this.sectionOutlineStroke = stroke;
1322: notifyListeners(new PlotChangeEvent(this));
1323: }
1324:
1325:
1341: public Stroke getSectionOutlineStroke(Comparable key) {
1342:
1343: return this.sectionOutlineStrokeMap.getStroke(key);
1344: }
1345:
1346:
1360: public void setSectionOutlineStroke(Comparable key, Stroke stroke) {
1361:
1362: this.sectionOutlineStrokeMap.put(key, stroke);
1363: notifyListeners(new PlotChangeEvent(this));
1364: }
1365:
1366:
1374: public Stroke getBaseSectionOutlineStroke() {
1375: return this.baseSectionOutlineStroke;
1376: }
1377:
1378:
1385: public void setBaseSectionOutlineStroke(Stroke stroke) {
1386: if (stroke == null) {
1387: throw new IllegalArgumentException("Null 'stroke' argument.");
1388: }
1389: this.baseSectionOutlineStroke = stroke;
1390: notifyListeners(new PlotChangeEvent(this));
1391: }
1392:
1393:
1400: public Paint getShadowPaint() {
1401: return this.shadowPaint;
1402: }
1403:
1404:
1412: public void setShadowPaint(Paint paint) {
1413: this.shadowPaint = paint;
1414: notifyListeners(new PlotChangeEvent(this));
1415: }
1416:
1417:
1424: public double getShadowXOffset() {
1425: return this.shadowXOffset;
1426: }
1427:
1428:
1436: public void setShadowXOffset(double offset) {
1437: this.shadowXOffset = offset;
1438: notifyListeners(new PlotChangeEvent(this));
1439: }
1440:
1441:
1448: public double getShadowYOffset() {
1449: return this.shadowYOffset;
1450: }
1451:
1452:
1460: public void setShadowYOffset(double offset) {
1461: this.shadowYOffset = offset;
1462: notifyListeners(new PlotChangeEvent(this));
1463: }
1464:
1465:
1481: public double getExplodePercent(Comparable key) {
1482: double result = 0.0;
1483: if (this.explodePercentages != null) {
1484: Number percent = (Number) this.explodePercentages.get(key);
1485: if (percent != null) {
1486: result = percent.doubleValue();
1487: }
1488: }
1489: return result;
1490: }
1491:
1492:
1503: public void setExplodePercent(Comparable key, double percent) {
1504: if (key == null) {
1505: throw new IllegalArgumentException("Null 'key' argument.");
1506: }
1507: if (this.explodePercentages == null) {
1508: this.explodePercentages = new TreeMap();
1509: }
1510: this.explodePercentages.put(key, new Double(percent));
1511: notifyListeners(new PlotChangeEvent(this));
1512: }
1513:
1514:
1519: public double getMaximumExplodePercent() {
1520: double result = 0.0;
1521: Iterator iterator = this.dataset.getKeys().iterator();
1522: while (iterator.hasNext()) {
1523: Comparable key = (Comparable) iterator.next();
1524: Number explode = (Number) this.explodePercentages.get(key);
1525: if (explode != null) {
1526: result = Math.max(result, explode.doubleValue());
1527: }
1528: }
1529: return result;
1530: }
1531:
1532:
1539: public PieSectionLabelGenerator getLabelGenerator() {
1540: return this.labelGenerator;
1541: }
1542:
1543:
1551: public void setLabelGenerator(PieSectionLabelGenerator generator) {
1552: this.labelGenerator = generator;
1553: notifyListeners(new PlotChangeEvent(this));
1554: }
1555:
1556:
1564: public double getLabelGap() {
1565: return this.labelGap;
1566: }
1567:
1568:
1577: public void setLabelGap(double gap) {
1578: this.labelGap = gap;
1579: notifyListeners(new PlotChangeEvent(this));
1580: }
1581:
1582:
1589: public double getMaximumLabelWidth() {
1590: return this.maximumLabelWidth;
1591: }
1592:
1593:
1601: public void setMaximumLabelWidth(double width) {
1602: this.maximumLabelWidth = width;
1603: notifyListeners(new PlotChangeEvent(this));
1604: }
1605:
1606:
1614: public boolean getLabelLinksVisible() {
1615: return this.labelLinksVisible;
1616: }
1617:
1618:
1629: public void setLabelLinksVisible(boolean visible) {
1630: this.labelLinksVisible = visible;
1631: notifyListeners(new PlotChangeEvent(this));
1632: }
1633:
1634:
1642: public double getLabelLinkMargin() {
1643: return this.labelLinkMargin;
1644: }
1645:
1646:
1654: public void setLabelLinkMargin(double margin) {
1655: this.labelLinkMargin = margin;
1656: notifyListeners(new PlotChangeEvent(this));
1657: }
1658:
1659:
1667: public Paint getLabelLinkPaint() {
1668: return this.labelLinkPaint;
1669: }
1670:
1671:
1680: public void setLabelLinkPaint(Paint paint) {
1681: if (paint == null) {
1682: throw new IllegalArgumentException("Null 'paint' argument.");
1683: }
1684: this.labelLinkPaint = paint;
1685: notifyListeners(new PlotChangeEvent(this));
1686: }
1687:
1688:
1695: public Stroke getLabelLinkStroke() {
1696: return this.labelLinkStroke;
1697: }
1698:
1699:
1707: public void setLabelLinkStroke(Stroke stroke) {
1708: if (stroke == null) {
1709: throw new IllegalArgumentException("Null 'stroke' argument.");
1710: }
1711: this.labelLinkStroke = stroke;
1712: notifyListeners(new PlotChangeEvent(this));
1713: }
1714:
1715:
1722: public Font getLabelFont() {
1723: return this.labelFont;
1724: }
1725:
1726:
1734: public void setLabelFont(Font font) {
1735: if (font == null) {
1736: throw new IllegalArgumentException("Null 'font' argument.");
1737: }
1738: this.labelFont = font;
1739: notifyListeners(new PlotChangeEvent(this));
1740: }
1741:
1742:
1749: public Paint getLabelPaint() {
1750: return this.labelPaint;
1751: }
1752:
1753:
1761: public void setLabelPaint(Paint paint) {
1762: if (paint == null) {
1763: throw new IllegalArgumentException("Null 'paint' argument.");
1764: }
1765: this.labelPaint = paint;
1766: notifyListeners(new PlotChangeEvent(this));
1767: }
1768:
1769:
1776: public Paint getLabelBackgroundPaint() {
1777: return this.labelBackgroundPaint;
1778: }
1779:
1780:
1788: public void setLabelBackgroundPaint(Paint paint) {
1789: this.labelBackgroundPaint = paint;
1790: notifyListeners(new PlotChangeEvent(this));
1791: }
1792:
1793:
1800: public Paint getLabelOutlinePaint() {
1801: return this.labelOutlinePaint;
1802: }
1803:
1804:
1812: public void setLabelOutlinePaint(Paint paint) {
1813: this.labelOutlinePaint = paint;
1814: notifyListeners(new PlotChangeEvent(this));
1815: }
1816:
1817:
1824: public Stroke getLabelOutlineStroke() {
1825: return this.labelOutlineStroke;
1826: }
1827:
1828:
1836: public void setLabelOutlineStroke(Stroke stroke) {
1837: this.labelOutlineStroke = stroke;
1838: notifyListeners(new PlotChangeEvent(this));
1839: }
1840:
1841:
1848: public Paint getLabelShadowPaint() {
1849: return this.labelShadowPaint;
1850: }
1851:
1852:
1860: public void setLabelShadowPaint(Paint paint) {
1861: this.labelShadowPaint = paint;
1862: notifyListeners(new PlotChangeEvent(this));
1863: }
1864:
1865:
1874: public RectangleInsets getLabelPadding() {
1875: return this.labelPadding;
1876: }
1877:
1878:
1888: public void setLabelPadding(RectangleInsets padding) {
1889: if (padding == null) {
1890: throw new IllegalArgumentException("Null 'padding' argument.");
1891: }
1892: this.labelPadding = padding;
1893: notifyListeners(new PlotChangeEvent(this));
1894: }
1895:
1896:
1904: public boolean getSimpleLabels() {
1905: return this.simpleLabels;
1906: }
1907:
1908:
1917: public void setSimpleLabels(boolean simple) {
1918: this.simpleLabels = simple;
1919: notifyListeners(new PlotChangeEvent(this));
1920: }
1921:
1922:
1931: public RectangleInsets getSimpleLabelOffset() {
1932: return this.simpleLabelOffset;
1933: }
1934:
1935:
1945: public void setSimpleLabelOffset(RectangleInsets offset) {
1946: if (offset == null) {
1947: throw new IllegalArgumentException("Null 'offset' argument.");
1948: }
1949: this.simpleLabelOffset = offset;
1950: notifyListeners(new PlotChangeEvent(this));
1951: }
1952:
1953:
1961: public AbstractPieLabelDistributor getLabelDistributor() {
1962: return this.labelDistributor;
1963: }
1964:
1965:
1973: public void setLabelDistributor(AbstractPieLabelDistributor distributor) {
1974: if (distributor == null) {
1975: throw new IllegalArgumentException("Null 'distributor' argument.");
1976: }
1977: this.labelDistributor = distributor;
1978: notifyListeners(new PlotChangeEvent(this));
1979: }
1980:
1981:
1990: public PieToolTipGenerator getToolTipGenerator() {
1991: return this.toolTipGenerator;
1992: }
1993:
1994:
2003: public void setToolTipGenerator(PieToolTipGenerator generator) {
2004: this.toolTipGenerator = generator;
2005: notifyListeners(new PlotChangeEvent(this));
2006: }
2007:
2008:
2015: public PieURLGenerator getURLGenerator() {
2016: return this.urlGenerator;
2017: }
2018:
2019:
2027: public void setURLGenerator(PieURLGenerator generator) {
2028: this.urlGenerator = generator;
2029: notifyListeners(new PlotChangeEvent(this));
2030: }
2031:
2032:
2040: public double getMinimumArcAngleToDraw() {
2041: return this.minimumArcAngleToDraw;
2042: }
2043:
2044:
2062: public void setMinimumArcAngleToDraw(double angle) {
2063: this.minimumArcAngleToDraw = angle;
2064: }
2065:
2066:
2073: public Shape getLegendItemShape() {
2074: return this.legendItemShape;
2075: }
2076:
2077:
2085: public void setLegendItemShape(Shape shape) {
2086: if (shape == null) {
2087: throw new IllegalArgumentException("Null 'shape' argument.");
2088: }
2089: this.legendItemShape = shape;
2090: notifyListeners(new PlotChangeEvent(this));
2091: }
2092:
2093:
2100: public PieSectionLabelGenerator getLegendLabelGenerator() {
2101: return this.legendLabelGenerator;
2102: }
2103:
2104:
2112: public void setLegendLabelGenerator(PieSectionLabelGenerator generator) {
2113: if (generator == null) {
2114: throw new IllegalArgumentException("Null 'generator' argument.");
2115: }
2116: this.legendLabelGenerator = generator;
2117: notifyListeners(new PlotChangeEvent(this));
2118: }
2119:
2120:
2127: public PieSectionLabelGenerator getLegendLabelToolTipGenerator() {
2128: return this.legendLabelToolTipGenerator;
2129: }
2130:
2131:
2139: public void setLegendLabelToolTipGenerator(
2140: PieSectionLabelGenerator generator) {
2141: this.legendLabelToolTipGenerator = generator;
2142: notifyListeners(new PlotChangeEvent(this));
2143: }
2144:
2145:
2154: public PieURLGenerator getLegendLabelURLGenerator() {
2155: return this.legendLabelURLGenerator;
2156: }
2157:
2158:
2168: public void setLegendLabelURLGenerator(PieURLGenerator generator) {
2169: this.legendLabelURLGenerator = generator;
2170: notifyListeners(new PlotChangeEvent(this));
2171: }
2172:
2173:
2188: public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea,
2189: PiePlot plot, Integer index, PlotRenderingInfo info) {
2190:
2191: PiePlotState state = new PiePlotState(info);
2192: state.setPassesRequired(2);
2193: state.setTotal(DatasetUtilities.calculatePieDatasetTotal(
2194: plot.getDataset()));
2195: state.setLatestAngle(plot.getStartAngle());
2196: return state;
2197:
2198: }
2199:
2200:
2211: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2212: PlotState parentState, PlotRenderingInfo info) {
2213:
2214:
2215: RectangleInsets insets = getInsets();
2216: insets.trim(area);
2217:
2218: if (info != null) {
2219: info.setPlotArea(area);
2220: info.setDataArea(area);
2221: }
2222:
2223: drawBackground(g2, area);
2224: drawOutline(g2, area);
2225:
2226: Shape savedClip = g2.getClip();
2227: g2.clip(area);
2228:
2229: Composite originalComposite = g2.getComposite();
2230: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2231: getForegroundAlpha()));
2232:
2233: if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
2234: drawPie(g2, area, info);
2235: }
2236: else {
2237: drawNoDataMessage(g2, area);
2238: }
2239:
2240: g2.setClip(savedClip);
2241: g2.setComposite(originalComposite);
2242:
2243: drawOutline(g2, area);
2244:
2245: }
2246:
2247:
2254: protected void drawPie(Graphics2D g2, Rectangle2D plotArea,
2255: PlotRenderingInfo info) {
2256:
2257: PiePlotState state = initialise(g2, plotArea, this, null, info);
2258:
2259:
2260: double labelReserve = 0.0;
2261: if (this.labelGenerator != null && !this.simpleLabels) {
2262: labelReserve = this.labelGap + this.maximumLabelWidth;
2263: }
2264: double gapHorizontal = plotArea.getWidth() * (this.interiorGap
2265: + labelReserve) * 2.0;
2266: double gapVertical = plotArea.getHeight() * this.interiorGap * 2.0;
2267:
2268:
2269: if (DEBUG_DRAW_INTERIOR) {
2270: double hGap = plotArea.getWidth() * this.interiorGap;
2271: double vGap = plotArea.getHeight() * this.interiorGap;
2272:
2273: double igx1 = plotArea.getX() + hGap;
2274: double igx2 = plotArea.getMaxX() - hGap;
2275: double igy1 = plotArea.getY() + vGap;
2276: double igy2 = plotArea.getMaxY() - vGap;
2277: g2.setPaint(Color.gray);
2278: g2.draw(new Rectangle2D.Double(igx1, igy1, igx2 - igx1,
2279: igy2 - igy1));
2280: }
2281:
2282: double linkX = plotArea.getX() + gapHorizontal / 2;
2283: double linkY = plotArea.getY() + gapVertical / 2;
2284: double linkW = plotArea.getWidth() - gapHorizontal;
2285: double linkH = plotArea.getHeight() - gapVertical;
2286:
2287:
2288: if (this.circular) {
2289: double min = Math.min(linkW, linkH) / 2;
2290: linkX = (linkX + linkX + linkW) / 2 - min;
2291: linkY = (linkY + linkY + linkH) / 2 - min;
2292: linkW = 2 * min;
2293: linkH = 2 * min;
2294: }
2295:
2296:
2297:
2298: Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW,
2299: linkH);
2300: state.setLinkArea(linkArea);
2301:
2302: if (DEBUG_DRAW_LINK_AREA) {
2303: g2.setPaint(Color.blue);
2304: g2.draw(linkArea);
2305: g2.setPaint(Color.yellow);
2306: g2.draw(new Ellipse2D.Double(linkArea.getX(), linkArea.getY(),
2307: linkArea.getWidth(), linkArea.getHeight()));
2308: }
2309:
2310:
2311:
2312:
2313: double lm = 0.0;
2314: if (!this.simpleLabels) {
2315: lm = this.labelLinkMargin;
2316: }
2317: double hh = linkArea.getWidth() * lm * 2.0;
2318: double vv = linkArea.getHeight() * lm * 2.0;
2319: Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0,
2320: linkY + vv / 2.0, linkW - hh, linkH - vv);
2321:
2322: state.setExplodedPieArea(explodeArea);
2323:
2324:
2325:
2326:
2327: double maximumExplodePercent = getMaximumExplodePercent();
2328: double percent = maximumExplodePercent / (1.0 + maximumExplodePercent);
2329:
2330: double h1 = explodeArea.getWidth() * percent;
2331: double v1 = explodeArea.getHeight() * percent;
2332: Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX()
2333: + h1 / 2.0, explodeArea.getY() + v1 / 2.0,
2334: explodeArea.getWidth() - h1, explodeArea.getHeight() - v1);
2335:
2336: if (DEBUG_DRAW_PIE_AREA) {
2337: g2.setPaint(Color.green);
2338: g2.draw(pieArea);
2339: }
2340: state.setPieArea(pieArea);
2341: state.setPieCenterX(pieArea.getCenterX());
2342: state.setPieCenterY(pieArea.getCenterY());
2343: state.setPieWRadius(pieArea.getWidth() / 2.0);
2344: state.setPieHRadius(pieArea.getHeight() / 2.0);
2345:
2346:
2347: if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) {
2348:
2349: List keys = this.dataset.getKeys();
2350: double totalValue = DatasetUtilities.calculatePieDatasetTotal(
2351: this.dataset);
2352:
2353: int passesRequired = state.getPassesRequired();
2354: for (int pass = 0; pass < passesRequired; pass++) {
2355: double runningTotal = 0.0;
2356: for (int section = 0; section < keys.size(); section++) {
2357: Number n = this.dataset.getValue(section);
2358: if (n != null) {
2359: double value = n.doubleValue();
2360: if (value > 0.0) {
2361: runningTotal += value;
2362: drawItem(g2, section, explodeArea, state, pass);
2363: }
2364: }
2365: }
2366: }
2367: if (this.simpleLabels) {
2368: drawSimpleLabels(g2, keys, totalValue, plotArea, linkArea,
2369: state);
2370: }
2371: else {
2372: drawLabels(g2, keys, totalValue, plotArea, linkArea, state);
2373: }
2374:
2375: }
2376: else {
2377: drawNoDataMessage(g2, plotArea);
2378: }
2379: }
2380:
2381:
2390: protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea,
2391: PiePlotState state, int currentPass) {
2392:
2393: Number n = this.dataset.getValue(section);
2394: if (n == null) {
2395: return;
2396: }
2397: double value = n.doubleValue();
2398: double angle1 = 0.0;
2399: double angle2 = 0.0;
2400:
2401: if (this.direction == Rotation.CLOCKWISE) {
2402: angle1 = state.getLatestAngle();
2403: angle2 = angle1 - value / state.getTotal() * 360.0;
2404: }
2405: else if (this.direction == Rotation.ANTICLOCKWISE) {
2406: angle1 = state.getLatestAngle();
2407: angle2 = angle1 + value / state.getTotal() * 360.0;
2408: }
2409: else {
2410: throw new IllegalStateException("Rotation type not recognised.");
2411: }
2412:
2413: double angle = (angle2 - angle1);
2414: if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
2415: double ep = 0.0;
2416: double mep = getMaximumExplodePercent();
2417: if (mep > 0.0) {
2418: ep = getExplodePercent(section) / mep;
2419: }
2420: Rectangle2D arcBounds = getArcBounds(state.getPieArea(),
2421: state.getExplodedPieArea(), angle1, angle, ep);
2422: Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle,
2423: Arc2D.PIE);
2424:
2425: if (currentPass == 0) {
2426: if (this.shadowPaint != null) {
2427: Shape shadowArc = ShapeUtilities.createTranslatedShape(
2428: arc, (float) this.shadowXOffset,
2429: (float) this.shadowYOffset);
2430: g2.setPaint(this.shadowPaint);
2431: g2.fill(shadowArc);
2432: }
2433: }
2434: else if (currentPass == 1) {
2435: Comparable key = getSectionKey(section);
2436: Paint paint = lookupSectionPaint(key, true);
2437: g2.setPaint(paint);
2438: g2.fill(arc);
2439:
2440: Paint outlinePaint = lookupSectionOutlinePaint(key);
2441: Stroke outlineStroke = lookupSectionOutlineStroke(key);
2442: if (this.sectionOutlinesVisible) {
2443: g2.setPaint(outlinePaint);
2444: g2.setStroke(outlineStroke);
2445: g2.draw(arc);
2446: }
2447:
2448:
2449:
2450: if (state.getInfo() != null) {
2451: EntityCollection entities = state.getEntityCollection();
2452: if (entities != null) {
2453: String tip = null;
2454: if (this.toolTipGenerator != null) {
2455: tip = this.toolTipGenerator.generateToolTip(
2456: this.dataset, key);
2457: }
2458: String url = null;
2459: if (this.urlGenerator != null) {
2460: url = this.urlGenerator.generateURL(this.dataset,
2461: key, this.pieIndex);
2462: }
2463: PieSectionEntity entity = new PieSectionEntity(
2464: arc, this.dataset, this.pieIndex, section, key,
2465: tip, url);
2466: entities.add(entity);
2467: }
2468: }
2469: }
2470: }
2471: state.setLatestAngle(angle2);
2472: }
2473:
2474:
2486: protected void drawSimpleLabels(Graphics2D g2, List keys,
2487: double totalValue, Rectangle2D plotArea, Rectangle2D pieArea,
2488: PiePlotState state) {
2489:
2490: Composite originalComposite = g2.getComposite();
2491: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2492: 1.0f));
2493:
2494: RectangleInsets labelInsets = new RectangleInsets(UnitType.RELATIVE,
2495: 0.18, 0.18, 0.18, 0.18);
2496: Rectangle2D labelsArea = labelInsets.createInsetRectangle(pieArea);
2497: double runningTotal = 0.0;
2498: Iterator iterator = keys.iterator();
2499: while (iterator.hasNext()) {
2500: Comparable key = (Comparable) iterator.next();
2501: boolean include = true;
2502: double v = 0.0;
2503: Number n = getDataset().getValue(key);
2504: if (n == null) {
2505: include = !getIgnoreNullValues();
2506: }
2507: else {
2508: v = n.doubleValue();
2509: include = getIgnoreZeroValues() ? v > 0.0 : v >= 0.0;
2510: }
2511:
2512: if (include) {
2513: runningTotal = runningTotal + v;
2514:
2515:
2516: double mid = getStartAngle() + (getDirection().getFactor()
2517: * ((runningTotal - v / 2.0) * 360) / totalValue);
2518:
2519: Arc2D arc = new Arc2D.Double(labelsArea, getStartAngle(),
2520: mid - getStartAngle(), Arc2D.OPEN);
2521: int x = (int) arc.getEndPoint().getX();
2522: int y = (int) arc.getEndPoint().getY();
2523:
2524: PieSectionLabelGenerator labelGenerator = getLabelGenerator();
2525: if (labelGenerator == null) {
2526: continue;
2527: }
2528: String label = labelGenerator.generateSectionLabel(
2529: this.dataset, key);
2530: if (label == null) {
2531: continue;
2532: }
2533: g2.setFont(this.labelFont);
2534: FontMetrics fm = g2.getFontMetrics();
2535: Rectangle2D bounds = TextUtilities.getTextBounds(label, g2, fm);
2536: Rectangle2D out = this.labelPadding.createOutsetRectangle(
2537: bounds);
2538: Shape bg = ShapeUtilities.createTranslatedShape(out,
2539: x - bounds.getCenterX(), y - bounds.getCenterY());
2540: if (this.labelShadowPaint != null) {
2541: Shape shadow = ShapeUtilities.createTranslatedShape(bg,
2542: this.shadowXOffset, this.shadowYOffset);
2543: g2.setPaint(this.labelShadowPaint);
2544: g2.fill(shadow);
2545: }
2546: if (this.labelBackgroundPaint != null) {
2547: g2.setPaint(this.labelBackgroundPaint);
2548: g2.fill(bg);
2549: }
2550: if (this.labelOutlinePaint != null
2551: && this.labelOutlineStroke != null) {
2552: g2.setPaint(this.labelOutlinePaint);
2553: g2.setStroke(this.labelOutlineStroke);
2554: g2.draw(bg);
2555: }
2556:
2557: g2.setPaint(this.labelPaint);
2558: g2.setFont(this.labelFont);
2559: TextUtilities.drawAlignedString(getLabelGenerator()
2560: .generateSectionLabel(getDataset(), key), g2, x, y,
2561: TextAnchor.CENTER);
2562:
2563: }
2564: }
2565:
2566: g2.setComposite(originalComposite);
2567:
2568: }
2569:
2570:
2580: protected void drawLabels(Graphics2D g2, List keys, double totalValue,
2581: Rectangle2D plotArea, Rectangle2D linkArea,
2582: PiePlotState state) {
2583:
2584: Composite originalComposite = g2.getComposite();
2585: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2586: 1.0f));
2587:
2588:
2589: DefaultKeyedValues leftKeys = new DefaultKeyedValues();
2590: DefaultKeyedValues rightKeys = new DefaultKeyedValues();
2591:
2592: double runningTotal = 0.0;
2593: Iterator iterator = keys.iterator();
2594: while (iterator.hasNext()) {
2595: Comparable key = (Comparable) iterator.next();
2596: boolean include = true;
2597: double v = 0.0;
2598: Number n = this.dataset.getValue(key);
2599: if (n == null) {
2600: include = !this.ignoreNullValues;
2601: }
2602: else {
2603: v = n.doubleValue();
2604: include = this.ignoreZeroValues ? v > 0.0 : v >= 0.0;
2605: }
2606:
2607: if (include) {
2608: runningTotal = runningTotal + v;
2609:
2610:
2611: double mid = this.startAngle + (this.direction.getFactor()
2612: * ((runningTotal - v / 2.0) * 360) / totalValue);
2613: if (Math.cos(Math.toRadians(mid)) < 0.0) {
2614: leftKeys.addValue(key, new Double(mid));
2615: }
2616: else {
2617: rightKeys.addValue(key, new Double(mid));
2618: }
2619: }
2620: }
2621:
2622: g2.setFont(getLabelFont());
2623:
2624:
2625:
2626: double marginX = plotArea.getX() + this.interiorGap * plotArea.getWidth();
2627: double gap = plotArea.getWidth() * this.labelGap;
2628: double ww = linkArea.getX() - gap - marginX;
2629: float labelWidth = (float) this.labelPadding.trimWidth(ww);
2630:
2631:
2632: if (this.labelGenerator != null) {
2633: drawLeftLabels(leftKeys, g2, plotArea, linkArea, labelWidth,
2634: state);
2635: drawRightLabels(rightKeys, g2, plotArea, linkArea, labelWidth,
2636: state);
2637: }
2638: g2.setComposite(originalComposite);
2639:
2640: }
2641:
2642:
2654: protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D g2,
2655: Rectangle2D plotArea, Rectangle2D linkArea,
2656: float maxLabelWidth, PiePlotState state) {
2657:
2658: this.labelDistributor.clear();
2659: double lGap = plotArea.getWidth() * this.labelGap;
2660: double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2661: for (int i = 0; i < leftKeys.getItemCount(); i++) {
2662: String label = this.labelGenerator.generateSectionLabel(
2663: this.dataset, leftKeys.getKey(i));
2664: if (label != null) {
2665: TextBlock block = TextUtilities.createTextBlock(label,
2666: this.labelFont, this.labelPaint, maxLabelWidth,
2667: new G2TextMeasurer(g2));
2668: TextBox labelBox = new TextBox(block);
2669: labelBox.setBackgroundPaint(this.labelBackgroundPaint);
2670: labelBox.setOutlinePaint(this.labelOutlinePaint);
2671: labelBox.setOutlineStroke(this.labelOutlineStroke);
2672: labelBox.setShadowPaint(this.labelShadowPaint);
2673: labelBox.setInteriorGap(this.labelPadding);
2674: double theta = Math.toRadians(
2675: leftKeys.getValue(i).doubleValue());
2676: double baseY = state.getPieCenterY() - Math.sin(theta)
2677: * verticalLinkRadius;
2678: double hh = labelBox.getHeight(g2);
2679:
2680: this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
2681: leftKeys.getKey(i), theta, baseY, labelBox, hh,
2682: lGap / 2.0 + lGap / 2.0 * -Math.cos(theta), 0.9
2683: + getExplodePercent(leftKeys.getKey(i))));
2684: }
2685: }
2686: this.labelDistributor.distributeLabels(plotArea.getMinY(),
2687: plotArea.getHeight());
2688: for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
2689: drawLeftLabel(g2, state,
2690: this.labelDistributor.getPieLabelRecord(i));
2691: }
2692: }
2693:
2694:
2704: protected void drawRightLabels(KeyedValues keys, Graphics2D g2,
2705: Rectangle2D plotArea, Rectangle2D linkArea,
2706: float maxLabelWidth, PiePlotState state) {
2707:
2708:
2709: this.labelDistributor.clear();
2710: double lGap = plotArea.getWidth() * this.labelGap;
2711: double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2712:
2713: for (int i = 0; i < keys.getItemCount(); i++) {
2714: String label = this.labelGenerator.generateSectionLabel(
2715: this.dataset, keys.getKey(i));
2716:
2717: if (label != null) {
2718: TextBlock block = TextUtilities.createTextBlock(label,
2719: this.labelFont, this.labelPaint, maxLabelWidth,
2720: new G2TextMeasurer(g2));
2721: TextBox labelBox = new TextBox(block);
2722: labelBox.setBackgroundPaint(this.labelBackgroundPaint);
2723: labelBox.setOutlinePaint(this.labelOutlinePaint);
2724: labelBox.setOutlineStroke(this.labelOutlineStroke);
2725: labelBox.setShadowPaint(this.labelShadowPaint);
2726: labelBox.setInteriorGap(this.labelPadding);
2727: double theta = Math.toRadians(keys.getValue(i).doubleValue());
2728: double baseY = state.getPieCenterY()
2729: - Math.sin(theta) * verticalLinkRadius;
2730: double hh = labelBox.getHeight(g2);
2731: this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
2732: keys.getKey(i), theta, baseY, labelBox, hh,
2733: lGap / 2.0 + lGap / 2.0 * Math.cos(theta),
2734: 0.9 + getExplodePercent(keys.getKey(i))));
2735: }
2736: }
2737: this.labelDistributor.distributeLabels(plotArea.getMinY(),
2738: plotArea.getHeight());
2739: for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
2740: drawRightLabel(g2, state,
2741: this.labelDistributor.getPieLabelRecord(i));
2742: }
2743:
2744: }
2745:
2746:
2751: public LegendItemCollection getLegendItems() {
2752:
2753: LegendItemCollection result = new LegendItemCollection();
2754: if (this.dataset == null) {
2755: return result;
2756: }
2757: List keys = this.dataset.getKeys();
2758: int section = 0;
2759: Shape shape = getLegendItemShape();
2760: Iterator iterator = keys.iterator();
2761: while (iterator.hasNext()) {
2762: Comparable key = (Comparable) iterator.next();
2763: Number n = this.dataset.getValue(key);
2764: boolean include = true;
2765: if (n == null) {
2766: include = !this.ignoreNullValues;
2767: }
2768: else {
2769: double v = n.doubleValue();
2770: if (v == 0.0) {
2771: include = !this.ignoreZeroValues;
2772: }
2773: else {
2774: include = v > 0.0;
2775: }
2776: }
2777: if (include) {
2778: String label = this.legendLabelGenerator.generateSectionLabel(
2779: this.dataset, key);
2780: if (label != null) {
2781: String description = label;
2782: String toolTipText = null;
2783: if (this.legendLabelToolTipGenerator != null) {
2784: toolTipText = this.legendLabelToolTipGenerator
2785: .generateSectionLabel(this.dataset, key);
2786: }
2787: String urlText = null;
2788: if (this.legendLabelURLGenerator != null) {
2789: urlText = this.legendLabelURLGenerator.generateURL(
2790: this.dataset, key, this.pieIndex);
2791: }
2792: Paint paint = lookupSectionPaint(key, true);
2793: Paint outlinePaint = lookupSectionOutlinePaint(key);
2794: Stroke outlineStroke = lookupSectionOutlineStroke(key);
2795: LegendItem item = new LegendItem(label, description,
2796: toolTipText, urlText, true, shape, true, paint,
2797: true, outlinePaint, outlineStroke,
2798: false,
2799: new Line2D.Float(), new BasicStroke(), Color.black);
2800: item.setDataset(getDataset());
2801: result.add(item);
2802: }
2803: section++;
2804: }
2805: else {
2806: section++;
2807: }
2808: }
2809: return result;
2810: }
2811:
2812:
2817: public String getPlotType() {
2818: return localizationResources.getString("Pie_Plot");
2819: }
2820:
2821:
2835: protected Rectangle2D getArcBounds(Rectangle2D unexploded,
2836: Rectangle2D exploded,
2837: double angle, double extent,
2838: double explodePercent) {
2839:
2840: if (explodePercent == 0.0) {
2841: return unexploded;
2842: }
2843: else {
2844: Arc2D arc1 = new Arc2D.Double(unexploded, angle, extent / 2,
2845: Arc2D.OPEN);
2846: Point2D point1 = arc1.getEndPoint();
2847: Arc2D.Double arc2 = new Arc2D.Double(exploded, angle, extent / 2,
2848: Arc2D.OPEN);
2849: Point2D point2 = arc2.getEndPoint();
2850: double deltaX = (point1.getX() - point2.getX()) * explodePercent;
2851: double deltaY = (point1.getY() - point2.getY()) * explodePercent;
2852: return new Rectangle2D.Double(unexploded.getX() - deltaX,
2853: unexploded.getY() - deltaY, unexploded.getWidth(),
2854: unexploded.getHeight());
2855: }
2856: }
2857:
2858:
2865: protected void drawLeftLabel(Graphics2D g2, PiePlotState state,
2866: PieLabelRecord record) {
2867:
2868: double anchorX = state.getLinkArea().getMinX();
2869: double targetX = anchorX - record.getGap();
2870: double targetY = record.getAllocatedY();
2871:
2872: if (this.labelLinksVisible) {
2873: double theta = record.getAngle();
2874: double linkX = state.getPieCenterX() + Math.cos(theta)
2875: * state.getPieWRadius() * record.getLinkPercent();
2876: double linkY = state.getPieCenterY() - Math.sin(theta)
2877: * state.getPieHRadius() * record.getLinkPercent();
2878: double elbowX = state.getPieCenterX() + Math.cos(theta)
2879: * state.getLinkArea().getWidth() / 2.0;
2880: double elbowY = state.getPieCenterY() - Math.sin(theta)
2881: * state.getLinkArea().getHeight() / 2.0;
2882: double anchorY = elbowY;
2883: g2.setPaint(this.labelLinkPaint);
2884: g2.setStroke(this.labelLinkStroke);
2885: g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
2886: g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY));
2887: g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY));
2888: }
2889: TextBox tb = record.getLabel();
2890: tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT);
2891:
2892: }
2893:
2894:
2901: protected void drawRightLabel(Graphics2D g2, PiePlotState state,
2902: PieLabelRecord record) {
2903:
2904: double anchorX = state.getLinkArea().getMaxX();
2905: double targetX = anchorX + record.getGap();
2906: double targetY = record.getAllocatedY();
2907:
2908: if (this.labelLinksVisible) {
2909: double theta = record.getAngle();
2910: double linkX = state.getPieCenterX() + Math.cos(theta)
2911: * state.getPieWRadius() * record.getLinkPercent();
2912: double linkY = state.getPieCenterY() - Math.sin(theta)
2913: * state.getPieHRadius() * record.getLinkPercent();
2914: double elbowX = state.getPieCenterX() + Math.cos(theta)
2915: * state.getLinkArea().getWidth() / 2.0;
2916: double elbowY = state.getPieCenterY() - Math.sin(theta)
2917: * state.getLinkArea().getHeight() / 2.0;
2918: double anchorY = elbowY;
2919: g2.setPaint(this.labelLinkPaint);
2920: g2.setStroke(this.labelLinkStroke);
2921: g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
2922: g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY));
2923: g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY));
2924: }
2925:
2926: TextBox tb = record.getLabel();
2927: tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT);
2928:
2929: }
2930:
2931:
2939: public boolean equals(Object obj) {
2940: if (obj == this) {
2941: return true;
2942: }
2943: if (!(obj instanceof PiePlot)) {
2944: return false;
2945: }
2946: if (!super.equals(obj)) {
2947: return false;
2948: }
2949: PiePlot that = (PiePlot) obj;
2950: if (this.pieIndex != that.pieIndex) {
2951: return false;
2952: }
2953: if (this.interiorGap != that.interiorGap) {
2954: return false;
2955: }
2956: if (this.circular != that.circular) {
2957: return false;
2958: }
2959: if (this.startAngle != that.startAngle) {
2960: return false;
2961: }
2962: if (this.direction != that.direction) {
2963: return false;
2964: }
2965: if (this.ignoreZeroValues != that.ignoreZeroValues) {
2966: return false;
2967: }
2968: if (this.ignoreNullValues != that.ignoreNullValues) {
2969: return false;
2970: }
2971: if (!PaintUtilities.equal(this.sectionPaint, that.sectionPaint)) {
2972: return false;
2973: }
2974: if (!ObjectUtilities.equal(this.sectionPaintMap,
2975: that.sectionPaintMap)) {
2976: return false;
2977: }
2978: if (!PaintUtilities.equal(this.baseSectionPaint,
2979: that.baseSectionPaint)) {
2980: return false;
2981: }
2982: if (this.sectionOutlinesVisible != that.sectionOutlinesVisible) {
2983: return false;
2984: }
2985: if (!PaintUtilities.equal(this.sectionOutlinePaint,
2986: that.sectionOutlinePaint)) {
2987: return false;
2988: }
2989: if (!ObjectUtilities.equal(this.sectionOutlinePaintMap,
2990: that.sectionOutlinePaintMap)) {
2991: return false;
2992: }
2993: if (!PaintUtilities.equal(
2994: this.baseSectionOutlinePaint, that.baseSectionOutlinePaint
2995: )) {
2996: return false;
2997: }
2998: if (!ObjectUtilities.equal(this.sectionOutlineStroke,
2999: that.sectionOutlineStroke)) {
3000: return false;
3001: }
3002: if (!ObjectUtilities.equal(this.sectionOutlineStrokeMap,
3003: that.sectionOutlineStrokeMap)) {
3004: return false;
3005: }
3006: if (!ObjectUtilities.equal(
3007: this.baseSectionOutlineStroke, that.baseSectionOutlineStroke
3008: )) {
3009: return false;
3010: }
3011: if (!PaintUtilities.equal(this.shadowPaint, that.shadowPaint)) {
3012: return false;
3013: }
3014: if (!(this.shadowXOffset == that.shadowXOffset)) {
3015: return false;
3016: }
3017: if (!(this.shadowYOffset == that.shadowYOffset)) {
3018: return false;
3019: }
3020: if (!ObjectUtilities.equal(this.explodePercentages,
3021: that.explodePercentages)) {
3022: return false;
3023: }
3024: if (!ObjectUtilities.equal(this.labelGenerator,
3025: that.labelGenerator)) {
3026: return false;
3027: }
3028: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
3029: return false;
3030: }
3031: if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
3032: return false;
3033: }
3034: if (!PaintUtilities.equal(this.labelBackgroundPaint,
3035: that.labelBackgroundPaint)) {
3036: return false;
3037: }
3038: if (!PaintUtilities.equal(this.labelOutlinePaint,
3039: that.labelOutlinePaint)) {
3040: return false;
3041: }
3042: if (!ObjectUtilities.equal(this.labelOutlineStroke,
3043: that.labelOutlineStroke)) {
3044: return false;
3045: }
3046: if (!PaintUtilities.equal(this.labelShadowPaint,
3047: that.labelShadowPaint)) {
3048: return false;
3049: }
3050: if (this.simpleLabels != that.simpleLabels) {
3051: return false;
3052: }
3053: if (!this.simpleLabelOffset.equals(that.simpleLabelOffset)) {
3054: return false;
3055: }
3056: if (!this.labelPadding.equals(that.labelPadding)) {
3057: return false;
3058: }
3059: if (!(this.maximumLabelWidth == that.maximumLabelWidth)) {
3060: return false;
3061: }
3062: if (!(this.labelGap == that.labelGap)) {
3063: return false;
3064: }
3065: if (!(this.labelLinkMargin == that.labelLinkMargin)) {
3066: return false;
3067: }
3068: if (this.labelLinksVisible != that.labelLinksVisible) {
3069: return false;
3070: }
3071: if (!PaintUtilities.equal(this.labelLinkPaint, that.labelLinkPaint)) {
3072: return false;
3073: }
3074: if (!ObjectUtilities.equal(this.labelLinkStroke,
3075: that.labelLinkStroke)) {
3076: return false;
3077: }
3078: if (!ObjectUtilities.equal(this.toolTipGenerator,
3079: that.toolTipGenerator)) {
3080: return false;
3081: }
3082: if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
3083: return false;
3084: }
3085: if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) {
3086: return false;
3087: }
3088: if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) {
3089: return false;
3090: }
3091: if (!ObjectUtilities.equal(this.legendLabelGenerator,
3092: that.legendLabelGenerator)) {
3093: return false;
3094: }
3095: if (!ObjectUtilities.equal(this.legendLabelToolTipGenerator,
3096: that.legendLabelToolTipGenerator)) {
3097: return false;
3098: }
3099: if (!ObjectUtilities.equal(this.legendLabelURLGenerator,
3100: that.legendLabelURLGenerator)) {
3101: return false;
3102: }
3103:
3104: return true;
3105: }
3106:
3107:
3115: public Object clone() throws CloneNotSupportedException {
3116: PiePlot clone = (PiePlot) super.clone();
3117: if (clone.dataset != null) {
3118: clone.dataset.addChangeListener(clone);
3119: }
3120: if (this.urlGenerator instanceof PublicCloneable) {
3121: clone.urlGenerator = (PieURLGenerator) ObjectUtilities.clone(
3122: this.urlGenerator);
3123: }
3124: clone.legendItemShape = ShapeUtilities.clone(this.legendItemShape);
3125: if (this.legendLabelGenerator != null) {
3126: clone.legendLabelGenerator = (PieSectionLabelGenerator)
3127: ObjectUtilities.clone(this.legendLabelGenerator);
3128: }
3129: if (this.legendLabelToolTipGenerator != null) {
3130: clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator)
3131: ObjectUtilities.clone(this.legendLabelToolTipGenerator);
3132: }
3133: if (this.legendLabelURLGenerator instanceof PublicCloneable) {
3134: clone.legendLabelURLGenerator = (PieURLGenerator)
3135: ObjectUtilities.clone(this.legendLabelURLGenerator);
3136: }
3137: return clone;
3138: }
3139:
3140:
3147: private void writeObject(ObjectOutputStream stream) throws IOException {
3148: stream.defaultWriteObject();
3149: SerialUtilities.writePaint(this.sectionPaint, stream);
3150: SerialUtilities.writePaint(this.baseSectionPaint, stream);
3151: SerialUtilities.writePaint(this.sectionOutlinePaint, stream);
3152: SerialUtilities.writePaint(this.baseSectionOutlinePaint, stream);
3153: SerialUtilities.writeStroke(this.sectionOutlineStroke, stream);
3154: SerialUtilities.writeStroke(this.baseSectionOutlineStroke, stream);
3155: SerialUtilities.writePaint(this.shadowPaint, stream);
3156: SerialUtilities.writePaint(this.labelPaint, stream);
3157: SerialUtilities.writePaint(this.labelBackgroundPaint, stream);
3158: SerialUtilities.writePaint(this.labelOutlinePaint, stream);
3159: SerialUtilities.writeStroke(this.labelOutlineStroke, stream);
3160: SerialUtilities.writePaint(this.labelShadowPaint, stream);
3161: SerialUtilities.writePaint(this.labelLinkPaint, stream);
3162: SerialUtilities.writeStroke(this.labelLinkStroke, stream);
3163: SerialUtilities.writeShape(this.legendItemShape, stream);
3164: }
3165:
3166:
3174: private void readObject(ObjectInputStream stream)
3175: throws IOException, ClassNotFoundException {
3176: stream.defaultReadObject();
3177: this.sectionPaint = SerialUtilities.readPaint(stream);
3178: this.baseSectionPaint = SerialUtilities.readPaint(stream);
3179: this.sectionOutlinePaint = SerialUtilities.readPaint(stream);
3180: this.baseSectionOutlinePaint = SerialUtilities.readPaint(stream);
3181: this.sectionOutlineStroke = SerialUtilities.readStroke(stream);
3182: this.baseSectionOutlineStroke = SerialUtilities.readStroke(stream);
3183: this.shadowPaint = SerialUtilities.readPaint(stream);
3184: this.labelPaint = SerialUtilities.readPaint(stream);
3185: this.labelBackgroundPaint = SerialUtilities.readPaint(stream);
3186: this.labelOutlinePaint = SerialUtilities.readPaint(stream);
3187: this.labelOutlineStroke = SerialUtilities.readStroke(stream);
3188: this.labelShadowPaint = SerialUtilities.readPaint(stream);
3189: this.labelLinkPaint = SerialUtilities.readPaint(stream);
3190: this.labelLinkStroke = SerialUtilities.readStroke(stream);
3191: this.legendItemShape = SerialUtilities.readShape(stream);
3192: }
3193:
3194:
3195:
3196:
3205: public Paint getSectionPaint(int section) {
3206: Comparable key = getSectionKey(section);
3207: return getSectionPaint(key);
3208: }
3209:
3210:
3219: public void setSectionPaint(int section, Paint paint) {
3220: Comparable key = getSectionKey(section);
3221: setSectionPaint(key, paint);
3222: }
3223:
3224:
3233: public Paint getSectionOutlinePaint(int section) {
3234: Comparable key = getSectionKey(section);
3235: return getSectionOutlinePaint(key);
3236: }
3237:
3238:
3248: public void setSectionOutlinePaint(int section, Paint paint) {
3249: Comparable key = getSectionKey(section);
3250: setSectionOutlinePaint(key, paint);
3251: }
3252:
3253:
3262: public Stroke getSectionOutlineStroke(int section) {
3263: Comparable key = getSectionKey(section);
3264: return getSectionOutlineStroke(key);
3265: }
3266:
3267:
3277: public void setSectionOutlineStroke(int section, Stroke stroke) {
3278: Comparable key = getSectionKey(section);
3279: setSectionOutlineStroke(key, stroke);
3280: }
3281:
3282:
3291: public double getExplodePercent(int section) {
3292: Comparable key = getSectionKey(section);
3293: return getExplodePercent(key);
3294: }
3295:
3296:
3305: public void setExplodePercent(int section, double percent) {
3306: Comparable key = getSectionKey(section);
3307: setExplodePercent(key, percent);
3308: }
3309:
3310: }