1:
158:
159: package ;
160:
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: import ;
180: import ;
181: import ;
182: import ;
183: import ;
184: import ;
185: import ;
186: import ;
187:
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: import ;
211: import ;
212: import ;
213: import ;
214: import ;
215: import ;
216: import ;
217: import ;
218:
219:
235: public class PiePlot extends Plot implements Cloneable, Serializable {
236:
237:
238: private static final long serialVersionUID = -795612466005590431L;
239:
240:
241: public static final double DEFAULT_INTERIOR_GAP = 0.08;
242:
243:
244: public static final double MAX_INTERIOR_GAP = 0.40;
245:
246:
247: public static final double DEFAULT_START_ANGLE = 90.0;
248:
249:
250: public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
251: Font.PLAIN, 10);
252:
253:
254: public static final Paint DEFAULT_LABEL_PAINT = Color.black;
255:
256:
257: public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255,
258: 255, 192);
259:
260:
261: public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
262:
263:
264: public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke(
265: 0.5f);
266:
267:
268: public static final Paint DEFAULT_LABEL_SHADOW_PAINT = new Color(151, 151,
269: 151, 128);
270:
271:
272: public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;
273:
274:
275: private PieDataset dataset;
276:
277:
278: private int pieIndex;
279:
280:
284: private double interiorGap;
285:
286:
287: private boolean circular;
288:
289:
290: private double startAngle;
291:
292:
293: private Rotation direction;
294:
295:
302: private transient Paint sectionPaint;
303:
304:
305: private PaintMap sectionPaintMap;
306:
307:
308: private transient Paint baseSectionPaint;
309:
310:
314: private boolean sectionOutlinesVisible;
315:
316:
323: private transient Paint sectionOutlinePaint;
324:
325:
326: private PaintMap sectionOutlinePaintMap;
327:
328:
329: private transient Paint baseSectionOutlinePaint;
330:
331:
338: private transient Stroke sectionOutlineStroke;
339:
340:
341: private StrokeMap sectionOutlineStrokeMap;
342:
343:
344: private transient Stroke baseSectionOutlineStroke;
345:
346:
347: private transient Paint shadowPaint = Color.gray;
348:
349:
350: private double shadowXOffset = 4.0f;
351:
352:
353: private double shadowYOffset = 4.0f;
354:
355:
356: private Map explodePercentages;
357:
358:
359: private PieSectionLabelGenerator labelGenerator;
360:
361:
362: private Font labelFont;
363:
364:
365: private transient Paint labelPaint;
366:
367:
371: private transient Paint labelBackgroundPaint;
372:
373:
377: private transient Paint labelOutlinePaint;
378:
379:
383: private transient Stroke labelOutlineStroke;
384:
385:
389: private transient Paint labelShadowPaint;
390:
391:
396: private boolean simpleLabels = true;
397:
398:
404: private RectangleInsets labelPadding;
405:
406:
411: private RectangleInsets simpleLabelOffset;
412:
413:
414: private double maximumLabelWidth = 0.14;
415:
416:
420: private double labelGap = 0.025;
421:
422:
423: private boolean labelLinksVisible;
424:
425:
430: private PieLabelLinkStyle labelLinkStyle = PieLabelLinkStyle.STANDARD;
431:
432:
433: private double labelLinkMargin = 0.025;
434:
435:
436: private transient Paint labelLinkPaint = Color.black;
437:
438:
439: private transient Stroke labelLinkStroke = new BasicStroke(0.5f);
440:
441:
446: private AbstractPieLabelDistributor labelDistributor;
447:
448:
449: private PieToolTipGenerator toolTipGenerator;
450:
451:
452: private PieURLGenerator urlGenerator;
453:
454:
455: private PieSectionLabelGenerator legendLabelGenerator;
456:
457:
458: private PieSectionLabelGenerator legendLabelToolTipGenerator;
459:
460:
465: private PieURLGenerator legendLabelURLGenerator;
466:
467:
470: private boolean ignoreNullValues;
471:
472:
475: private boolean ignoreZeroValues;
476:
477:
478: private transient Shape legendItemShape;
479:
480:
491: private double minimumArcAngleToDraw;
492:
493:
494: protected static ResourceBundle localizationResources =
495: ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
496:
497:
502: static final boolean DEBUG_DRAW_INTERIOR = false;
503:
504:
509: static final boolean DEBUG_DRAW_LINK_AREA = false;
510:
511:
515: static final boolean DEBUG_DRAW_PIE_AREA = false;
516:
517:
520: public PiePlot() {
521: this(null);
522: }
523:
524:
529: public PiePlot(PieDataset dataset) {
530: super();
531: this.dataset = dataset;
532: if (dataset != null) {
533: dataset.addChangeListener(this);
534: }
535: this.pieIndex = 0;
536:
537: this.interiorGap = DEFAULT_INTERIOR_GAP;
538: this.circular = true;
539: this.startAngle = DEFAULT_START_ANGLE;
540: this.direction = Rotation.CLOCKWISE;
541: this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;
542:
543: this.sectionPaint = null;
544: this.sectionPaintMap = new PaintMap();
545: this.baseSectionPaint = Color.gray;
546:
547: this.sectionOutlinesVisible = true;
548: this.sectionOutlinePaint = null;
549: this.sectionOutlinePaintMap = new PaintMap();
550: this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;
551:
552: this.sectionOutlineStroke = null;
553: this.sectionOutlineStrokeMap = new StrokeMap();
554: this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;
555:
556: this.explodePercentages = new TreeMap();
557:
558: this.labelGenerator = new StandardPieSectionLabelGenerator();
559: this.labelFont = DEFAULT_LABEL_FONT;
560: this.labelPaint = DEFAULT_LABEL_PAINT;
561: this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
562: this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
563: this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
564: this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
565: this.labelLinksVisible = true;
566: this.labelDistributor = new PieLabelDistributor(0);
567:
568: this.simpleLabels = false;
569: this.simpleLabelOffset = new RectangleInsets(UnitType.RELATIVE, 0.18,
570: 0.18, 0.18, 0.18);
571: this.labelPadding = new RectangleInsets(2, 2, 2, 2);
572:
573: this.toolTipGenerator = null;
574: this.urlGenerator = null;
575: this.legendLabelGenerator = new StandardPieSectionLabelGenerator();
576: this.legendLabelToolTipGenerator = null;
577: this.legendLabelURLGenerator = null;
578: this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;
579:
580: this.ignoreNullValues = false;
581: this.ignoreZeroValues = false;
582: }
583:
584:
591: public PieDataset getDataset() {
592: return this.dataset;
593: }
594:
595:
602: public void setDataset(PieDataset dataset) {
603:
604:
605: PieDataset existing = this.dataset;
606: if (existing != null) {
607: existing.removeChangeListener(this);
608: }
609:
610:
611: this.dataset = dataset;
612: if (dataset != null) {
613: setDatasetGroup(dataset.getGroup());
614: dataset.addChangeListener(this);
615: }
616:
617:
618: DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
619: datasetChanged(event);
620: }
621:
622:
630: public int getPieIndex() {
631: return this.pieIndex;
632: }
633:
634:
642: public void setPieIndex(int index) {
643: this.pieIndex = index;
644: }
645:
646:
654: public double getStartAngle() {
655: return this.startAngle;
656: }
657:
658:
668: public void setStartAngle(double angle) {
669: this.startAngle = angle;
670: fireChangeEvent();
671: }
672:
673:
681: public Rotation getDirection() {
682: return this.direction;
683: }
684:
685:
693: public void setDirection(Rotation direction) {
694: if (direction == null) {
695: throw new IllegalArgumentException("Null 'direction' argument.");
696: }
697: this.direction = direction;
698: fireChangeEvent();
699:
700: }
701:
702:
710: public double getInteriorGap() {
711: return this.interiorGap;
712: }
713:
714:
724: public void setInteriorGap(double percent) {
725:
726: if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
727: throw new IllegalArgumentException(
728: "Invalid 'percent' (" + percent + ") argument.");
729: }
730:
731: if (this.interiorGap != percent) {
732: this.interiorGap = percent;
733: fireChangeEvent();
734: }
735:
736: }
737:
738:
746: public boolean isCircular() {
747: return this.circular;
748: }
749:
750:
758: public void setCircular(boolean flag) {
759: setCircular(flag, true);
760: }
761:
762:
771: public void setCircular(boolean circular, boolean notify) {
772: this.circular = circular;
773: if (notify) {
774: fireChangeEvent();
775: }
776: }
777:
778:
786: public boolean getIgnoreNullValues() {
787: return this.ignoreNullValues;
788: }
789:
790:
801: public void setIgnoreNullValues(boolean flag) {
802: this.ignoreNullValues = flag;
803: fireChangeEvent();
804: }
805:
806:
814: public boolean getIgnoreZeroValues() {
815: return this.ignoreZeroValues;
816: }
817:
818:
829: public void setIgnoreZeroValues(boolean flag) {
830: this.ignoreZeroValues = flag;
831: fireChangeEvent();
832: }
833:
834:
835:
836:
848: protected Paint lookupSectionPaint(Comparable key) {
849: return lookupSectionPaint(key, false);
850: }
851:
852:
875: protected Paint lookupSectionPaint(Comparable key, boolean autoPopulate) {
876:
877:
878: Paint result = getSectionPaint();
879: if (result != null) {
880: return result;
881: }
882:
883:
884: result = this.sectionPaintMap.getPaint(key);
885: if (result != null) {
886: return result;
887: }
888:
889:
890: if (autoPopulate) {
891: DrawingSupplier ds = getDrawingSupplier();
892: if (ds != null) {
893: result = ds.getNextPaint();
894: this.sectionPaintMap.put(key, result);
895: }
896: else {
897: result = this.baseSectionPaint;
898: }
899: }
900: else {
901: result = this.baseSectionPaint;
902: }
903: return result;
904: }
905:
906:
916: public Paint getSectionPaint() {
917: return this.sectionPaint;
918: }
919:
920:
932: public void setSectionPaint(Paint paint) {
933: this.sectionPaint = paint;
934: fireChangeEvent();
935: }
936:
937:
951: protected Comparable getSectionKey(int section) {
952: Comparable key = null;
953: if (this.dataset != null) {
954: if (section >= 0 && section < this.dataset.getItemCount()) {
955: key = this.dataset.getKey(section);
956: }
957: }
958: if (key == null) {
959: key = new Integer(section);
960: }
961: return key;
962: }
963:
964:
980: public Paint getSectionPaint(Comparable key) {
981:
982: return this.sectionPaintMap.getPaint(key);
983: }
984:
985:
999: public void setSectionPaint(Comparable key, Paint paint) {
1000:
1001: this.sectionPaintMap.put(key, paint);
1002: fireChangeEvent();
1003: }
1004:
1005:
1013: public Paint getBaseSectionPaint() {
1014: return this.baseSectionPaint;
1015: }
1016:
1017:
1025: public void setBaseSectionPaint(Paint paint) {
1026: if (paint == null) {
1027: throw new IllegalArgumentException("Null 'paint' argument.");
1028: }
1029: this.baseSectionPaint = paint;
1030: fireChangeEvent();
1031: }
1032:
1033:
1034:
1035:
1044: public boolean getSectionOutlinesVisible() {
1045: return this.sectionOutlinesVisible;
1046: }
1047:
1048:
1057: public void setSectionOutlinesVisible(boolean visible) {
1058: this.sectionOutlinesVisible = visible;
1059: fireChangeEvent();
1060: }
1061:
1062:
1074: protected Paint lookupSectionOutlinePaint(Comparable key) {
1075: return lookupSectionOutlinePaint(key, false);
1076: }
1077:
1078:
1101: protected Paint lookupSectionOutlinePaint(Comparable key,
1102: boolean autoPopulate) {
1103:
1104:
1105: Paint result = getSectionOutlinePaint();
1106: if (result != null) {
1107: return result;
1108: }
1109:
1110:
1111: result = this.sectionOutlinePaintMap.getPaint(key);
1112: if (result != null) {
1113: return result;
1114: }
1115:
1116:
1117: if (autoPopulate) {
1118: DrawingSupplier ds = getDrawingSupplier();
1119: if (ds != null) {
1120: result = ds.getNextOutlinePaint();
1121: this.sectionOutlinePaintMap.put(key, result);
1122: }
1123: else {
1124: result = this.baseSectionOutlinePaint;
1125: }
1126: }
1127: else {
1128: result = this.baseSectionOutlinePaint;
1129: }
1130: return result;
1131: }
1132:
1133:
1144: public Paint getSectionOutlinePaint() {
1145: return this.sectionOutlinePaint;
1146: }
1147:
1148:
1161: public void setSectionOutlinePaint(Paint paint) {
1162: this.sectionOutlinePaint = paint;
1163: fireChangeEvent();
1164: }
1165:
1166:
1182: public Paint getSectionOutlinePaint(Comparable key) {
1183:
1184: return this.sectionOutlinePaintMap.getPaint(key);
1185: }
1186:
1187:
1201: public void setSectionOutlinePaint(Comparable key, Paint paint) {
1202:
1203: this.sectionOutlinePaintMap.put(key, paint);
1204: fireChangeEvent();
1205: }
1206:
1207:
1215: public Paint getBaseSectionOutlinePaint() {
1216: return this.baseSectionOutlinePaint;
1217: }
1218:
1219:
1226: public void setBaseSectionOutlinePaint(Paint paint) {
1227: if (paint == null) {
1228: throw new IllegalArgumentException("Null 'paint' argument.");
1229: }
1230: this.baseSectionOutlinePaint = paint;
1231: fireChangeEvent();
1232: }
1233:
1234:
1235:
1236:
1248: protected Stroke lookupSectionOutlineStroke(Comparable key) {
1249: return lookupSectionOutlineStroke(key, false);
1250: }
1251:
1252:
1275: protected Stroke lookupSectionOutlineStroke(Comparable key,
1276: boolean autoPopulate) {
1277:
1278:
1279: Stroke result = getSectionOutlineStroke();
1280: if (result != null) {
1281: return result;
1282: }
1283:
1284:
1285: result = this.sectionOutlineStrokeMap.getStroke(key);
1286: if (result != null) {
1287: return result;
1288: }
1289:
1290:
1291: if (autoPopulate) {
1292: DrawingSupplier ds = getDrawingSupplier();
1293: if (ds != null) {
1294: result = ds.getNextOutlineStroke();
1295: this.sectionOutlineStrokeMap.put(key, result);
1296: }
1297: else {
1298: result = this.baseSectionOutlineStroke;
1299: }
1300: }
1301: else {
1302: result = this.baseSectionOutlineStroke;
1303: }
1304: return result;
1305: }
1306:
1307:
1318: public Stroke getSectionOutlineStroke() {
1319: return this.sectionOutlineStroke;
1320: }
1321:
1322:
1335: public void setSectionOutlineStroke(Stroke stroke) {
1336: this.sectionOutlineStroke = stroke;
1337: fireChangeEvent();
1338: }
1339:
1340:
1356: public Stroke getSectionOutlineStroke(Comparable key) {
1357:
1358: return this.sectionOutlineStrokeMap.getStroke(key);
1359: }
1360:
1361:
1375: public void setSectionOutlineStroke(Comparable key, Stroke stroke) {
1376:
1377: this.sectionOutlineStrokeMap.put(key, stroke);
1378: fireChangeEvent();
1379: }
1380:
1381:
1389: public Stroke getBaseSectionOutlineStroke() {
1390: return this.baseSectionOutlineStroke;
1391: }
1392:
1393:
1400: public void setBaseSectionOutlineStroke(Stroke stroke) {
1401: if (stroke == null) {
1402: throw new IllegalArgumentException("Null 'stroke' argument.");
1403: }
1404: this.baseSectionOutlineStroke = stroke;
1405: fireChangeEvent();
1406: }
1407:
1408:
1415: public Paint getShadowPaint() {
1416: return this.shadowPaint;
1417: }
1418:
1419:
1427: public void setShadowPaint(Paint paint) {
1428: this.shadowPaint = paint;
1429: fireChangeEvent();
1430: }
1431:
1432:
1439: public double getShadowXOffset() {
1440: return this.shadowXOffset;
1441: }
1442:
1443:
1451: public void setShadowXOffset(double offset) {
1452: this.shadowXOffset = offset;
1453: fireChangeEvent();
1454: }
1455:
1456:
1463: public double getShadowYOffset() {
1464: return this.shadowYOffset;
1465: }
1466:
1467:
1475: public void setShadowYOffset(double offset) {
1476: this.shadowYOffset = offset;
1477: fireChangeEvent();
1478: }
1479:
1480:
1496: public double getExplodePercent(Comparable key) {
1497: double result = 0.0;
1498: if (this.explodePercentages != null) {
1499: Number percent = (Number) this.explodePercentages.get(key);
1500: if (percent != null) {
1501: result = percent.doubleValue();
1502: }
1503: }
1504: return result;
1505: }
1506:
1507:
1518: public void setExplodePercent(Comparable key, double percent) {
1519: if (key == null) {
1520: throw new IllegalArgumentException("Null 'key' argument.");
1521: }
1522: if (this.explodePercentages == null) {
1523: this.explodePercentages = new TreeMap();
1524: }
1525: this.explodePercentages.put(key, new Double(percent));
1526: fireChangeEvent();
1527: }
1528:
1529:
1534: public double getMaximumExplodePercent() {
1535: if (this.dataset == null) {
1536: return 0.0;
1537: }
1538: double result = 0.0;
1539: Iterator iterator = this.dataset.getKeys().iterator();
1540: while (iterator.hasNext()) {
1541: Comparable key = (Comparable) iterator.next();
1542: Number explode = (Number) this.explodePercentages.get(key);
1543: if (explode != null) {
1544: result = Math.max(result, explode.doubleValue());
1545: }
1546: }
1547: return result;
1548: }
1549:
1550:
1557: public PieSectionLabelGenerator getLabelGenerator() {
1558: return this.labelGenerator;
1559: }
1560:
1561:
1569: public void setLabelGenerator(PieSectionLabelGenerator generator) {
1570: this.labelGenerator = generator;
1571: fireChangeEvent();
1572: }
1573:
1574:
1582: public double getLabelGap() {
1583: return this.labelGap;
1584: }
1585:
1586:
1595: public void setLabelGap(double gap) {
1596: this.labelGap = gap;
1597: fireChangeEvent();
1598: }
1599:
1600:
1607: public double getMaximumLabelWidth() {
1608: return this.maximumLabelWidth;
1609: }
1610:
1611:
1619: public void setMaximumLabelWidth(double width) {
1620: this.maximumLabelWidth = width;
1621: fireChangeEvent();
1622: }
1623:
1624:
1632: public boolean getLabelLinksVisible() {
1633: return this.labelLinksVisible;
1634: }
1635:
1636:
1647: public void setLabelLinksVisible(boolean visible) {
1648: this.labelLinksVisible = visible;
1649: fireChangeEvent();
1650: }
1651:
1652:
1661: public PieLabelLinkStyle getLabelLinkStyle() {
1662: return this.labelLinkStyle;
1663: }
1664:
1665:
1675: public void setLabelLinkStyle(PieLabelLinkStyle style) {
1676: if (style == null) {
1677: throw new IllegalArgumentException("Null 'style' argument.");
1678: }
1679: this.labelLinkStyle = style;
1680: fireChangeEvent();
1681: }
1682:
1683:
1691: public double getLabelLinkMargin() {
1692: return this.labelLinkMargin;
1693: }
1694:
1695:
1703: public void setLabelLinkMargin(double margin) {
1704: this.labelLinkMargin = margin;
1705: fireChangeEvent();
1706: }
1707:
1708:
1716: public Paint getLabelLinkPaint() {
1717: return this.labelLinkPaint;
1718: }
1719:
1720:
1729: public void setLabelLinkPaint(Paint paint) {
1730: if (paint == null) {
1731: throw new IllegalArgumentException("Null 'paint' argument.");
1732: }
1733: this.labelLinkPaint = paint;
1734: fireChangeEvent();
1735: }
1736:
1737:
1744: public Stroke getLabelLinkStroke() {
1745: return this.labelLinkStroke;
1746: }
1747:
1748:
1756: public void setLabelLinkStroke(Stroke stroke) {
1757: if (stroke == null) {
1758: throw new IllegalArgumentException("Null 'stroke' argument.");
1759: }
1760: this.labelLinkStroke = stroke;
1761: fireChangeEvent();
1762: }
1763:
1764:
1771: public Font getLabelFont() {
1772: return this.labelFont;
1773: }
1774:
1775:
1783: public void setLabelFont(Font font) {
1784: if (font == null) {
1785: throw new IllegalArgumentException("Null 'font' argument.");
1786: }
1787: this.labelFont = font;
1788: fireChangeEvent();
1789: }
1790:
1791:
1798: public Paint getLabelPaint() {
1799: return this.labelPaint;
1800: }
1801:
1802:
1810: public void setLabelPaint(Paint paint) {
1811: if (paint == null) {
1812: throw new IllegalArgumentException("Null 'paint' argument.");
1813: }
1814: this.labelPaint = paint;
1815: fireChangeEvent();
1816: }
1817:
1818:
1825: public Paint getLabelBackgroundPaint() {
1826: return this.labelBackgroundPaint;
1827: }
1828:
1829:
1837: public void setLabelBackgroundPaint(Paint paint) {
1838: this.labelBackgroundPaint = paint;
1839: fireChangeEvent();
1840: }
1841:
1842:
1849: public Paint getLabelOutlinePaint() {
1850: return this.labelOutlinePaint;
1851: }
1852:
1853:
1861: public void setLabelOutlinePaint(Paint paint) {
1862: this.labelOutlinePaint = paint;
1863: fireChangeEvent();
1864: }
1865:
1866:
1873: public Stroke getLabelOutlineStroke() {
1874: return this.labelOutlineStroke;
1875: }
1876:
1877:
1885: public void setLabelOutlineStroke(Stroke stroke) {
1886: this.labelOutlineStroke = stroke;
1887: fireChangeEvent();
1888: }
1889:
1890:
1897: public Paint getLabelShadowPaint() {
1898: return this.labelShadowPaint;
1899: }
1900:
1901:
1909: public void setLabelShadowPaint(Paint paint) {
1910: this.labelShadowPaint = paint;
1911: fireChangeEvent();
1912: }
1913:
1914:
1923: public RectangleInsets getLabelPadding() {
1924: return this.labelPadding;
1925: }
1926:
1927:
1937: public void setLabelPadding(RectangleInsets padding) {
1938: if (padding == null) {
1939: throw new IllegalArgumentException("Null 'padding' argument.");
1940: }
1941: this.labelPadding = padding;
1942: fireChangeEvent();
1943: }
1944:
1945:
1953: public boolean getSimpleLabels() {
1954: return this.simpleLabels;
1955: }
1956:
1957:
1966: public void setSimpleLabels(boolean simple) {
1967: this.simpleLabels = simple;
1968: fireChangeEvent();
1969: }
1970:
1971:
1980: public RectangleInsets getSimpleLabelOffset() {
1981: return this.simpleLabelOffset;
1982: }
1983:
1984:
1994: public void setSimpleLabelOffset(RectangleInsets offset) {
1995: if (offset == null) {
1996: throw new IllegalArgumentException("Null 'offset' argument.");
1997: }
1998: this.simpleLabelOffset = offset;
1999: fireChangeEvent();
2000: }
2001:
2002:
2010: public AbstractPieLabelDistributor getLabelDistributor() {
2011: return this.labelDistributor;
2012: }
2013:
2014:
2022: public void setLabelDistributor(AbstractPieLabelDistributor distributor) {
2023: if (distributor == null) {
2024: throw new IllegalArgumentException("Null 'distributor' argument.");
2025: }
2026: this.labelDistributor = distributor;
2027: fireChangeEvent();
2028: }
2029:
2030:
2039: public PieToolTipGenerator getToolTipGenerator() {
2040: return this.toolTipGenerator;
2041: }
2042:
2043:
2052: public void setToolTipGenerator(PieToolTipGenerator generator) {
2053: this.toolTipGenerator = generator;
2054: fireChangeEvent();
2055: }
2056:
2057:
2064: public PieURLGenerator getURLGenerator() {
2065: return this.urlGenerator;
2066: }
2067:
2068:
2076: public void setURLGenerator(PieURLGenerator generator) {
2077: this.urlGenerator = generator;
2078: fireChangeEvent();
2079: }
2080:
2081:
2089: public double getMinimumArcAngleToDraw() {
2090: return this.minimumArcAngleToDraw;
2091: }
2092:
2093:
2111: public void setMinimumArcAngleToDraw(double angle) {
2112: this.minimumArcAngleToDraw = angle;
2113: }
2114:
2115:
2122: public Shape getLegendItemShape() {
2123: return this.legendItemShape;
2124: }
2125:
2126:
2134: public void setLegendItemShape(Shape shape) {
2135: if (shape == null) {
2136: throw new IllegalArgumentException("Null 'shape' argument.");
2137: }
2138: this.legendItemShape = shape;
2139: fireChangeEvent();
2140: }
2141:
2142:
2149: public PieSectionLabelGenerator getLegendLabelGenerator() {
2150: return this.legendLabelGenerator;
2151: }
2152:
2153:
2161: public void setLegendLabelGenerator(PieSectionLabelGenerator generator) {
2162: if (generator == null) {
2163: throw new IllegalArgumentException("Null 'generator' argument.");
2164: }
2165: this.legendLabelGenerator = generator;
2166: fireChangeEvent();
2167: }
2168:
2169:
2176: public PieSectionLabelGenerator getLegendLabelToolTipGenerator() {
2177: return this.legendLabelToolTipGenerator;
2178: }
2179:
2180:
2188: public void setLegendLabelToolTipGenerator(
2189: PieSectionLabelGenerator generator) {
2190: this.legendLabelToolTipGenerator = generator;
2191: fireChangeEvent();
2192: }
2193:
2194:
2203: public PieURLGenerator getLegendLabelURLGenerator() {
2204: return this.legendLabelURLGenerator;
2205: }
2206:
2207:
2217: public void setLegendLabelURLGenerator(PieURLGenerator generator) {
2218: this.legendLabelURLGenerator = generator;
2219: fireChangeEvent();
2220: }
2221:
2222:
2237: public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea,
2238: PiePlot plot, Integer index, PlotRenderingInfo info) {
2239:
2240: PiePlotState state = new PiePlotState(info);
2241: state.setPassesRequired(2);
2242: if (this.dataset != null) {
2243: state.setTotal(DatasetUtilities.calculatePieDatasetTotal(
2244: plot.getDataset()));
2245: }
2246: state.setLatestAngle(plot.getStartAngle());
2247: return state;
2248:
2249: }
2250:
2251:
2262: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2263: PlotState parentState, PlotRenderingInfo info) {
2264:
2265:
2266: RectangleInsets insets = getInsets();
2267: insets.trim(area);
2268:
2269: if (info != null) {
2270: info.setPlotArea(area);
2271: info.setDataArea(area);
2272: }
2273:
2274: drawBackground(g2, area);
2275: drawOutline(g2, area);
2276:
2277: Shape savedClip = g2.getClip();
2278: g2.clip(area);
2279:
2280: Composite originalComposite = g2.getComposite();
2281: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2282: getForegroundAlpha()));
2283:
2284: if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
2285: drawPie(g2, area, info);
2286: }
2287: else {
2288: drawNoDataMessage(g2, area);
2289: }
2290:
2291: g2.setClip(savedClip);
2292: g2.setComposite(originalComposite);
2293:
2294: drawOutline(g2, area);
2295:
2296: }
2297:
2298:
2305: protected void drawPie(Graphics2D g2, Rectangle2D plotArea,
2306: PlotRenderingInfo info) {
2307:
2308: PiePlotState state = initialise(g2, plotArea, this, null, info);
2309:
2310:
2311: double labelReserve = 0.0;
2312: if (this.labelGenerator != null && !this.simpleLabels) {
2313: labelReserve = this.labelGap + this.maximumLabelWidth;
2314: }
2315: double gapHorizontal = plotArea.getWidth() * (this.interiorGap
2316: + labelReserve) * 2.0;
2317: double gapVertical = plotArea.getHeight() * this.interiorGap * 2.0;
2318:
2319:
2320: if (DEBUG_DRAW_INTERIOR) {
2321: double hGap = plotArea.getWidth() * this.interiorGap;
2322: double vGap = plotArea.getHeight() * this.interiorGap;
2323:
2324: double igx1 = plotArea.getX() + hGap;
2325: double igx2 = plotArea.getMaxX() - hGap;
2326: double igy1 = plotArea.getY() + vGap;
2327: double igy2 = plotArea.getMaxY() - vGap;
2328: g2.setPaint(Color.gray);
2329: g2.draw(new Rectangle2D.Double(igx1, igy1, igx2 - igx1,
2330: igy2 - igy1));
2331: }
2332:
2333: double linkX = plotArea.getX() + gapHorizontal / 2;
2334: double linkY = plotArea.getY() + gapVertical / 2;
2335: double linkW = plotArea.getWidth() - gapHorizontal;
2336: double linkH = plotArea.getHeight() - gapVertical;
2337:
2338:
2339: if (this.circular) {
2340: double min = Math.min(linkW, linkH) / 2;
2341: linkX = (linkX + linkX + linkW) / 2 - min;
2342: linkY = (linkY + linkY + linkH) / 2 - min;
2343: linkW = 2 * min;
2344: linkH = 2 * min;
2345: }
2346:
2347:
2348:
2349: Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW,
2350: linkH);
2351: state.setLinkArea(linkArea);
2352:
2353: if (DEBUG_DRAW_LINK_AREA) {
2354: g2.setPaint(Color.blue);
2355: g2.draw(linkArea);
2356: g2.setPaint(Color.yellow);
2357: g2.draw(new Ellipse2D.Double(linkArea.getX(), linkArea.getY(),
2358: linkArea.getWidth(), linkArea.getHeight()));
2359: }
2360:
2361:
2362:
2363:
2364: double lm = 0.0;
2365: if (!this.simpleLabels) {
2366: lm = this.labelLinkMargin;
2367: }
2368: double hh = linkArea.getWidth() * lm * 2.0;
2369: double vv = linkArea.getHeight() * lm * 2.0;
2370: Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0,
2371: linkY + vv / 2.0, linkW - hh, linkH - vv);
2372:
2373: state.setExplodedPieArea(explodeArea);
2374:
2375:
2376:
2377:
2378: double maximumExplodePercent = getMaximumExplodePercent();
2379: double percent = maximumExplodePercent / (1.0 + maximumExplodePercent);
2380:
2381: double h1 = explodeArea.getWidth() * percent;
2382: double v1 = explodeArea.getHeight() * percent;
2383: Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX()
2384: + h1 / 2.0, explodeArea.getY() + v1 / 2.0,
2385: explodeArea.getWidth() - h1, explodeArea.getHeight() - v1);
2386:
2387: if (DEBUG_DRAW_PIE_AREA) {
2388: g2.setPaint(Color.green);
2389: g2.draw(pieArea);
2390: }
2391: state.setPieArea(pieArea);
2392: state.setPieCenterX(pieArea.getCenterX());
2393: state.setPieCenterY(pieArea.getCenterY());
2394: state.setPieWRadius(pieArea.getWidth() / 2.0);
2395: state.setPieHRadius(pieArea.getHeight() / 2.0);
2396:
2397:
2398: if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) {
2399:
2400: List keys = this.dataset.getKeys();
2401: double totalValue = DatasetUtilities.calculatePieDatasetTotal(
2402: this.dataset);
2403:
2404: int passesRequired = state.getPassesRequired();
2405: for (int pass = 0; pass < passesRequired; pass++) {
2406: double runningTotal = 0.0;
2407: for (int section = 0; section < keys.size(); section++) {
2408: Number n = this.dataset.getValue(section);
2409: if (n != null) {
2410: double value = n.doubleValue();
2411: if (value > 0.0) {
2412: runningTotal += value;
2413: drawItem(g2, section, explodeArea, state, pass);
2414: }
2415: }
2416: }
2417: }
2418: if (this.simpleLabels) {
2419: drawSimpleLabels(g2, keys, totalValue, plotArea, linkArea,
2420: state);
2421: }
2422: else {
2423: drawLabels(g2, keys, totalValue, plotArea, linkArea, state);
2424: }
2425:
2426: }
2427: else {
2428: drawNoDataMessage(g2, plotArea);
2429: }
2430: }
2431:
2432:
2441: protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea,
2442: PiePlotState state, int currentPass) {
2443:
2444: Number n = this.dataset.getValue(section);
2445: if (n == null) {
2446: return;
2447: }
2448: double value = n.doubleValue();
2449: double angle1 = 0.0;
2450: double angle2 = 0.0;
2451:
2452: if (this.direction == Rotation.CLOCKWISE) {
2453: angle1 = state.getLatestAngle();
2454: angle2 = angle1 - value / state.getTotal() * 360.0;
2455: }
2456: else if (this.direction == Rotation.ANTICLOCKWISE) {
2457: angle1 = state.getLatestAngle();
2458: angle2 = angle1 + value / state.getTotal() * 360.0;
2459: }
2460: else {
2461: throw new IllegalStateException("Rotation type not recognised.");
2462: }
2463:
2464: double angle = (angle2 - angle1);
2465: if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
2466: double ep = 0.0;
2467: double mep = getMaximumExplodePercent();
2468: if (mep > 0.0) {
2469: ep = getExplodePercent(section) / mep;
2470: }
2471: Rectangle2D arcBounds = getArcBounds(state.getPieArea(),
2472: state.getExplodedPieArea(), angle1, angle, ep);
2473: Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle,
2474: Arc2D.PIE);
2475:
2476: if (currentPass == 0) {
2477: if (this.shadowPaint != null) {
2478: Shape shadowArc = ShapeUtilities.createTranslatedShape(
2479: arc, (float) this.shadowXOffset,
2480: (float) this.shadowYOffset);
2481: g2.setPaint(this.shadowPaint);
2482: g2.fill(shadowArc);
2483: }
2484: }
2485: else if (currentPass == 1) {
2486: Comparable key = getSectionKey(section);
2487: Paint paint = lookupSectionPaint(key, true);
2488: g2.setPaint(paint);
2489: g2.fill(arc);
2490:
2491: Paint outlinePaint = lookupSectionOutlinePaint(key);
2492: Stroke outlineStroke = lookupSectionOutlineStroke(key);
2493: if (this.sectionOutlinesVisible) {
2494: g2.setPaint(outlinePaint);
2495: g2.setStroke(outlineStroke);
2496: g2.draw(arc);
2497: }
2498:
2499:
2500:
2501: if (state.getInfo() != null) {
2502: EntityCollection entities = state.getEntityCollection();
2503: if (entities != null) {
2504: String tip = null;
2505: if (this.toolTipGenerator != null) {
2506: tip = this.toolTipGenerator.generateToolTip(
2507: this.dataset, key);
2508: }
2509: String url = null;
2510: if (this.urlGenerator != null) {
2511: url = this.urlGenerator.generateURL(this.dataset,
2512: key, this.pieIndex);
2513: }
2514: PieSectionEntity entity = new PieSectionEntity(
2515: arc, this.dataset, this.pieIndex, section, key,
2516: tip, url);
2517: entities.add(entity);
2518: }
2519: }
2520: }
2521: }
2522: state.setLatestAngle(angle2);
2523: }
2524:
2525:
2537: protected void drawSimpleLabels(Graphics2D g2, List keys,
2538: double totalValue, Rectangle2D plotArea, Rectangle2D pieArea,
2539: PiePlotState state) {
2540:
2541: Composite originalComposite = g2.getComposite();
2542: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2543: 1.0f));
2544:
2545: RectangleInsets labelInsets = new RectangleInsets(UnitType.RELATIVE,
2546: 0.18, 0.18, 0.18, 0.18);
2547: Rectangle2D labelsArea = labelInsets.createInsetRectangle(pieArea);
2548: double runningTotal = 0.0;
2549: Iterator iterator = keys.iterator();
2550: while (iterator.hasNext()) {
2551: Comparable key = (Comparable) iterator.next();
2552: boolean include = true;
2553: double v = 0.0;
2554: Number n = getDataset().getValue(key);
2555: if (n == null) {
2556: include = !getIgnoreNullValues();
2557: }
2558: else {
2559: v = n.doubleValue();
2560: include = getIgnoreZeroValues() ? v > 0.0 : v >= 0.0;
2561: }
2562:
2563: if (include) {
2564: runningTotal = runningTotal + v;
2565:
2566:
2567: double mid = getStartAngle() + (getDirection().getFactor()
2568: * ((runningTotal - v / 2.0) * 360) / totalValue);
2569:
2570: Arc2D arc = new Arc2D.Double(labelsArea, getStartAngle(),
2571: mid - getStartAngle(), Arc2D.OPEN);
2572: int x = (int) arc.getEndPoint().getX();
2573: int y = (int) arc.getEndPoint().getY();
2574:
2575: PieSectionLabelGenerator labelGenerator = getLabelGenerator();
2576: if (labelGenerator == null) {
2577: continue;
2578: }
2579: String label = labelGenerator.generateSectionLabel(
2580: this.dataset, key);
2581: if (label == null) {
2582: continue;
2583: }
2584: g2.setFont(this.labelFont);
2585: FontMetrics fm = g2.getFontMetrics();
2586: Rectangle2D bounds = TextUtilities.getTextBounds(label, g2, fm);
2587: Rectangle2D out = this.labelPadding.createOutsetRectangle(
2588: bounds);
2589: Shape bg = ShapeUtilities.createTranslatedShape(out,
2590: x - bounds.getCenterX(), y - bounds.getCenterY());
2591: if (this.labelShadowPaint != null) {
2592: Shape shadow = ShapeUtilities.createTranslatedShape(bg,
2593: this.shadowXOffset, this.shadowYOffset);
2594: g2.setPaint(this.labelShadowPaint);
2595: g2.fill(shadow);
2596: }
2597: if (this.labelBackgroundPaint != null) {
2598: g2.setPaint(this.labelBackgroundPaint);
2599: g2.fill(bg);
2600: }
2601: if (this.labelOutlinePaint != null
2602: && this.labelOutlineStroke != null) {
2603: g2.setPaint(this.labelOutlinePaint);
2604: g2.setStroke(this.labelOutlineStroke);
2605: g2.draw(bg);
2606: }
2607:
2608: g2.setPaint(this.labelPaint);
2609: g2.setFont(this.labelFont);
2610: TextUtilities.drawAlignedString(getLabelGenerator()
2611: .generateSectionLabel(getDataset(), key), g2, x, y,
2612: TextAnchor.CENTER);
2613:
2614: }
2615: }
2616:
2617: g2.setComposite(originalComposite);
2618:
2619: }
2620:
2621:
2631: protected void drawLabels(Graphics2D g2, List keys, double totalValue,
2632: Rectangle2D plotArea, Rectangle2D linkArea,
2633: PiePlotState state) {
2634:
2635: Composite originalComposite = g2.getComposite();
2636: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2637: 1.0f));
2638:
2639:
2640: DefaultKeyedValues leftKeys = new DefaultKeyedValues();
2641: DefaultKeyedValues rightKeys = new DefaultKeyedValues();
2642:
2643: double runningTotal = 0.0;
2644: Iterator iterator = keys.iterator();
2645: while (iterator.hasNext()) {
2646: Comparable key = (Comparable) iterator.next();
2647: boolean include = true;
2648: double v = 0.0;
2649: Number n = this.dataset.getValue(key);
2650: if (n == null) {
2651: include = !this.ignoreNullValues;
2652: }
2653: else {
2654: v = n.doubleValue();
2655: include = this.ignoreZeroValues ? v > 0.0 : v >= 0.0;
2656: }
2657:
2658: if (include) {
2659: runningTotal = runningTotal + v;
2660:
2661:
2662: double mid = this.startAngle + (this.direction.getFactor()
2663: * ((runningTotal - v / 2.0) * 360) / totalValue);
2664: if (Math.cos(Math.toRadians(mid)) < 0.0) {
2665: leftKeys.addValue(key, new Double(mid));
2666: }
2667: else {
2668: rightKeys.addValue(key, new Double(mid));
2669: }
2670: }
2671: }
2672:
2673: g2.setFont(getLabelFont());
2674:
2675:
2676:
2677: double marginX = plotArea.getX() + this.interiorGap
2678: * plotArea.getWidth();
2679: double gap = plotArea.getWidth() * this.labelGap;
2680: double ww = linkArea.getX() - gap - marginX;
2681: float labelWidth = (float) this.labelPadding.trimWidth(ww);
2682:
2683:
2684: if (this.labelGenerator != null) {
2685: drawLeftLabels(leftKeys, g2, plotArea, linkArea, labelWidth,
2686: state);
2687: drawRightLabels(rightKeys, g2, plotArea, linkArea, labelWidth,
2688: state);
2689: }
2690: g2.setComposite(originalComposite);
2691:
2692: }
2693:
2694:
2706: protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D g2,
2707: Rectangle2D plotArea, Rectangle2D linkArea,
2708: float maxLabelWidth, PiePlotState state) {
2709:
2710: this.labelDistributor.clear();
2711: double lGap = plotArea.getWidth() * this.labelGap;
2712: double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2713: for (int i = 0; i < leftKeys.getItemCount(); i++) {
2714: String label = this.labelGenerator.generateSectionLabel(
2715: this.dataset, leftKeys.getKey(i));
2716: if (label != null) {
2717: TextBlock block = TextUtilities.createTextBlock(label,
2718: this.labelFont, this.labelPaint, maxLabelWidth,
2719: new G2TextMeasurer(g2));
2720: TextBox labelBox = new TextBox(block);
2721: labelBox.setBackgroundPaint(this.labelBackgroundPaint);
2722: labelBox.setOutlinePaint(this.labelOutlinePaint);
2723: labelBox.setOutlineStroke(this.labelOutlineStroke);
2724: labelBox.setShadowPaint(this.labelShadowPaint);
2725: labelBox.setInteriorGap(this.labelPadding);
2726: double theta = Math.toRadians(
2727: leftKeys.getValue(i).doubleValue());
2728: double baseY = state.getPieCenterY() - Math.sin(theta)
2729: * verticalLinkRadius;
2730: double hh = labelBox.getHeight(g2);
2731:
2732: this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
2733: leftKeys.getKey(i), theta, baseY, labelBox, hh,
2734: lGap / 2.0 + lGap / 2.0 * -Math.cos(theta), 0.9
2735: + getExplodePercent(leftKeys.getKey(i))));
2736: }
2737: }
2738: double hh = plotArea.getHeight();
2739: double gap = hh * getInteriorGap();
2740: this.labelDistributor.distributeLabels(plotArea.getMinY() + gap,
2741: hh - 2 * gap);
2742: for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
2743: drawLeftLabel(g2, state,
2744: this.labelDistributor.getPieLabelRecord(i));
2745: }
2746: }
2747:
2748:
2758: protected void drawRightLabels(KeyedValues keys, Graphics2D g2,
2759: Rectangle2D plotArea, Rectangle2D linkArea,
2760: float maxLabelWidth, PiePlotState state) {
2761:
2762:
2763: this.labelDistributor.clear();
2764: double lGap = plotArea.getWidth() * this.labelGap;
2765: double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2766:
2767: for (int i = 0; i < keys.getItemCount(); i++) {
2768: String label = this.labelGenerator.generateSectionLabel(
2769: this.dataset, keys.getKey(i));
2770:
2771: if (label != null) {
2772: TextBlock block = TextUtilities.createTextBlock(label,
2773: this.labelFont, this.labelPaint, maxLabelWidth,
2774: new G2TextMeasurer(g2));
2775: TextBox labelBox = new TextBox(block);
2776: labelBox.setBackgroundPaint(this.labelBackgroundPaint);
2777: labelBox.setOutlinePaint(this.labelOutlinePaint);
2778: labelBox.setOutlineStroke(this.labelOutlineStroke);
2779: labelBox.setShadowPaint(this.labelShadowPaint);
2780: labelBox.setInteriorGap(this.labelPadding);
2781: double theta = Math.toRadians(keys.getValue(i).doubleValue());
2782: double baseY = state.getPieCenterY()
2783: - Math.sin(theta) * verticalLinkRadius;
2784: double hh = labelBox.getHeight(g2);
2785: this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
2786: keys.getKey(i), theta, baseY, labelBox, hh,
2787: lGap / 2.0 + lGap / 2.0 * Math.cos(theta),
2788: 0.9 + getExplodePercent(keys.getKey(i))));
2789: }
2790: }
2791: double hh = plotArea.getHeight();
2792: double gap = hh * getInteriorGap();
2793: this.labelDistributor.distributeLabels(plotArea.getMinY() + gap,
2794: hh - 2 * gap);
2795: for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
2796: drawRightLabel(g2, state,
2797: this.labelDistributor.getPieLabelRecord(i));
2798: }
2799:
2800: }
2801:
2802:
2807: public LegendItemCollection getLegendItems() {
2808:
2809: LegendItemCollection result = new LegendItemCollection();
2810: if (this.dataset == null) {
2811: return result;
2812: }
2813: List keys = this.dataset.getKeys();
2814: int section = 0;
2815: Shape shape = getLegendItemShape();
2816: Iterator iterator = keys.iterator();
2817: while (iterator.hasNext()) {
2818: Comparable key = (Comparable) iterator.next();
2819: Number n = this.dataset.getValue(key);
2820: boolean include = true;
2821: if (n == null) {
2822: include = !this.ignoreNullValues;
2823: }
2824: else {
2825: double v = n.doubleValue();
2826: if (v == 0.0) {
2827: include = !this.ignoreZeroValues;
2828: }
2829: else {
2830: include = v > 0.0;
2831: }
2832: }
2833: if (include) {
2834: String label = this.legendLabelGenerator.generateSectionLabel(
2835: this.dataset, key);
2836: if (label != null) {
2837: String description = label;
2838: String toolTipText = null;
2839: if (this.legendLabelToolTipGenerator != null) {
2840: toolTipText = this.legendLabelToolTipGenerator
2841: .generateSectionLabel(this.dataset, key);
2842: }
2843: String urlText = null;
2844: if (this.legendLabelURLGenerator != null) {
2845: urlText = this.legendLabelURLGenerator.generateURL(
2846: this.dataset, key, this.pieIndex);
2847: }
2848: Paint paint = lookupSectionPaint(key, true);
2849: Paint outlinePaint = lookupSectionOutlinePaint(key);
2850: Stroke outlineStroke = lookupSectionOutlineStroke(key);
2851: LegendItem item = new LegendItem(label, description,
2852: toolTipText, urlText, true, shape, true, paint,
2853: true, outlinePaint, outlineStroke,
2854: false,
2855: new Line2D.Float(), new BasicStroke(), Color.black);
2856: item.setDataset(getDataset());
2857: result.add(item);
2858: }
2859: section++;
2860: }
2861: else {
2862: section++;
2863: }
2864: }
2865: return result;
2866: }
2867:
2868:
2873: public String getPlotType() {
2874: return localizationResources.getString("Pie_Plot");
2875: }
2876:
2877:
2891: protected Rectangle2D getArcBounds(Rectangle2D unexploded,
2892: Rectangle2D exploded,
2893: double angle, double extent,
2894: double explodePercent) {
2895:
2896: if (explodePercent == 0.0) {
2897: return unexploded;
2898: }
2899: else {
2900: Arc2D arc1 = new Arc2D.Double(unexploded, angle, extent / 2,
2901: Arc2D.OPEN);
2902: Point2D point1 = arc1.getEndPoint();
2903: Arc2D.Double arc2 = new Arc2D.Double(exploded, angle, extent / 2,
2904: Arc2D.OPEN);
2905: Point2D point2 = arc2.getEndPoint();
2906: double deltaX = (point1.getX() - point2.getX()) * explodePercent;
2907: double deltaY = (point1.getY() - point2.getY()) * explodePercent;
2908: return new Rectangle2D.Double(unexploded.getX() - deltaX,
2909: unexploded.getY() - deltaY, unexploded.getWidth(),
2910: unexploded.getHeight());
2911: }
2912: }
2913:
2914:
2921: protected void drawLeftLabel(Graphics2D g2, PiePlotState state,
2922: PieLabelRecord record) {
2923:
2924: double anchorX = state.getLinkArea().getMinX();
2925: double targetX = anchorX - record.getGap();
2926: double targetY = record.getAllocatedY();
2927:
2928: if (this.labelLinksVisible) {
2929: double theta = record.getAngle();
2930: double linkX = state.getPieCenterX() + Math.cos(theta)
2931: * state.getPieWRadius() * record.getLinkPercent();
2932: double linkY = state.getPieCenterY() - Math.sin(theta)
2933: * state.getPieHRadius() * record.getLinkPercent();
2934: double elbowX = state.getPieCenterX() + Math.cos(theta)
2935: * state.getLinkArea().getWidth() / 2.0;
2936: double elbowY = state.getPieCenterY() - Math.sin(theta)
2937: * state.getLinkArea().getHeight() / 2.0;
2938: double anchorY = elbowY;
2939: g2.setPaint(this.labelLinkPaint);
2940: g2.setStroke(this.labelLinkStroke);
2941: PieLabelLinkStyle style = getLabelLinkStyle();
2942: if (style.equals(PieLabelLinkStyle.STANDARD)) {
2943: g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
2944: g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY));
2945: g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY));
2946: }
2947: else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) {
2948: QuadCurve2D q = new QuadCurve2D.Float();
2949: q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY);
2950: g2.draw(q);
2951: g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY));
2952: }
2953: else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) {
2954: CubicCurve2D c = new CubicCurve2D .Float();
2955: c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY,
2956: linkX, linkY);
2957: g2.draw(c);
2958: }
2959: }
2960: TextBox tb = record.getLabel();
2961: tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT);
2962:
2963: }
2964:
2965:
2972: protected void drawRightLabel(Graphics2D g2, PiePlotState state,
2973: PieLabelRecord record) {
2974:
2975: double anchorX = state.getLinkArea().getMaxX();
2976: double targetX = anchorX + record.getGap();
2977: double targetY = record.getAllocatedY();
2978:
2979: if (this.labelLinksVisible) {
2980: double theta = record.getAngle();
2981: double linkX = state.getPieCenterX() + Math.cos(theta)
2982: * state.getPieWRadius() * record.getLinkPercent();
2983: double linkY = state.getPieCenterY() - Math.sin(theta)
2984: * state.getPieHRadius() * record.getLinkPercent();
2985: double elbowX = state.getPieCenterX() + Math.cos(theta)
2986: * state.getLinkArea().getWidth() / 2.0;
2987: double elbowY = state.getPieCenterY() - Math.sin(theta)
2988: * state.getLinkArea().getHeight() / 2.0;
2989: double anchorY = elbowY;
2990: g2.setPaint(this.labelLinkPaint);
2991: g2.setStroke(this.labelLinkStroke);
2992: PieLabelLinkStyle style = getLabelLinkStyle();
2993: if (style.equals(PieLabelLinkStyle.STANDARD)) {
2994: g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
2995: g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY));
2996: g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY));
2997: }
2998: else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) {
2999: QuadCurve2D q = new QuadCurve2D.Float();
3000: q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY);
3001: g2.draw(q);
3002: g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY));
3003: }
3004: else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) {
3005: CubicCurve2D c = new CubicCurve2D .Float();
3006: c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY,
3007: linkX, linkY);
3008: g2.draw(c);
3009: }
3010: }
3011:
3012: TextBox tb = record.getLabel();
3013: tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT);
3014:
3015: }
3016:
3017:
3025: public boolean equals(Object obj) {
3026: if (obj == this) {
3027: return true;
3028: }
3029: if (!(obj instanceof PiePlot)) {
3030: return false;
3031: }
3032: if (!super.equals(obj)) {
3033: return false;
3034: }
3035: PiePlot that = (PiePlot) obj;
3036: if (this.pieIndex != that.pieIndex) {
3037: return false;
3038: }
3039: if (this.interiorGap != that.interiorGap) {
3040: return false;
3041: }
3042: if (this.circular != that.circular) {
3043: return false;
3044: }
3045: if (this.startAngle != that.startAngle) {
3046: return false;
3047: }
3048: if (this.direction != that.direction) {
3049: return false;
3050: }
3051: if (this.ignoreZeroValues != that.ignoreZeroValues) {
3052: return false;
3053: }
3054: if (this.ignoreNullValues != that.ignoreNullValues) {
3055: return false;
3056: }
3057: if (!PaintUtilities.equal(this.sectionPaint, that.sectionPaint)) {
3058: return false;
3059: }
3060: if (!ObjectUtilities.equal(this.sectionPaintMap,
3061: that.sectionPaintMap)) {
3062: return false;
3063: }
3064: if (!PaintUtilities.equal(this.baseSectionPaint,
3065: that.baseSectionPaint)) {
3066: return false;
3067: }
3068: if (this.sectionOutlinesVisible != that.sectionOutlinesVisible) {
3069: return false;
3070: }
3071: if (!PaintUtilities.equal(this.sectionOutlinePaint,
3072: that.sectionOutlinePaint)) {
3073: return false;
3074: }
3075: if (!ObjectUtilities.equal(this.sectionOutlinePaintMap,
3076: that.sectionOutlinePaintMap)) {
3077: return false;
3078: }
3079: if (!PaintUtilities.equal(
3080: this.baseSectionOutlinePaint, that.baseSectionOutlinePaint
3081: )) {
3082: return false;
3083: }
3084: if (!ObjectUtilities.equal(this.sectionOutlineStroke,
3085: that.sectionOutlineStroke)) {
3086: return false;
3087: }
3088: if (!ObjectUtilities.equal(this.sectionOutlineStrokeMap,
3089: that.sectionOutlineStrokeMap)) {
3090: return false;
3091: }
3092: if (!ObjectUtilities.equal(
3093: this.baseSectionOutlineStroke, that.baseSectionOutlineStroke
3094: )) {
3095: return false;
3096: }
3097: if (!PaintUtilities.equal(this.shadowPaint, that.shadowPaint)) {
3098: return false;
3099: }
3100: if (!(this.shadowXOffset == that.shadowXOffset)) {
3101: return false;
3102: }
3103: if (!(this.shadowYOffset == that.shadowYOffset)) {
3104: return false;
3105: }
3106: if (!ObjectUtilities.equal(this.explodePercentages,
3107: that.explodePercentages)) {
3108: return false;
3109: }
3110: if (!ObjectUtilities.equal(this.labelGenerator,
3111: that.labelGenerator)) {
3112: return false;
3113: }
3114: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
3115: return false;
3116: }
3117: if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
3118: return false;
3119: }
3120: if (!PaintUtilities.equal(this.labelBackgroundPaint,
3121: that.labelBackgroundPaint)) {
3122: return false;
3123: }
3124: if (!PaintUtilities.equal(this.labelOutlinePaint,
3125: that.labelOutlinePaint)) {
3126: return false;
3127: }
3128: if (!ObjectUtilities.equal(this.labelOutlineStroke,
3129: that.labelOutlineStroke)) {
3130: return false;
3131: }
3132: if (!PaintUtilities.equal(this.labelShadowPaint,
3133: that.labelShadowPaint)) {
3134: return false;
3135: }
3136: if (this.simpleLabels != that.simpleLabels) {
3137: return false;
3138: }
3139: if (!this.simpleLabelOffset.equals(that.simpleLabelOffset)) {
3140: return false;
3141: }
3142: if (!this.labelPadding.equals(that.labelPadding)) {
3143: return false;
3144: }
3145: if (!(this.maximumLabelWidth == that.maximumLabelWidth)) {
3146: return false;
3147: }
3148: if (!(this.labelGap == that.labelGap)) {
3149: return false;
3150: }
3151: if (!(this.labelLinkMargin == that.labelLinkMargin)) {
3152: return false;
3153: }
3154: if (this.labelLinksVisible != that.labelLinksVisible) {
3155: return false;
3156: }
3157: if (!this.labelLinkStyle.equals(that.labelLinkStyle)) {
3158: return false;
3159: }
3160: if (!PaintUtilities.equal(this.labelLinkPaint, that.labelLinkPaint)) {
3161: return false;
3162: }
3163: if (!ObjectUtilities.equal(this.labelLinkStroke,
3164: that.labelLinkStroke)) {
3165: return false;
3166: }
3167: if (!ObjectUtilities.equal(this.toolTipGenerator,
3168: that.toolTipGenerator)) {
3169: return false;
3170: }
3171: if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
3172: return false;
3173: }
3174: if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) {
3175: return false;
3176: }
3177: if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) {
3178: return false;
3179: }
3180: if (!ObjectUtilities.equal(this.legendLabelGenerator,
3181: that.legendLabelGenerator)) {
3182: return false;
3183: }
3184: if (!ObjectUtilities.equal(this.legendLabelToolTipGenerator,
3185: that.legendLabelToolTipGenerator)) {
3186: return false;
3187: }
3188: if (!ObjectUtilities.equal(this.legendLabelURLGenerator,
3189: that.legendLabelURLGenerator)) {
3190: return false;
3191: }
3192:
3193: return true;
3194: }
3195:
3196:
3204: public Object clone() throws CloneNotSupportedException {
3205: PiePlot clone = (PiePlot) super.clone();
3206: if (clone.dataset != null) {
3207: clone.dataset.addChangeListener(clone);
3208: }
3209: if (this.urlGenerator instanceof PublicCloneable) {
3210: clone.urlGenerator = (PieURLGenerator) ObjectUtilities.clone(
3211: this.urlGenerator);
3212: }
3213: clone.legendItemShape = ShapeUtilities.clone(this.legendItemShape);
3214: if (this.legendLabelGenerator != null) {
3215: clone.legendLabelGenerator = (PieSectionLabelGenerator)
3216: ObjectUtilities.clone(this.legendLabelGenerator);
3217: }
3218: if (this.legendLabelToolTipGenerator != null) {
3219: clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator)
3220: ObjectUtilities.clone(this.legendLabelToolTipGenerator);
3221: }
3222: if (this.legendLabelURLGenerator instanceof PublicCloneable) {
3223: clone.legendLabelURLGenerator = (PieURLGenerator)
3224: ObjectUtilities.clone(this.legendLabelURLGenerator);
3225: }
3226: return clone;
3227: }
3228:
3229:
3236: private void writeObject(ObjectOutputStream stream) throws IOException {
3237: stream.defaultWriteObject();
3238: SerialUtilities.writePaint(this.sectionPaint, stream);
3239: SerialUtilities.writePaint(this.baseSectionPaint, stream);
3240: SerialUtilities.writePaint(this.sectionOutlinePaint, stream);
3241: SerialUtilities.writePaint(this.baseSectionOutlinePaint, stream);
3242: SerialUtilities.writeStroke(this.sectionOutlineStroke, stream);
3243: SerialUtilities.writeStroke(this.baseSectionOutlineStroke, stream);
3244: SerialUtilities.writePaint(this.shadowPaint, stream);
3245: SerialUtilities.writePaint(this.labelPaint, stream);
3246: SerialUtilities.writePaint(this.labelBackgroundPaint, stream);
3247: SerialUtilities.writePaint(this.labelOutlinePaint, stream);
3248: SerialUtilities.writeStroke(this.labelOutlineStroke, stream);
3249: SerialUtilities.writePaint(this.labelShadowPaint, stream);
3250: SerialUtilities.writePaint(this.labelLinkPaint, stream);
3251: SerialUtilities.writeStroke(this.labelLinkStroke, stream);
3252: SerialUtilities.writeShape(this.legendItemShape, stream);
3253: }
3254:
3255:
3263: private void readObject(ObjectInputStream stream)
3264: throws IOException, ClassNotFoundException {
3265: stream.defaultReadObject();
3266: this.sectionPaint = SerialUtilities.readPaint(stream);
3267: this.baseSectionPaint = SerialUtilities.readPaint(stream);
3268: this.sectionOutlinePaint = SerialUtilities.readPaint(stream);
3269: this.baseSectionOutlinePaint = SerialUtilities.readPaint(stream);
3270: this.sectionOutlineStroke = SerialUtilities.readStroke(stream);
3271: this.baseSectionOutlineStroke = SerialUtilities.readStroke(stream);
3272: this.shadowPaint = SerialUtilities.readPaint(stream);
3273: this.labelPaint = SerialUtilities.readPaint(stream);
3274: this.labelBackgroundPaint = SerialUtilities.readPaint(stream);
3275: this.labelOutlinePaint = SerialUtilities.readPaint(stream);
3276: this.labelOutlineStroke = SerialUtilities.readStroke(stream);
3277: this.labelShadowPaint = SerialUtilities.readPaint(stream);
3278: this.labelLinkPaint = SerialUtilities.readPaint(stream);
3279: this.labelLinkStroke = SerialUtilities.readStroke(stream);
3280: this.legendItemShape = SerialUtilities.readShape(stream);
3281: }
3282:
3283:
3284:
3285:
3294: public Paint getSectionPaint(int section) {
3295: Comparable key = getSectionKey(section);
3296: return getSectionPaint(key);
3297: }
3298:
3299:
3308: public void setSectionPaint(int section, Paint paint) {
3309: Comparable key = getSectionKey(section);
3310: setSectionPaint(key, paint);
3311: }
3312:
3313:
3322: public Paint getSectionOutlinePaint(int section) {
3323: Comparable key = getSectionKey(section);
3324: return getSectionOutlinePaint(key);
3325: }
3326:
3327:
3337: public void setSectionOutlinePaint(int section, Paint paint) {
3338: Comparable key = getSectionKey(section);
3339: setSectionOutlinePaint(key, paint);
3340: }
3341:
3342:
3351: public Stroke getSectionOutlineStroke(int section) {
3352: Comparable key = getSectionKey(section);
3353: return getSectionOutlineStroke(key);
3354: }
3355:
3356:
3366: public void setSectionOutlineStroke(int section, Stroke stroke) {
3367: Comparable key = getSectionKey(section);
3368: setSectionOutlineStroke(key, stroke);
3369: }
3370:
3371:
3380: public double getExplodePercent(int section) {
3381: Comparable key = getSectionKey(section);
3382: return getExplodePercent(key);
3383: }
3384:
3385:
3394: public void setExplodePercent(int section, double percent) {
3395: Comparable key = getSectionKey(section);
3396: setExplodePercent(key, percent);
3397: }
3398:
3399: }