KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > plot > PieLabelDistributor


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ------------------------
28  * PieLabelDistributor.java
29  * ------------------------
30  * (C) Copyright 2004, 2005, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: PieLabelDistributor.java,v 1.5.2.1 2005/10/25 20:52:08 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 08-Mar-2004 : Version 1 (DG);
40  * 18-Apr-2005 : Use StringBuffer (DG);
41  *
42  */

43
44 package org.jfree.chart.plot;
45
46 import java.util.ArrayList JavaDoc;
47 import java.util.Collections JavaDoc;
48 import java.util.List JavaDoc;
49
50 /**
51  * This class distributes the section labels for one side of a pie chart so
52  * that they do not overlap.
53  */

54 public class PieLabelDistributor {
55     
56     /** The label records. */
57     private List JavaDoc labels;
58     
59     /** The minimum gap. */
60     private double minGap = 4.0;
61     
62     /**
63      * Creates a new distributor.
64      *
65      * @param labelCount the number of labels.
66      */

67     public PieLabelDistributor(int labelCount) {
68         this.labels = new ArrayList JavaDoc(labelCount);
69     }
70     
71     /**
72      * Returns a label record from the list.
73      *
74      * @param index the index.
75      *
76      * @return The label record.
77      */

78     public PieLabelRecord getPieLabelRecord(int index) {
79         return (PieLabelRecord) this.labels.get(index);
80     }
81     
82     /**
83      * Adds a label record.
84      *
85      * @param record the label record.
86      */

87     public void addPieLabelRecord(PieLabelRecord record) {
88         this.labels.add(record);
89     }
90     
91     /**
92      * Returns the number of items in the list.
93      *
94      * @return The item count.
95      */

96     public int getItemCount() {
97         return this.labels.size();
98     }
99     
100     /**
101      * Distributes the labels.
102      *
103      * @param minY the minimum y-coordinate in Java2D-space.
104      * @param height the height.
105      */

106     public void distributeLabels(double minY, double height) {
107         sort();
108         if (isOverlap()) {
109             adjustInwards();
110         }
111         
112         // if still overlapping, do something else...
113
if (isOverlap()) {
114             adjustDownwards(minY, height);
115         }
116         
117         if (isOverlap()) {
118             adjustUpwards(minY, height);
119         }
120         
121         if (isOverlap()) {
122             spreadEvenly(minY, height);
123         }
124
125     }
126     
127     /**
128      * Returns <code>true</code> if there are overlapping labels in the list,
129      * and <code>false</code> otherwise.
130      *
131      * @return A boolean.
132      */

133     private boolean isOverlap() {
134         double y = 0.0;
135         for (int i = 0; i < this.labels.size(); i++) {
136             PieLabelRecord plr = getPieLabelRecord(i);
137             if (y > plr.getLowerY()) {
138                 return true;
139             }
140             y = plr.getUpperY();
141         }
142         return false;
143     }
144     
145     /**
146      * Adjusts the y-coordinate for the labels in towards the center in an
147      * attempt to fix overlapping.
148      */

149     protected void adjustInwards() {
150         int lower = 0;
151         int upper = this.labels.size() - 1;
152         while (upper > lower) {
153             if (lower < upper - 1) {
154                 PieLabelRecord r0 = getPieLabelRecord(lower);
155                 PieLabelRecord r1 = getPieLabelRecord(lower + 1);
156                 if (r1.getLowerY() < r0.getUpperY()) {
157                     double adjust = r0.getUpperY() - r1.getLowerY()
158                                     + this.minGap;
159                     r1.setAllocatedY(r1.getAllocatedY() + adjust);
160                 }
161             }
162             PieLabelRecord r2 = getPieLabelRecord(upper - 1);
163             PieLabelRecord r3 = getPieLabelRecord(upper);
164             if (r2.getUpperY() > r3.getLowerY()) {
165                 double adjust = (r2.getUpperY() - r3.getLowerY()) + this.minGap;
166                 r2.setAllocatedY(r2.getAllocatedY() - adjust);
167             }
168             lower++;
169             upper--;
170         }
171     }
172     
173     /**
174      * Any labels that are overlapping are moved down in an attempt to
175      * eliminate the overlaps.
176      *
177      * @param minY the minimum y value (in Java2D coordinate space).
178      * @param height the height available for all labels.
179      */

180     protected void adjustDownwards(double minY, double height) {
181         for (int i = 0; i < this.labels.size() - 1; i++) {
182             PieLabelRecord record0 = getPieLabelRecord(i);
183             PieLabelRecord record1 = getPieLabelRecord(i + 1);
184             if (record1.getLowerY() < record0.getUpperY()) {
185                 record1.setAllocatedY(
186                     Math.min(
187                         minY + height,
188                         record0.getUpperY() + this.minGap
189                         + record1.getLabelHeight() / 2.0
190                     )
191                 );
192             }
193         }
194     }
195
196     /**
197      * Any labels that are overlapping are moved up in an attempt to eliminate
198      * the overlaps.
199      *
200      * @param minY the minimum y value (in Java2D coordinate space).
201      * @param height the height available for all labels.
202      */

203     protected void adjustUpwards(double minY, double height) {
204         for (int i = this.labels.size() - 1; i > 0; i--) {
205             PieLabelRecord record0 = getPieLabelRecord(i);
206             PieLabelRecord record1 = getPieLabelRecord(i - 1);
207             if (record1.getUpperY() > record0.getLowerY()) {
208                 record1.setAllocatedY(
209                     Math.max(
210                         minY,
211                         record0.getLowerY() - this.minGap
212                         - record1.getLabelHeight() / 2.0
213                     )
214                 );
215             }
216         }
217     }
218
219     /**
220      * Labels are spaced evenly in the available space in an attempt to
221      * eliminate the overlaps.
222      *
223      * @param minY the minimum y value (in Java2D coordinate space).
224      * @param height the height available for all labels.
225      */

226     protected void spreadEvenly(double minY, double height) {
227         double y = minY;
228         double sumOfLabelHeights = 0.0;
229         for (int i = 0; i < this.labels.size(); i++) {
230             sumOfLabelHeights += getPieLabelRecord(i).getLabelHeight();
231         }
232         double gap = height - sumOfLabelHeights;
233         if (this.labels.size() > 1) {
234             gap = gap / (this.labels.size() - 1);
235         }
236         for (int i = 0; i < this.labels.size(); i++) {
237             PieLabelRecord record = getPieLabelRecord(i);
238             y = y + record.getLabelHeight() / 2.0;
239             record.setAllocatedY(y);
240             y = y + record.getLabelHeight() / 2.0 + gap;
241         }
242     }
243         
244     /**
245      * Sorts the label records into ascending order by y-value.
246      */

247     public void sort() {
248         Collections.sort(this.labels);
249     }
250     
251     /**
252      * Returns a string containing a description of the object for
253      * debugging purposes.
254      *
255      * @return A string.
256      */

257     public String JavaDoc toString() {
258         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
259         for (int i = 0; i < this.labels.size(); i++) {
260             result.append(getPieLabelRecord(i).toString()).append("\n");
261         }
262         return result.toString();
263     }
264     
265 }
266
Popular Tags