1:
69:
70: package ;
71:
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77: import ;
78: import ;
79: import ;
80: import ;
81: import ;
82: import ;
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96:
97: import ;
98: import ;
99: import ;
100: import ;
101: import ;
102: import ;
103: import ;
104: import ;
105: import ;
106: import ;
107: import ;
108: import ;
109: import ;
110: import ;
111: import ;
112: import ;
113: import ;
114: import ;
115: import ;
116: import ;
117: import ;
118:
119:
124: public class SpiderWebPlot extends Plot implements Cloneable, Serializable {
125:
126:
127: private static final long serialVersionUID = -5376340422031599463L;
128:
129:
130: public static final double DEFAULT_HEAD = 0.01;
131:
132:
133: public static final double DEFAULT_AXIS_LABEL_GAP = 0.10;
134:
135:
136: public static final double DEFAULT_INTERIOR_GAP = 0.25;
137:
138:
139: public static final double MAX_INTERIOR_GAP = 0.40;
140:
141:
142: public static final double DEFAULT_START_ANGLE = 90.0;
143:
144:
145: public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
146: Font.PLAIN, 10);
147:
148:
149: public static final Paint DEFAULT_LABEL_PAINT = Color.black;
150:
151:
152: public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT
153: = new Color(255, 255, 192);
154:
155:
156: public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
157:
158:
159: public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE
160: = new BasicStroke(0.5f);
161:
162:
163: public static final Paint DEFAULT_LABEL_SHADOW_PAINT = Color.lightGray;
164:
165:
169: public static final double DEFAULT_MAX_VALUE = -1.0;
170:
171:
172: protected double headPercent;
173:
174:
175: private double interiorGap;
176:
177:
178: private double axisLabelGap;
179:
180:
185: private transient Paint axisLinePaint;
186:
187:
192: private transient Stroke axisLineStroke;
193:
194:
195: private CategoryDataset dataset;
196:
197:
198: private double maxValue;
199:
200:
206: private TableOrder dataExtractOrder;
207:
208:
209: private double startAngle;
210:
211:
212: private Rotation direction;
213:
214:
215: private transient Shape legendItemShape;
216:
217:
218: private transient Paint seriesPaint;
219:
220:
221: private PaintList seriesPaintList;
222:
223:
224: private transient Paint baseSeriesPaint;
225:
226:
227: private transient Paint seriesOutlinePaint;
228:
229:
230: private PaintList seriesOutlinePaintList;
231:
232:
233: private transient Paint baseSeriesOutlinePaint;
234:
235:
236: private transient Stroke seriesOutlineStroke;
237:
238:
239: private StrokeList seriesOutlineStrokeList;
240:
241:
242: private transient Stroke baseSeriesOutlineStroke;
243:
244:
245: private Font labelFont;
246:
247:
248: private transient Paint labelPaint;
249:
250:
251: private CategoryItemLabelGenerator labelGenerator;
252:
253:
254: private boolean webFilled = true;
255:
256:
257: private CategoryToolTipGenerator toolTipGenerator;
258:
259:
260: private CategoryURLGenerator urlGenerator;
261:
262:
265: public SpiderWebPlot() {
266: this(null);
267: }
268:
269:
275: public SpiderWebPlot(CategoryDataset dataset) {
276: this(dataset, TableOrder.BY_ROW);
277: }
278:
279:
286: public SpiderWebPlot(CategoryDataset dataset, TableOrder extract) {
287: super();
288: if (extract == null) {
289: throw new IllegalArgumentException("Null 'extract' argument.");
290: }
291: this.dataset = dataset;
292: if (dataset != null) {
293: dataset.addChangeListener(this);
294: }
295:
296: this.dataExtractOrder = extract;
297: this.headPercent = DEFAULT_HEAD;
298: this.axisLabelGap = DEFAULT_AXIS_LABEL_GAP;
299: this.axisLinePaint = Color.black;
300: this.axisLineStroke = new BasicStroke(1.0f);
301:
302: this.interiorGap = DEFAULT_INTERIOR_GAP;
303: this.startAngle = DEFAULT_START_ANGLE;
304: this.direction = Rotation.CLOCKWISE;
305: this.maxValue = DEFAULT_MAX_VALUE;
306:
307: this.seriesPaint = null;
308: this.seriesPaintList = new PaintList();
309: this.baseSeriesPaint = null;
310:
311: this.seriesOutlinePaint = null;
312: this.seriesOutlinePaintList = new PaintList();
313: this.baseSeriesOutlinePaint = DEFAULT_OUTLINE_PAINT;
314:
315: this.seriesOutlineStroke = null;
316: this.seriesOutlineStrokeList = new StrokeList();
317: this.baseSeriesOutlineStroke = DEFAULT_OUTLINE_STROKE;
318:
319: this.labelFont = DEFAULT_LABEL_FONT;
320: this.labelPaint = DEFAULT_LABEL_PAINT;
321: this.labelGenerator = new StandardCategoryItemLabelGenerator();
322:
323: this.legendItemShape = DEFAULT_LEGEND_ITEM_CIRCLE;
324: }
325:
326:
331: public String getPlotType() {
332:
333: return ("Spider Web Plot");
334: }
335:
336:
343: public CategoryDataset getDataset() {
344: return this.dataset;
345: }
346:
347:
355: public void setDataset(CategoryDataset dataset) {
356:
357:
358: if (this.dataset != null) {
359: this.dataset.removeChangeListener(this);
360: }
361:
362:
363: this.dataset = dataset;
364: if (dataset != null) {
365: setDatasetGroup(dataset.getGroup());
366: dataset.addChangeListener(this);
367: }
368:
369:
370: datasetChanged(new DatasetChangeEvent(this, dataset));
371: }
372:
373:
380: public boolean isWebFilled() {
381: return this.webFilled;
382: }
383:
384:
392: public void setWebFilled(boolean flag) {
393: this.webFilled = flag;
394: fireChangeEvent();
395: }
396:
397:
404: public TableOrder getDataExtractOrder() {
405: return this.dataExtractOrder;
406: }
407:
408:
419: public void setDataExtractOrder(TableOrder order) {
420: if (order == null) {
421: throw new IllegalArgumentException("Null 'order' argument");
422: }
423: this.dataExtractOrder = order;
424: fireChangeEvent();
425: }
426:
427:
434: public double getHeadPercent() {
435: return this.headPercent;
436: }
437:
438:
446: public void setHeadPercent(double percent) {
447: this.headPercent = percent;
448: fireChangeEvent();
449: }
450:
451:
461: public double getStartAngle() {
462: return this.startAngle;
463: }
464:
465:
477: public void setStartAngle(double angle) {
478: this.startAngle = angle;
479: fireChangeEvent();
480: }
481:
482:
489: public double getMaxValue() {
490: return this.maxValue;
491: }
492:
493:
501: public void setMaxValue(double value) {
502: this.maxValue = value;
503: fireChangeEvent();
504: }
505:
506:
514: public Rotation getDirection() {
515: return this.direction;
516: }
517:
518:
526: public void setDirection(Rotation direction) {
527: if (direction == null) {
528: throw new IllegalArgumentException("Null 'direction' argument.");
529: }
530: this.direction = direction;
531: fireChangeEvent();
532: }
533:
534:
542: public double getInteriorGap() {
543: return this.interiorGap;
544: }
545:
546:
555: public void setInteriorGap(double percent) {
556: if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
557: throw new IllegalArgumentException(
558: "Percentage outside valid range.");
559: }
560: if (this.interiorGap != percent) {
561: this.interiorGap = percent;
562: fireChangeEvent();
563: }
564: }
565:
566:
573: public double getAxisLabelGap() {
574: return this.axisLabelGap;
575: }
576:
577:
585: public void setAxisLabelGap(double gap) {
586: this.axisLabelGap = gap;
587: fireChangeEvent();
588: }
589:
590:
599: public Paint getAxisLinePaint() {
600: return this.axisLinePaint;
601: }
602:
603:
612: public void setAxisLinePaint(Paint paint) {
613: if (paint == null) {
614: throw new IllegalArgumentException("Null 'paint' argument.");
615: }
616: this.axisLinePaint = paint;
617: fireChangeEvent();
618: }
619:
620:
629: public Stroke getAxisLineStroke() {
630: return this.axisLineStroke;
631: }
632:
633:
642: public void setAxisLineStroke(Stroke stroke) {
643: if (stroke == null) {
644: throw new IllegalArgumentException("Null 'stroke' argument.");
645: }
646: this.axisLineStroke = stroke;
647: fireChangeEvent();
648: }
649:
650:
651:
652:
659: public Paint getSeriesPaint() {
660: return this.seriesPaint;
661: }
662:
663:
672: public void setSeriesPaint(Paint paint) {
673: this.seriesPaint = paint;
674: fireChangeEvent();
675: }
676:
677:
686: public Paint getSeriesPaint(int series) {
687:
688:
689: if (this.seriesPaint != null) {
690: return this.seriesPaint;
691: }
692:
693:
694: Paint result = this.seriesPaintList.getPaint(series);
695: if (result == null) {
696: DrawingSupplier supplier = getDrawingSupplier();
697: if (supplier != null) {
698: Paint p = supplier.getNextPaint();
699: this.seriesPaintList.setPaint(series, p);
700: result = p;
701: }
702: else {
703: result = this.baseSeriesPaint;
704: }
705: }
706: return result;
707:
708: }
709:
710:
719: public void setSeriesPaint(int series, Paint paint) {
720: this.seriesPaintList.setPaint(series, paint);
721: fireChangeEvent();
722: }
723:
724:
732: public Paint getBaseSeriesPaint() {
733: return this.baseSeriesPaint;
734: }
735:
736:
743: public void setBaseSeriesPaint(Paint paint) {
744: if (paint == null) {
745: throw new IllegalArgumentException("Null 'paint' argument.");
746: }
747: this.baseSeriesPaint = paint;
748: fireChangeEvent();
749: }
750:
751:
752:
753:
758: public Paint getSeriesOutlinePaint() {
759: return this.seriesOutlinePaint;
760: }
761:
762:
769: public void setSeriesOutlinePaint(Paint paint) {
770: this.seriesOutlinePaint = paint;
771: fireChangeEvent();
772: }
773:
774:
781: public Paint getSeriesOutlinePaint(int series) {
782:
783: if (this.seriesOutlinePaint != null) {
784: return this.seriesOutlinePaint;
785: }
786:
787: Paint result = this.seriesOutlinePaintList.getPaint(series);
788: if (result == null) {
789: result = this.baseSeriesOutlinePaint;
790: }
791: return result;
792: }
793:
794:
801: public void setSeriesOutlinePaint(int series, Paint paint) {
802: this.seriesOutlinePaintList.setPaint(series, paint);
803: fireChangeEvent();
804: }
805:
806:
812: public Paint getBaseSeriesOutlinePaint() {
813: return this.baseSeriesOutlinePaint;
814: }
815:
816:
821: public void setBaseSeriesOutlinePaint(Paint paint) {
822: if (paint == null) {
823: throw new IllegalArgumentException("Null 'paint' argument.");
824: }
825: this.baseSeriesOutlinePaint = paint;
826: fireChangeEvent();
827: }
828:
829:
830:
831:
836: public Stroke getSeriesOutlineStroke() {
837: return this.seriesOutlineStroke;
838: }
839:
840:
847: public void setSeriesOutlineStroke(Stroke stroke) {
848: this.seriesOutlineStroke = stroke;
849: fireChangeEvent();
850: }
851:
852:
859: public Stroke getSeriesOutlineStroke(int series) {
860:
861:
862: if (this.seriesOutlineStroke != null) {
863: return this.seriesOutlineStroke;
864: }
865:
866:
867: Stroke result = this.seriesOutlineStrokeList.getStroke(series);
868: if (result == null) {
869: result = this.baseSeriesOutlineStroke;
870: }
871: return result;
872:
873: }
874:
875:
882: public void setSeriesOutlineStroke(int series, Stroke stroke) {
883: this.seriesOutlineStrokeList.setStroke(series, stroke);
884: fireChangeEvent();
885: }
886:
887:
893: public Stroke getBaseSeriesOutlineStroke() {
894: return this.baseSeriesOutlineStroke;
895: }
896:
897:
902: public void setBaseSeriesOutlineStroke(Stroke stroke) {
903: if (stroke == null) {
904: throw new IllegalArgumentException("Null 'stroke' argument.");
905: }
906: this.baseSeriesOutlineStroke = stroke;
907: fireChangeEvent();
908: }
909:
910:
917: public Shape getLegendItemShape() {
918: return this.legendItemShape;
919: }
920:
921:
929: public void setLegendItemShape(Shape shape) {
930: if (shape == null) {
931: throw new IllegalArgumentException("Null 'shape' argument.");
932: }
933: this.legendItemShape = shape;
934: fireChangeEvent();
935: }
936:
937:
944: public Font getLabelFont() {
945: return this.labelFont;
946: }
947:
948:
956: public void setLabelFont(Font font) {
957: if (font == null) {
958: throw new IllegalArgumentException("Null 'font' argument.");
959: }
960: this.labelFont = font;
961: fireChangeEvent();
962: }
963:
964:
971: public Paint getLabelPaint() {
972: return this.labelPaint;
973: }
974:
975:
983: public void setLabelPaint(Paint paint) {
984: if (paint == null) {
985: throw new IllegalArgumentException("Null 'paint' argument.");
986: }
987: this.labelPaint = paint;
988: fireChangeEvent();
989: }
990:
991:
998: public CategoryItemLabelGenerator getLabelGenerator() {
999: return this.labelGenerator;
1000: }
1001:
1002:
1010: public void setLabelGenerator(CategoryItemLabelGenerator generator) {
1011: if (generator == null) {
1012: throw new IllegalArgumentException("Null 'generator' argument.");
1013: }
1014: this.labelGenerator = generator;
1015: }
1016:
1017:
1026: public CategoryToolTipGenerator getToolTipGenerator() {
1027: return this.toolTipGenerator;
1028: }
1029:
1030:
1040: public void setToolTipGenerator(CategoryToolTipGenerator generator) {
1041: this.toolTipGenerator = generator;
1042: fireChangeEvent();
1043: }
1044:
1045:
1054: public CategoryURLGenerator getURLGenerator() {
1055: return this.urlGenerator;
1056: }
1057:
1058:
1068: public void setURLGenerator(CategoryURLGenerator generator) {
1069: this.urlGenerator = generator;
1070: fireChangeEvent();
1071: }
1072:
1073:
1078: public LegendItemCollection getLegendItems() {
1079:
1080: LegendItemCollection result = new LegendItemCollection();
1081: if (getDataset() == null) {
1082: return result;
1083: }
1084:
1085: List keys = null;
1086: if (this.dataExtractOrder == TableOrder.BY_ROW) {
1087: keys = this.dataset.getRowKeys();
1088: }
1089: else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
1090: keys = this.dataset.getColumnKeys();
1091: }
1092:
1093: if (keys != null) {
1094: int series = 0;
1095: Iterator iterator = keys.iterator();
1096: Shape shape = getLegendItemShape();
1097:
1098: while (iterator.hasNext()) {
1099: String label = iterator.next().toString();
1100: String description = label;
1101:
1102: Paint paint = getSeriesPaint(series);
1103: Paint outlinePaint = getSeriesOutlinePaint(series);
1104: Stroke stroke = getSeriesOutlineStroke(series);
1105: LegendItem item = new LegendItem(label, description,
1106: null, null, shape, paint, stroke, outlinePaint);
1107: item.setDataset(getDataset());
1108: result.add(item);
1109: series++;
1110: }
1111: }
1112:
1113: return result;
1114: }
1115:
1116:
1125: protected Point2D getWebPoint(Rectangle2D bounds,
1126: double angle, double length) {
1127:
1128: double angrad = Math.toRadians(angle);
1129: double x = Math.cos(angrad) * length * bounds.getWidth() / 2;
1130: double y = -Math.sin(angrad) * length * bounds.getHeight() / 2;
1131:
1132: return new Point2D.Double(bounds.getX() + x + bounds.getWidth() / 2,
1133: bounds.getY() + y + bounds.getHeight() / 2);
1134: }
1135:
1136:
1146: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
1147: PlotState parentState, PlotRenderingInfo info) {
1148:
1149:
1150: RectangleInsets insets = getInsets();
1151: insets.trim(area);
1152:
1153: if (info != null) {
1154: info.setPlotArea(area);
1155: info.setDataArea(area);
1156: }
1157:
1158: drawBackground(g2, area);
1159: drawOutline(g2, area);
1160:
1161: Shape savedClip = g2.getClip();
1162:
1163: g2.clip(area);
1164: Composite originalComposite = g2.getComposite();
1165: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1166: getForegroundAlpha()));
1167:
1168: if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
1169: int seriesCount = 0, catCount = 0;
1170:
1171: if (this.dataExtractOrder == TableOrder.BY_ROW) {
1172: seriesCount = this.dataset.getRowCount();
1173: catCount = this.dataset.getColumnCount();
1174: }
1175: else {
1176: seriesCount = this.dataset.getColumnCount();
1177: catCount = this.dataset.getRowCount();
1178: }
1179:
1180:
1181: if (this.maxValue == DEFAULT_MAX_VALUE)
1182: calculateMaxValue(seriesCount, catCount);
1183:
1184:
1185:
1186:
1187:
1188: double gapHorizontal = area.getWidth() * getInteriorGap();
1189: double gapVertical = area.getHeight() * getInteriorGap();
1190:
1191: double X = area.getX() + gapHorizontal / 2;
1192: double Y = area.getY() + gapVertical / 2;
1193: double W = area.getWidth() - gapHorizontal;
1194: double H = area.getHeight() - gapVertical;
1195:
1196: double headW = area.getWidth() * this.headPercent;
1197: double headH = area.getHeight() * this.headPercent;
1198:
1199:
1200: double min = Math.min(W, H) / 2;
1201: X = (X + X + W) / 2 - min;
1202: Y = (Y + Y + H) / 2 - min;
1203: W = 2 * min;
1204: H = 2 * min;
1205:
1206: Point2D centre = new Point2D.Double(X + W / 2, Y + H / 2);
1207: Rectangle2D radarArea = new Rectangle2D.Double(X, Y, W, H);
1208:
1209:
1210: for (int cat = 0; cat < catCount; cat++) {
1211: double angle = getStartAngle()
1212: + (getDirection().getFactor() * cat * 360 / catCount);
1213:
1214: Point2D endPoint = getWebPoint(radarArea, angle, 1);
1215:
1216: Line2D line = new Line2D.Double(centre, endPoint);
1217: g2.setPaint(this.axisLinePaint);
1218: g2.setStroke(this.axisLineStroke);
1219: g2.draw(line);
1220: drawLabel(g2, radarArea, 0.0, cat, angle, 360.0 / catCount);
1221: }
1222:
1223:
1224: for (int series = 0; series < seriesCount; series++) {
1225: drawRadarPoly(g2, radarArea, centre, info, series, catCount,
1226: headH, headW);
1227: }
1228: }
1229: else {
1230: drawNoDataMessage(g2, area);
1231: }
1232: g2.setClip(savedClip);
1233: g2.setComposite(originalComposite);
1234: drawOutline(g2, area);
1235: }
1236:
1237:
1244: private void calculateMaxValue(int seriesCount, int catCount) {
1245: double v = 0;
1246: Number nV = null;
1247:
1248: for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) {
1249: for (int catIndex = 0; catIndex < catCount; catIndex++) {
1250: nV = getPlotValue(seriesIndex, catIndex);
1251: if (nV != null) {
1252: v = nV.doubleValue();
1253: if (v > this.maxValue) {
1254: this.maxValue = v;
1255: }
1256: }
1257: }
1258: }
1259: }
1260:
1261:
1273: protected void drawRadarPoly(Graphics2D g2,
1274: Rectangle2D plotArea,
1275: Point2D centre,
1276: PlotRenderingInfo info,
1277: int series, int catCount,
1278: double headH, double headW) {
1279:
1280: Polygon polygon = new Polygon();
1281:
1282: EntityCollection entities = null;
1283: if (info != null) {
1284: entities = info.getOwner().getEntityCollection();
1285: }
1286:
1287:
1288: for (int cat = 0; cat < catCount; cat++) {
1289:
1290: Number dataValue = getPlotValue(series, cat);
1291:
1292: if (dataValue != null) {
1293: double value = dataValue.doubleValue();
1294:
1295: if (value >= 0) {
1296:
1297:
1298:
1299: double angle = getStartAngle()
1300: + (getDirection().getFactor() * cat * 360 / catCount);
1301:
1302:
1303:
1304:
1305:
1306:
1307:
1308:
1309:
1310:
1311:
1312:
1313: Point2D point = getWebPoint(plotArea, angle,
1314: value / this.maxValue);
1315: polygon.addPoint((int) point.getX(), (int) point.getY());
1316:
1317:
1318:
1319: Paint paint = getSeriesPaint(series);
1320: Paint outlinePaint = getSeriesOutlinePaint(series);
1321: Stroke outlineStroke = getSeriesOutlineStroke(series);
1322:
1323: Ellipse2D head = new Ellipse2D.Double(point.getX()
1324: - headW / 2, point.getY() - headH / 2, headW,
1325: headH);
1326: g2.setPaint(paint);
1327: g2.fill(head);
1328: g2.setStroke(outlineStroke);
1329: g2.setPaint(outlinePaint);
1330: g2.draw(head);
1331:
1332: if (entities != null) {
1333: int row = 0; int col = 0;
1334: if (this.dataExtractOrder == TableOrder.BY_ROW) {
1335: row = series;
1336: col = cat;
1337: }
1338: else {
1339: row = cat;
1340: col = series;
1341: }
1342: String tip = null;
1343: if (this.toolTipGenerator != null) {
1344: tip = this.toolTipGenerator.generateToolTip(
1345: this.dataset, row, col);
1346: }
1347:
1348: String url = null;
1349: if (this.urlGenerator != null) {
1350: url = this.urlGenerator.generateURL(this.dataset,
1351: row, col);
1352: }
1353:
1354: Shape area = new Rectangle(
1355: (int) (point.getX() - headW),
1356: (int) (point.getY() - headH),
1357: (int) (headW * 2), (int) (headH * 2));
1358: CategoryItemEntity entity = new CategoryItemEntity(
1359: area, tip, url, this.dataset,
1360: this.dataset.getRowKey(row),
1361: this.dataset.getColumnKey(col));
1362: entities.add(entity);
1363: }
1364:
1365: }
1366: }
1367: }
1368:
1369:
1370: Paint paint = getSeriesPaint(series);
1371: g2.setPaint(paint);
1372: g2.setStroke(getSeriesOutlineStroke(series));
1373: g2.draw(polygon);
1374:
1375:
1376:
1377: if (this.webFilled) {
1378: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1379: 0.1f));
1380: g2.fill(polygon);
1381: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1382: getForegroundAlpha()));
1383: }
1384: }
1385:
1386:
1400: protected Number getPlotValue(int series, int cat) {
1401: Number value = null;
1402: if (this.dataExtractOrder == TableOrder.BY_ROW) {
1403: value = this.dataset.getValue(series, cat);
1404: }
1405: else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
1406: value = this.dataset.getValue(cat, series);
1407: }
1408: return value;
1409: }
1410:
1411:
1421: protected void drawLabel(Graphics2D g2, Rectangle2D plotArea, double value,
1422: int cat, double startAngle, double extent) {
1423: FontRenderContext frc = g2.getFontRenderContext();
1424:
1425: String label = null;
1426: if (this.dataExtractOrder == TableOrder.BY_ROW) {
1427:
1428: label = this.labelGenerator.generateColumnLabel(this.dataset, cat);
1429: }
1430: else {
1431:
1432: label = this.labelGenerator.generateRowLabel(this.dataset, cat);
1433: }
1434:
1435: Rectangle2D labelBounds = getLabelFont().getStringBounds(label, frc);
1436: LineMetrics lm = getLabelFont().getLineMetrics(label, frc);
1437: double ascent = lm.getAscent();
1438:
1439: Point2D labelLocation = calculateLabelLocation(labelBounds, ascent,
1440: plotArea, startAngle);
1441:
1442: Composite saveComposite = g2.getComposite();
1443:
1444: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1445: 1.0f));
1446: g2.setPaint(getLabelPaint());
1447: g2.setFont(getLabelFont());
1448: g2.drawString(label, (float) labelLocation.getX(),
1449: (float) labelLocation.getY());
1450: g2.setComposite(saveComposite);
1451: }
1452:
1453:
1463: protected Point2D calculateLabelLocation(Rectangle2D labelBounds,
1464: double ascent,
1465: Rectangle2D plotArea,
1466: double startAngle)
1467: {
1468: Arc2D arc1 = new Arc2D.Double(plotArea, startAngle, 0, Arc2D.OPEN);
1469: Point2D point1 = arc1.getEndPoint();
1470:
1471: double deltaX = -(point1.getX() - plotArea.getCenterX())
1472: * this.axisLabelGap;
1473: double deltaY = -(point1.getY() - plotArea.getCenterY())
1474: * this.axisLabelGap;
1475:
1476: double labelX = point1.getX() - deltaX;
1477: double labelY = point1.getY() - deltaY;
1478:
1479: if (labelX < plotArea.getCenterX()) {
1480: labelX -= labelBounds.getWidth();
1481: }
1482:
1483: if (labelX == plotArea.getCenterX()) {
1484: labelX -= labelBounds.getWidth() / 2;
1485: }
1486:
1487: if (labelY > plotArea.getCenterY()) {
1488: labelY += ascent;
1489: }
1490:
1491: return new Point2D.Double(labelX, labelY);
1492: }
1493:
1494:
1501: public boolean equals(Object obj) {
1502: if (obj == this) {
1503: return true;
1504: }
1505: if (!(obj instanceof SpiderWebPlot)) {
1506: return false;
1507: }
1508: if (!super.equals(obj)) {
1509: return false;
1510: }
1511: SpiderWebPlot that = (SpiderWebPlot) obj;
1512: if (!this.dataExtractOrder.equals(that.dataExtractOrder)) {
1513: return false;
1514: }
1515: if (this.headPercent != that.headPercent) {
1516: return false;
1517: }
1518: if (this.interiorGap != that.interiorGap) {
1519: return false;
1520: }
1521: if (this.startAngle != that.startAngle) {
1522: return false;
1523: }
1524: if (!this.direction.equals(that.direction)) {
1525: return false;
1526: }
1527: if (this.maxValue != that.maxValue) {
1528: return false;
1529: }
1530: if (this.webFilled != that.webFilled) {
1531: return false;
1532: }
1533: if (this.axisLabelGap != that.axisLabelGap) {
1534: return false;
1535: }
1536: if (!PaintUtilities.equal(this.axisLinePaint, that.axisLinePaint)) {
1537: return false;
1538: }
1539: if (!this.axisLineStroke.equals(that.axisLineStroke)) {
1540: return false;
1541: }
1542: if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) {
1543: return false;
1544: }
1545: if (!PaintUtilities.equal(this.seriesPaint, that.seriesPaint)) {
1546: return false;
1547: }
1548: if (!this.seriesPaintList.equals(that.seriesPaintList)) {
1549: return false;
1550: }
1551: if (!PaintUtilities.equal(this.baseSeriesPaint, that.baseSeriesPaint)) {
1552: return false;
1553: }
1554: if (!PaintUtilities.equal(this.seriesOutlinePaint,
1555: that.seriesOutlinePaint)) {
1556: return false;
1557: }
1558: if (!this.seriesOutlinePaintList.equals(that.seriesOutlinePaintList)) {
1559: return false;
1560: }
1561: if (!PaintUtilities.equal(this.baseSeriesOutlinePaint,
1562: that.baseSeriesOutlinePaint)) {
1563: return false;
1564: }
1565: if (!ObjectUtilities.equal(this.seriesOutlineStroke,
1566: that.seriesOutlineStroke)) {
1567: return false;
1568: }
1569: if (!this.seriesOutlineStrokeList.equals(
1570: that.seriesOutlineStrokeList)) {
1571: return false;
1572: }
1573: if (!this.baseSeriesOutlineStroke.equals(
1574: that.baseSeriesOutlineStroke)) {
1575: return false;
1576: }
1577: if (!this.labelFont.equals(that.labelFont)) {
1578: return false;
1579: }
1580: if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
1581: return false;
1582: }
1583: if (!this.labelGenerator.equals(that.labelGenerator)) {
1584: return false;
1585: }
1586: if (!ObjectUtilities.equal(this.toolTipGenerator,
1587: that.toolTipGenerator)) {
1588: return false;
1589: }
1590: if (!ObjectUtilities.equal(this.urlGenerator,
1591: that.urlGenerator)) {
1592: return false;
1593: }
1594: return true;
1595: }
1596:
1597:
1605: public Object clone() throws CloneNotSupportedException {
1606: SpiderWebPlot clone = (SpiderWebPlot) super.clone();
1607: clone.legendItemShape = ShapeUtilities.clone(this.legendItemShape);
1608: clone.seriesPaintList = (PaintList) this.seriesPaintList.clone();
1609: clone.seriesOutlinePaintList
1610: = (PaintList) this.seriesOutlinePaintList.clone();
1611: clone.seriesOutlineStrokeList
1612: = (StrokeList) this.seriesOutlineStrokeList.clone();
1613: return clone;
1614: }
1615:
1616:
1623: private void writeObject(ObjectOutputStream stream) throws IOException {
1624: stream.defaultWriteObject();
1625:
1626: SerialUtilities.writeShape(this.legendItemShape, stream);
1627: SerialUtilities.writePaint(this.seriesPaint, stream);
1628: SerialUtilities.writePaint(this.baseSeriesPaint, stream);
1629: SerialUtilities.writePaint(this.seriesOutlinePaint, stream);
1630: SerialUtilities.writePaint(this.baseSeriesOutlinePaint, stream);
1631: SerialUtilities.writeStroke(this.seriesOutlineStroke, stream);
1632: SerialUtilities.writeStroke(this.baseSeriesOutlineStroke, stream);
1633: SerialUtilities.writePaint(this.labelPaint, stream);
1634: SerialUtilities.writePaint(this.axisLinePaint, stream);
1635: SerialUtilities.writeStroke(this.axisLineStroke, stream);
1636: }
1637:
1638:
1646: private void readObject(ObjectInputStream stream) throws IOException,
1647: ClassNotFoundException {
1648: stream.defaultReadObject();
1649:
1650: this.legendItemShape = SerialUtilities.readShape(stream);
1651: this.seriesPaint = SerialUtilities.readPaint(stream);
1652: this.baseSeriesPaint = SerialUtilities.readPaint(stream);
1653: this.seriesOutlinePaint = SerialUtilities.readPaint(stream);
1654: this.baseSeriesOutlinePaint = SerialUtilities.readPaint(stream);
1655: this.seriesOutlineStroke = SerialUtilities.readStroke(stream);
1656: this.baseSeriesOutlineStroke = SerialUtilities.readStroke(stream);
1657: this.labelPaint = SerialUtilities.readPaint(stream);
1658: this.axisLinePaint = SerialUtilities.readPaint(stream);
1659: this.axisLineStroke = SerialUtilities.readStroke(stream);
1660: if (this.dataset != null) {
1661: this.dataset.addChangeListener(this);
1662: }
1663: }
1664:
1665: }