1:
50:
51: package ;
52:
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67:
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77: import ;
78: import ;
79: import ;
80: import ;
81:
82:
85: public class RingPlot extends PiePlot implements Cloneable, Serializable {
86:
87:
88: private static final long serialVersionUID = 1556064784129676620L;
89:
90:
94: private boolean separatorsVisible;
95:
96:
97: private transient Stroke separatorStroke;
98:
99:
100: private transient Paint separatorPaint;
101:
102:
106: private double innerSeparatorExtension;
107:
108:
112: private double outerSeparatorExtension;
113:
114:
117: private double sectionDepth;
118:
119:
122: public RingPlot() {
123: this(null);
124: }
125:
126:
131: public RingPlot(PieDataset dataset) {
132: super(dataset);
133: this.separatorsVisible = true;
134: this.separatorStroke = new BasicStroke(0.5f);
135: this.separatorPaint = Color.gray;
136: this.innerSeparatorExtension = 0.20;
137: this.outerSeparatorExtension = 0.20;
138: this.sectionDepth = 0.20;
139: }
140:
141:
149: public boolean getSeparatorsVisible() {
150: return this.separatorsVisible;
151: }
152:
153:
162: public void setSeparatorsVisible(boolean visible) {
163: this.separatorsVisible = visible;
164: notifyListeners(new PlotChangeEvent(this));
165: }
166:
167:
174: public Stroke getSeparatorStroke() {
175: return this.separatorStroke;
176: }
177:
178:
186: public void setSeparatorStroke(Stroke stroke) {
187: if (stroke == null) {
188: throw new IllegalArgumentException("Null 'stroke' argument.");
189: }
190: this.separatorStroke = stroke;
191: notifyListeners(new PlotChangeEvent(this));
192: }
193:
194:
201: public Paint getSeparatorPaint() {
202: return this.separatorPaint;
203: }
204:
205:
213: public void setSeparatorPaint(Paint paint) {
214: if (paint == null) {
215: throw new IllegalArgumentException("Null 'paint' argument.");
216: }
217: this.separatorPaint = paint;
218: notifyListeners(new PlotChangeEvent(this));
219: }
220:
221:
230: public double getInnerSeparatorExtension() {
231: return this.innerSeparatorExtension;
232: }
233:
234:
245: public void setInnerSeparatorExtension(double percent) {
246: this.innerSeparatorExtension = percent;
247: notifyListeners(new PlotChangeEvent(this));
248: }
249:
250:
259: public double getOuterSeparatorExtension() {
260: return this.outerSeparatorExtension;
261: }
262:
263:
273: public void setOuterSeparatorExtension(double percent) {
274: this.outerSeparatorExtension = percent;
275: notifyListeners(new PlotChangeEvent(this));
276: }
277:
278:
287: public double getSectionDepth() {
288: return this.sectionDepth;
289: }
290:
291:
300: public void setSectionDepth(double sectionDepth) {
301: this.sectionDepth = sectionDepth;
302: notifyListeners(new PlotChangeEvent(this));
303: }
304:
305:
320: public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea,
321: PiePlot plot, Integer index, PlotRenderingInfo info) {
322:
323: PiePlotState state = super.initialise(g2, plotArea, plot, index, info);
324: state.setPassesRequired(3);
325: return state;
326:
327: }
328:
329:
338: protected void drawItem(Graphics2D g2,
339: int section,
340: Rectangle2D dataArea,
341: PiePlotState state,
342: int currentPass) {
343:
344: PieDataset dataset = getDataset();
345: Number n = dataset.getValue(section);
346: if (n == null) {
347: return;
348: }
349: double value = n.doubleValue();
350: double angle1 = 0.0;
351: double angle2 = 0.0;
352:
353: Rotation direction = getDirection();
354: if (direction == Rotation.CLOCKWISE) {
355: angle1 = state.getLatestAngle();
356: angle2 = angle1 - value / state.getTotal() * 360.0;
357: }
358: else if (direction == Rotation.ANTICLOCKWISE) {
359: angle1 = state.getLatestAngle();
360: angle2 = angle1 + value / state.getTotal() * 360.0;
361: }
362: else {
363: throw new IllegalStateException("Rotation type not recognised.");
364: }
365:
366: double angle = (angle2 - angle1);
367: if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
368: Comparable key = getSectionKey(section);
369: double ep = 0.0;
370: double mep = getMaximumExplodePercent();
371: if (mep > 0.0) {
372: ep = getExplodePercent(key) / mep;
373: }
374: Rectangle2D arcBounds = getArcBounds(state.getPieArea(),
375: state.getExplodedPieArea(), angle1, angle, ep);
376: Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle,
377: Arc2D.OPEN);
378:
379:
380: double depth = this.sectionDepth / 2.0;
381: RectangleInsets s = new RectangleInsets(UnitType.RELATIVE,
382: depth, depth, depth, depth);
383: Rectangle2D innerArcBounds = new Rectangle2D.Double();
384: innerArcBounds.setRect(arcBounds);
385: s.trim(innerArcBounds);
386:
387:
388: Arc2D.Double arc2 = new Arc2D.Double(innerArcBounds, angle1
389: + angle, -angle, Arc2D.OPEN);
390: GeneralPath path = new GeneralPath();
391: path.moveTo((float) arc.getStartPoint().getX(),
392: (float) arc.getStartPoint().getY());
393: path.append(arc.getPathIterator(null), false);
394: path.append(arc2.getPathIterator(null), true);
395: path.closePath();
396:
397: Line2D separator = new Line2D.Double(arc2.getEndPoint(),
398: arc.getStartPoint());
399:
400: if (currentPass == 0) {
401: Paint shadowPaint = getShadowPaint();
402: double shadowXOffset = getShadowXOffset();
403: double shadowYOffset = getShadowYOffset();
404: if (shadowPaint != null) {
405: Shape shadowArc = ShapeUtilities.createTranslatedShape(
406: path, (float) shadowXOffset, (float) shadowYOffset);
407: g2.setPaint(shadowPaint);
408: g2.fill(shadowArc);
409: }
410: }
411: else if (currentPass == 1) {
412: Paint paint = lookupSectionPaint(key, true);
413: g2.setPaint(paint);
414: g2.fill(path);
415: Paint outlinePaint = lookupSectionOutlinePaint(key);
416: Stroke outlineStroke = lookupSectionOutlineStroke(key);
417: if (outlinePaint != null && outlineStroke != null) {
418: g2.setPaint(outlinePaint);
419: g2.setStroke(outlineStroke);
420: g2.draw(path);
421: }
422:
423:
424: if (state.getInfo() != null) {
425: EntityCollection entities = state.getEntityCollection();
426: if (entities != null) {
427: String tip = null;
428: PieToolTipGenerator toolTipGenerator
429: = getToolTipGenerator();
430: if (toolTipGenerator != null) {
431: tip = toolTipGenerator.generateToolTip(dataset,
432: key);
433: }
434: String url = null;
435: PieURLGenerator urlGenerator = getURLGenerator();
436: if (urlGenerator != null) {
437: url = urlGenerator.generateURL(dataset, key,
438: getPieIndex());
439: }
440: PieSectionEntity entity = new PieSectionEntity(path,
441: dataset, getPieIndex(), section, key, tip,
442: url);
443: entities.add(entity);
444: }
445: }
446: }
447: else if (currentPass == 2) {
448: if (this.separatorsVisible) {
449: Line2D extendedSeparator = extendLine(separator,
450: this.innerSeparatorExtension,
451: this.outerSeparatorExtension);
452: g2.setStroke(this.separatorStroke);
453: g2.setPaint(this.separatorPaint);
454: g2.draw(extendedSeparator);
455: }
456: }
457: }
458: state.setLatestAngle(angle2);
459: }
460:
461:
468: public boolean equals(Object obj) {
469: if (this == obj) {
470: return true;
471: }
472: if (!(obj instanceof RingPlot)) {
473: return false;
474: }
475: RingPlot that = (RingPlot) obj;
476: if (this.separatorsVisible != that.separatorsVisible) {
477: return false;
478: }
479: if (!ObjectUtilities.equal(this.separatorStroke,
480: that.separatorStroke)) {
481: return false;
482: }
483: if (!PaintUtilities.equal(this.separatorPaint, that.separatorPaint)) {
484: return false;
485: }
486: if (this.innerSeparatorExtension != that.innerSeparatorExtension) {
487: return false;
488: }
489: if (this.outerSeparatorExtension != that.outerSeparatorExtension) {
490: return false;
491: }
492: if (this.sectionDepth != that.sectionDepth) {
493: return false;
494: }
495: return super.equals(obj);
496: }
497:
498:
508: private Line2D extendLine(Line2D line, double startPercent,
509: double endPercent) {
510: if (line == null) {
511: throw new IllegalArgumentException("Null 'line' argument.");
512: }
513: double x1 = line.getX1();
514: double x2 = line.getX2();
515: double deltaX = x2 - x1;
516: double y1 = line.getY1();
517: double y2 = line.getY2();
518: double deltaY = y2 - y1;
519: x1 = x1 - (startPercent * deltaX);
520: y1 = y1 - (startPercent * deltaY);
521: x2 = x2 + (endPercent * deltaX);
522: y2 = y2 + (endPercent * deltaY);
523: return new Line2D.Double(x1, y1, x2, y2);
524: }
525:
526:
533: private void writeObject(ObjectOutputStream stream) throws IOException {
534: stream.defaultWriteObject();
535: SerialUtilities.writeStroke(this.separatorStroke, stream);
536: SerialUtilities.writePaint(this.separatorPaint, stream);
537: }
538:
539:
547: private void readObject(ObjectInputStream stream)
548: throws IOException, ClassNotFoundException {
549: stream.defaultReadObject();
550: this.separatorStroke = SerialUtilities.readStroke(stream);
551: this.separatorPaint = SerialUtilities.readPaint(stream);
552: }
553:
554: }