1:
47:
48: package ;
49:
50: import ;
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:
65: import ;
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:
79:
82: public class RingPlot extends PiePlot implements Cloneable, Serializable {
83:
84:
85: private static final long serialVersionUID = 1556064784129676620L;
86:
87:
91: private boolean separatorsVisible;
92:
93:
94: private transient Stroke separatorStroke;
95:
96:
97: private transient Paint separatorPaint;
98:
99:
103: private double innerSeparatorExtension;
104:
105:
109: private double outerSeparatorExtension;
110:
111:
114: public RingPlot() {
115: this(null);
116: }
117:
118:
123: public RingPlot(PieDataset dataset) {
124: super(dataset);
125: this.separatorsVisible = true;
126: this.separatorStroke = new BasicStroke(0.5f);
127: this.separatorPaint = Color.gray;
128: this.innerSeparatorExtension = 0.20;
129: this.outerSeparatorExtension = 0.20;
130: }
131:
132:
138: public boolean getSeparatorsVisible() {
139: return this.separatorsVisible;
140: }
141:
142:
149: public void setSeparatorsVisible(boolean visible) {
150: this.separatorsVisible = visible;
151: notifyListeners(new PlotChangeEvent(this));
152: }
153:
154:
159: public Stroke getSeparatorStroke() {
160: return this.separatorStroke;
161: }
162:
163:
168: public void setSeparatorStroke(Stroke stroke) {
169: if (stroke == null) {
170: throw new IllegalArgumentException("Null 'stroke' argument.");
171: }
172: this.separatorStroke = stroke;
173: notifyListeners(new PlotChangeEvent(this));
174: }
175:
176:
181: public Paint getSeparatorPaint() {
182: return this.separatorPaint;
183: }
184:
185:
190: public void setSeparatorPaint(Paint paint) {
191: if (paint == null) {
192: throw new IllegalArgumentException("Null 'paint' argument.");
193: }
194: this.separatorPaint = paint;
195: notifyListeners(new PlotChangeEvent(this));
196: }
197:
198:
205: public double getInnerSeparatorExtension() {
206: return this.innerSeparatorExtension;
207: }
208:
209:
217: public void setInnerSeparatorExtension(double percent) {
218: this.innerSeparatorExtension = percent;
219: notifyListeners(new PlotChangeEvent(this));
220: }
221:
222:
229: public double getOuterSeparatorExtension() {
230: return this.outerSeparatorExtension;
231: }
232:
233:
241: public void setOuterSeparatorExtension(double percent) {
242: this.outerSeparatorExtension = percent;
243: notifyListeners(new PlotChangeEvent(this));
244: }
245:
246:
255: protected void drawItem(Graphics2D g2,
256: int section,
257: Rectangle2D dataArea,
258: PiePlotState state,
259: int currentPass) {
260:
261: PieDataset dataset = getDataset();
262: Number n = dataset.getValue(section);
263: if (n == null) {
264: return;
265: }
266: double value = n.doubleValue();
267: double angle1 = 0.0;
268: double angle2 = 0.0;
269:
270: Rotation direction = getDirection();
271: if (direction == Rotation.CLOCKWISE) {
272: angle1 = state.getLatestAngle();
273: angle2 = angle1 - value / state.getTotal() * 360.0;
274: }
275: else if (direction == Rotation.ANTICLOCKWISE) {
276: angle1 = state.getLatestAngle();
277: angle2 = angle1 + value / state.getTotal() * 360.0;
278: }
279: else {
280: throw new IllegalStateException("Rotation type not recognised.");
281: }
282:
283: double angle = (angle2 - angle1);
284: if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
285: double ep = 0.0;
286: double mep = getMaximumExplodePercent();
287: if (mep > 0.0) {
288: ep = getExplodePercent(section) / mep;
289: }
290: Rectangle2D arcBounds = getArcBounds(
291: state.getPieArea(), state.getExplodedPieArea(),
292: angle1, angle, ep
293: );
294: Arc2D.Double arc = new Arc2D.Double(
295: arcBounds, angle1, angle, Arc2D.OPEN
296: );
297:
298:
299: RectangleInsets s = new RectangleInsets(
300: UnitType.RELATIVE, 0.10, 0.10, 0.10, 0.10
301: );
302: Rectangle2D innerArcBounds = new Rectangle2D.Double();
303: innerArcBounds.setRect(arcBounds);
304: s.trim(innerArcBounds);
305:
306:
307: Arc2D.Double arc2 = new Arc2D.Double(
308: innerArcBounds, angle1 + angle, -angle, Arc2D.OPEN
309: );
310: GeneralPath path = new GeneralPath();
311: path.moveTo(
312: (float) arc.getStartPoint().getX(),
313: (float) arc.getStartPoint().getY()
314: );
315: path.append(arc.getPathIterator(null), false);
316: path.append(arc2.getPathIterator(null), true);
317: path.closePath();
318:
319: Line2D separator = new Line2D.Double(
320: arc2.getEndPoint(), arc.getStartPoint()
321: );
322:
323: if (currentPass == 0) {
324: Paint shadowPaint = getShadowPaint();
325: double shadowXOffset = getShadowXOffset();
326: double shadowYOffset = getShadowYOffset();
327: if (shadowPaint != null) {
328: Shape shadowArc = ShapeUtilities.createTranslatedShape(
329: path, (float) shadowXOffset, (float) shadowYOffset
330: );
331: g2.setPaint(shadowPaint);
332: g2.fill(shadowArc);
333: }
334: }
335: else if (currentPass == 1) {
336:
337: Paint paint = getSectionPaint(section);
338: g2.setPaint(paint);
339: g2.fill(path);
340: Paint outlinePaint = getSectionOutlinePaint(section);
341: Stroke outlineStroke = getSectionOutlineStroke(section);
342: if (outlinePaint != null && outlineStroke != null) {
343: g2.setPaint(outlinePaint);
344: g2.setStroke(outlineStroke);
345: g2.draw(path);
346: }
347:
348: if (this.separatorsVisible) {
349: Line2D extendedSeparator = extendLine(
350: separator, this.innerSeparatorExtension,
351: this.innerSeparatorExtension
352: );
353: g2.setStroke(this.separatorStroke);
354: g2.setPaint(this.separatorPaint);
355: g2.draw(extendedSeparator);
356: }
357:
358:
359: if (state.getInfo() != null) {
360: EntityCollection entities = state.getEntityCollection();
361: if (entities != null) {
362: Comparable key = dataset.getKey(section);
363: String tip = null;
364: PieToolTipGenerator toolTipGenerator
365: = getToolTipGenerator();
366: if (toolTipGenerator != null) {
367: tip = toolTipGenerator.generateToolTip(
368: dataset, key
369: );
370: }
371: String url = null;
372: PieURLGenerator urlGenerator = getURLGenerator();
373: if (urlGenerator != null) {
374: url = urlGenerator.generateURL(
375: dataset, key, getPieIndex()
376: );
377: }
378: PieSectionEntity entity = new PieSectionEntity(
379: path, dataset, getPieIndex(), section, key, tip, url
380: );
381: entities.add(entity);
382: }
383: }
384: }
385: }
386: state.setLatestAngle(angle2);
387: }
388:
389:
396: public boolean equals(Object obj) {
397: if (this == obj) {
398: return true;
399: }
400: if (!(obj instanceof RingPlot)) {
401: return false;
402: }
403: if (!super.equals(obj)) {
404: return false;
405: }
406: RingPlot that = (RingPlot) obj;
407: if (this.separatorsVisible != that.separatorsVisible) {
408: return false;
409: }
410: if (!ObjectUtilities.equal(
411: this.separatorStroke, that.separatorStroke
412: )) {
413: return false;
414: }
415: if (!PaintUtilities.equal(this.separatorPaint, that.separatorPaint)) {
416: return false;
417: }
418: if (this.innerSeparatorExtension != that.innerSeparatorExtension) {
419: return false;
420: }
421: if (this.outerSeparatorExtension != that.outerSeparatorExtension) {
422: return false;
423: }
424: return true;
425: }
426:
427:
437: private Line2D extendLine(Line2D line, double startPercent,
438: double endPercent) {
439: if (line == null) {
440: throw new IllegalArgumentException("Null 'line' argument.");
441: }
442: double x1 = line.getX1();
443: double x2 = line.getX2();
444: double deltaX = x2 - x1;
445: double y1 = line.getY1();
446: double y2 = line.getY2();
447: double deltaY = y2 - y1;
448: x1 = x1 - (startPercent * deltaX);
449: y1 = y1 - (startPercent * deltaY);
450: x2 = x2 + (endPercent * deltaX);
451: y2 = y2 + (endPercent * deltaY);
452: return new Line2D.Double(x1, y1, x2, y2);
453: }
454:
455:
462: private void writeObject(ObjectOutputStream stream) throws IOException {
463: stream.defaultWriteObject();
464: SerialUtilities.writeStroke(this.separatorStroke, stream);
465: SerialUtilities.writePaint(this.separatorPaint, stream);
466: }
467:
468:
476: private void readObject(ObjectInputStream stream)
477: throws IOException, ClassNotFoundException {
478: stream.defaultReadObject();
479: this.separatorStroke = SerialUtilities.readStroke(stream);
480: this.separatorPaint = SerialUtilities.readPaint(stream);
481: }
482:
483: }