Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, 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-2007, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes 36: * ------- 37: * 08-Mar-2004 : Version 1 (DG); 38: * 18-Apr-2005 : Use StringBuffer (DG); 39: * 14-Jun-2007 : Now extends AbstractPieLabelDistributor (DG); 40: * 41: */ 42: 43: package org.jfree.chart.plot; 44: 45: import java.util.Collections; 46: 47: /** 48: * This class distributes the section labels for one side of a pie chart so 49: * that they do not overlap. 50: */ 51: public class PieLabelDistributor extends AbstractPieLabelDistributor { 52: 53: /** The minimum gap. */ 54: private double minGap = 4.0; 55: 56: /** 57: * Creates a new distributor. 58: * 59: * @param labelCount the number of labels (ignored). 60: */ 61: public PieLabelDistributor(int labelCount) { 62: super(); 63: } 64: 65: /** 66: * Distributes the labels. 67: * 68: * @param minY the minimum y-coordinate in Java2D-space. 69: * @param height the available height (in Java2D units). 70: */ 71: public void distributeLabels(double minY, double height) { 72: sort(); // sorts the label records into ascending order by baseY 73: if (isOverlap()) { 74: adjustInwards(); 75: } 76: 77: // if still overlapping, do something else... 78: if (isOverlap()) { 79: adjustDownwards(minY, height); 80: } 81: 82: if (isOverlap()) { 83: adjustUpwards(minY, height); 84: } 85: 86: if (isOverlap()) { 87: spreadEvenly(minY, height); 88: } 89: 90: } 91: 92: /** 93: * Returns <code>true</code> if there are overlapping labels in the list, 94: * and <code>false</code> otherwise. 95: * 96: * @return A boolean. 97: */ 98: private boolean isOverlap() { 99: double y = 0.0; 100: for (int i = 0; i < this.labels.size(); i++) { 101: PieLabelRecord plr = getPieLabelRecord(i); 102: if (y > plr.getLowerY()) { 103: return true; 104: } 105: y = plr.getUpperY(); 106: } 107: return false; 108: } 109: 110: /** 111: * Adjusts the y-coordinate for the labels in towards the center in an 112: * attempt to fix overlapping. 113: */ 114: protected void adjustInwards() { 115: int lower = 0; 116: int upper = this.labels.size() - 1; 117: while (upper > lower) { 118: if (lower < upper - 1) { 119: PieLabelRecord r0 = getPieLabelRecord(lower); 120: PieLabelRecord r1 = getPieLabelRecord(lower + 1); 121: if (r1.getLowerY() < r0.getUpperY()) { 122: double adjust = r0.getUpperY() - r1.getLowerY() 123: + this.minGap; 124: r1.setAllocatedY(r1.getAllocatedY() + adjust); 125: } 126: } 127: PieLabelRecord r2 = getPieLabelRecord(upper - 1); 128: PieLabelRecord r3 = getPieLabelRecord(upper); 129: if (r2.getUpperY() > r3.getLowerY()) { 130: double adjust = (r2.getUpperY() - r3.getLowerY()) + this.minGap; 131: r3.setAllocatedY(r3.getAllocatedY() + adjust); 132: } 133: lower++; 134: upper--; 135: } 136: } 137: 138: /** 139: * Any labels that are overlapping are moved down in an attempt to 140: * eliminate the overlaps. 141: * 142: * @param minY the minimum y value (in Java2D coordinate space). 143: * @param height the height available for all labels. 144: */ 145: protected void adjustDownwards(double minY, double height) { 146: for (int i = 0; i < this.labels.size() - 1; i++) { 147: PieLabelRecord record0 = getPieLabelRecord(i); 148: PieLabelRecord record1 = getPieLabelRecord(i + 1); 149: if (record1.getLowerY() < record0.getUpperY()) { 150: record1.setAllocatedY(Math.min(minY + height, 151: record0.getUpperY() + this.minGap 152: + record1.getLabelHeight() / 2.0)); 153: } 154: } 155: } 156: 157: /** 158: * Any labels that are overlapping are moved up in an attempt to eliminate 159: * the overlaps. 160: * 161: * @param minY the minimum y value (in Java2D coordinate space). 162: * @param height the height available for all labels. 163: */ 164: protected void adjustUpwards(double minY, double height) { 165: for (int i = this.labels.size() - 1; i > 0; i--) { 166: PieLabelRecord record0 = getPieLabelRecord(i); 167: PieLabelRecord record1 = getPieLabelRecord(i - 1); 168: if (record1.getUpperY() > record0.getLowerY()) { 169: record1.setAllocatedY(Math.max(minY, record0.getLowerY() 170: - this.minGap - record1.getLabelHeight() / 2.0)); 171: } 172: } 173: } 174: 175: /** 176: * Labels are spaced evenly in the available space in an attempt to 177: * eliminate the overlaps. 178: * 179: * @param minY the minimum y value (in Java2D coordinate space). 180: * @param height the height available for all labels. 181: */ 182: protected void spreadEvenly(double minY, double height) { 183: double y = minY; 184: double sumOfLabelHeights = 0.0; 185: for (int i = 0; i < this.labels.size(); i++) { 186: sumOfLabelHeights += getPieLabelRecord(i).getLabelHeight(); 187: } 188: double gap = Math.max(0, height - sumOfLabelHeights); 189: if (this.labels.size() > 1) { 190: gap = gap / (this.labels.size() - 1); 191: } 192: for (int i = 0; i < this.labels.size(); i++) { 193: PieLabelRecord record = getPieLabelRecord(i); 194: y = y + record.getLabelHeight() / 2.0; 195: record.setAllocatedY(y); 196: y = y + record.getLabelHeight() / 2.0 + gap; 197: } 198: } 199: 200: /** 201: * Sorts the label records into ascending order by y-value. 202: */ 203: public void sort() { 204: Collections.sort(this.labels); 205: } 206: 207: /** 208: * Returns a string containing a description of the object for 209: * debugging purposes. 210: * 211: * @return A string. 212: */ 213: public String toString() { 214: StringBuffer result = new StringBuffer(); 215: for (int i = 0; i < this.labels.size(); i++) { 216: result.append(getPieLabelRecord(i).toString()).append("\n"); 217: } 218: return result.toString(); 219: } 220: 221: }