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