001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * ------------------------
028: * PieLabelDistributor.java
029: * ------------------------
030: * (C) Copyright 2004-2007, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: PieLabelDistributor.java,v 1.5.2.2 2007/06/14 15:04:21 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 08-Mar-2004 : Version 1 (DG);
040: * 18-Apr-2005 : Use StringBuffer (DG);
041: * 14-Jun-2007 : Now extends AbstractPieLabelDistributor (DG);
042: *
043: */
044:
045: package org.jfree.chart.plot;
046:
047: import java.util.Collections;
048:
049: /**
050: * This class distributes the section labels for one side of a pie chart so
051: * that they do not overlap.
052: */
053: public class PieLabelDistributor extends AbstractPieLabelDistributor {
054:
055: /** The minimum gap. */
056: private double minGap = 4.0;
057:
058: /**
059: * Creates a new distributor.
060: *
061: * @param labelCount the number of labels (ignored).
062: */
063: public PieLabelDistributor(int labelCount) {
064: super ();
065: }
066:
067: /**
068: * Distributes the labels.
069: *
070: * @param minY the minimum y-coordinate in Java2D-space.
071: * @param height the available height (in Java2D units).
072: */
073: public void distributeLabels(double minY, double height) {
074: sort(); // sorts the label records into ascending order by baseY
075: if (isOverlap()) {
076: adjustInwards();
077: }
078:
079: // if still overlapping, do something else...
080: if (isOverlap()) {
081: adjustDownwards(minY, height);
082: }
083:
084: if (isOverlap()) {
085: adjustUpwards(minY, height);
086: }
087:
088: if (isOverlap()) {
089: spreadEvenly(minY, height);
090: }
091:
092: }
093:
094: /**
095: * Returns <code>true</code> if there are overlapping labels in the list,
096: * and <code>false</code> otherwise.
097: *
098: * @return A boolean.
099: */
100: private boolean isOverlap() {
101: double y = 0.0;
102: for (int i = 0; i < this .labels.size(); i++) {
103: PieLabelRecord plr = getPieLabelRecord(i);
104: if (y > plr.getLowerY()) {
105: return true;
106: }
107: y = plr.getUpperY();
108: }
109: return false;
110: }
111:
112: /**
113: * Adjusts the y-coordinate for the labels in towards the center in an
114: * attempt to fix overlapping.
115: */
116: protected void adjustInwards() {
117: int lower = 0;
118: int upper = this .labels.size() - 1;
119: while (upper > lower) {
120: if (lower < upper - 1) {
121: PieLabelRecord r0 = getPieLabelRecord(lower);
122: PieLabelRecord r1 = getPieLabelRecord(lower + 1);
123: if (r1.getLowerY() < r0.getUpperY()) {
124: double adjust = r0.getUpperY() - r1.getLowerY()
125: + this .minGap;
126: r1.setAllocatedY(r1.getAllocatedY() + adjust);
127: }
128: }
129: PieLabelRecord r2 = getPieLabelRecord(upper - 1);
130: PieLabelRecord r3 = getPieLabelRecord(upper);
131: if (r2.getUpperY() > r3.getLowerY()) {
132: double adjust = (r2.getUpperY() - r3.getLowerY())
133: + this .minGap;
134: r3.setAllocatedY(r3.getAllocatedY() + adjust);
135: }
136: lower++;
137: upper--;
138: }
139: }
140:
141: /**
142: * Any labels that are overlapping are moved down in an attempt to
143: * eliminate the overlaps.
144: *
145: * @param minY the minimum y value (in Java2D coordinate space).
146: * @param height the height available for all labels.
147: */
148: protected void adjustDownwards(double minY, double height) {
149: for (int i = 0; i < this .labels.size() - 1; i++) {
150: PieLabelRecord record0 = getPieLabelRecord(i);
151: PieLabelRecord record1 = getPieLabelRecord(i + 1);
152: if (record1.getLowerY() < record0.getUpperY()) {
153: record1
154: .setAllocatedY(Math.min(minY + height, record0
155: .getUpperY()
156: + this .minGap
157: + record1.getLabelHeight() / 2.0));
158: }
159: }
160: }
161:
162: /**
163: * Any labels that are overlapping are moved up in an attempt to eliminate
164: * the overlaps.
165: *
166: * @param minY the minimum y value (in Java2D coordinate space).
167: * @param height the height available for all labels.
168: */
169: protected void adjustUpwards(double minY, double height) {
170: for (int i = this .labels.size() - 1; i > 0; i--) {
171: PieLabelRecord record0 = getPieLabelRecord(i);
172: PieLabelRecord record1 = getPieLabelRecord(i - 1);
173: if (record1.getUpperY() > record0.getLowerY()) {
174: record1
175: .setAllocatedY(Math.max(minY, record0
176: .getLowerY()
177: - this .minGap
178: - record1.getLabelHeight() / 2.0));
179: }
180: }
181: }
182:
183: /**
184: * Labels are spaced evenly in the available space in an attempt to
185: * eliminate the overlaps.
186: *
187: * @param minY the minimum y value (in Java2D coordinate space).
188: * @param height the height available for all labels.
189: */
190: protected void spreadEvenly(double minY, double height) {
191: double y = minY;
192: double sumOfLabelHeights = 0.0;
193: for (int i = 0; i < this .labels.size(); i++) {
194: sumOfLabelHeights += getPieLabelRecord(i).getLabelHeight();
195: }
196: double gap = Math.max(0, height - sumOfLabelHeights);
197: if (this .labels.size() > 1) {
198: gap = gap / (this .labels.size() - 1);
199: }
200: for (int i = 0; i < this .labels.size(); i++) {
201: PieLabelRecord record = getPieLabelRecord(i);
202: y = y + record.getLabelHeight() / 2.0;
203: record.setAllocatedY(y);
204: y = y + record.getLabelHeight() / 2.0 + gap;
205: }
206: }
207:
208: /**
209: * Sorts the label records into ascending order by y-value.
210: */
211: public void sort() {
212: Collections.sort(this .labels);
213: }
214:
215: /**
216: * Returns a string containing a description of the object for
217: * debugging purposes.
218: *
219: * @return A string.
220: */
221: public String toString() {
222: StringBuffer result = new StringBuffer();
223: for (int i = 0; i < this .labels.size(); i++) {
224: result.append(getPieLabelRecord(i).toString()).append("\n");
225: }
226: return result.toString();
227: }
228:
229: }
|