001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2009, 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 * RendererUtilities.java 029 * ---------------------- 030 * (C) Copyright 2007-2009, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 19-Apr-2007 : Version 1 (DG); 038 * 27-Mar-2009 : Fixed results for unsorted datasets (DG); 039 * 040 */ 041 042 package org.jfree.chart.renderer; 043 044 import org.jfree.data.DomainOrder; 045 import org.jfree.data.xy.XYDataset; 046 047 /** 048 * Utility methods related to the rendering process. 049 * 050 * @since 1.0.6 051 */ 052 public class RendererUtilities { 053 054 /** 055 * Finds the lower index of the range of live items in the specified data 056 * series. 057 * 058 * @param dataset the dataset (<code>null</code> not permitted). 059 * @param series the series index. 060 * @param xLow the lowest x-value in the live range. 061 * @param xHigh the highest x-value in the live range. 062 * 063 * @return The index of the required item. 064 * 065 * @since 1.0.6 066 * 067 * @see #findLiveItemsUpperBound(XYDataset, int, double, double) 068 */ 069 public static int findLiveItemsLowerBound(XYDataset dataset, int series, 070 double xLow, double xHigh) { 071 if (dataset == null) { 072 throw new IllegalArgumentException("Null 'dataset' argument."); 073 } 074 if (xLow >= xHigh) { 075 throw new IllegalArgumentException("Requires xLow < xHigh."); 076 } 077 int itemCount = dataset.getItemCount(series); 078 if (itemCount <= 1) { 079 return 0; 080 } 081 if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { 082 // for data in ascending order by x-value, we are (broadly) looking 083 // for the index of the highest x-value that is less than xLow 084 int low = 0; 085 int high = itemCount - 1; 086 double lowValue = dataset.getXValue(series, low); 087 if (lowValue >= xLow) { 088 // special case where the lowest x-value is >= xLow 089 return low; 090 } 091 double highValue = dataset.getXValue(series, high); 092 if (highValue < xLow) { 093 // special case where the highest x-value is < xLow 094 return high; 095 } 096 while (high - low > 1) { 097 int mid = (low + high) / 2; 098 double midV = dataset.getXValue(series, mid); 099 if (midV >= xLow) { 100 high = mid; 101 } 102 else { 103 low = mid; 104 } 105 } 106 return high; 107 } 108 else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { 109 // when the x-values are sorted in descending order, the lower 110 // bound is found by calculating relative to the xHigh value 111 int low = 0; 112 int high = itemCount - 1; 113 double lowValue = dataset.getXValue(series, low); 114 if (lowValue <= xHigh) { 115 return low; 116 } 117 double highValue = dataset.getXValue(series, high); 118 if (highValue > xHigh) { 119 return high; 120 } 121 while (high - low > 1) { 122 int mid = (low + high) / 2; 123 double midV = dataset.getXValue(series, mid); 124 if (midV > xHigh) { 125 low = mid; 126 } 127 else { 128 high = mid; 129 } 130 mid = (low + high) / 2; 131 } 132 return high; 133 } 134 else { 135 // we don't know anything about the ordering of the x-values, 136 // but we can still skip any initial values that fall outside the 137 // range... 138 int index = 0; 139 // skip any items that don't need including... 140 double x = dataset.getXValue(series, index); 141 while (index < itemCount && (x < xLow || x > xHigh)) { 142 index++; 143 if (index < itemCount) { 144 x = dataset.getXValue(series, index); 145 } 146 } 147 return Math.min(Math.max(0, index), itemCount - 1); 148 } 149 } 150 151 /** 152 * Finds the upper index of the range of live items in the specified data 153 * series. 154 * 155 * @param dataset the dataset (<code>null</code> not permitted). 156 * @param series the series index. 157 * @param xLow the lowest x-value in the live range. 158 * @param xHigh the highest x-value in the live range. 159 * 160 * @return The index of the required item. 161 * 162 * @since 1.0.6 163 * 164 * @see #findLiveItemsLowerBound(XYDataset, int, double, double) 165 */ 166 public static int findLiveItemsUpperBound(XYDataset dataset, int series, 167 double xLow, double xHigh) { 168 if (dataset == null) { 169 throw new IllegalArgumentException("Null 'dataset' argument."); 170 } 171 if (xLow >= xHigh) { 172 throw new IllegalArgumentException("Requires xLow < xHigh."); 173 } 174 int itemCount = dataset.getItemCount(series); 175 if (itemCount <= 1) { 176 return 0; 177 } 178 if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { 179 int low = 0; 180 int high = itemCount - 1; 181 double lowValue = dataset.getXValue(series, low); 182 if (lowValue > xHigh) { 183 return low; 184 } 185 double highValue = dataset.getXValue(series, high); 186 if (highValue <= xHigh) { 187 return high; 188 } 189 int mid = (low + high) / 2; 190 while (high - low > 1) { 191 double midV = dataset.getXValue(series, mid); 192 if (midV <= xHigh) { 193 low = mid; 194 } 195 else { 196 high = mid; 197 } 198 mid = (low + high) / 2; 199 } 200 return mid; 201 } 202 else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { 203 // when the x-values are descending, the upper bound is found by 204 // comparing against xLow 205 int low = 0; 206 int high = itemCount - 1; 207 int mid = (low + high) / 2; 208 double lowValue = dataset.getXValue(series, low); 209 if (lowValue < xLow) { 210 return low; 211 } 212 double highValue = dataset.getXValue(series, high); 213 if (highValue >= xLow) { 214 return high; 215 } 216 while (high - low > 1) { 217 double midV = dataset.getXValue(series, mid); 218 if (midV >= xLow) { 219 low = mid; 220 } 221 else { 222 high = mid; 223 } 224 mid = (low + high) / 2; 225 } 226 return mid; 227 } 228 else { 229 // we don't know anything about the ordering of the x-values, 230 // but we can still skip any trailing values that fall outside the 231 // range... 232 int index = itemCount - 1; 233 // skip any items that don't need including... 234 double x = dataset.getXValue(series, index); 235 while (index >= 0 && (x < xLow || x > xHigh)) { 236 index--; 237 if (index >= 0) { 238 x = dataset.getXValue(series, index); 239 } 240 } 241 return Math.max(index, 0); 242 } 243 } 244 245 /** 246 * Finds a range of item indices that is guaranteed to contain all the 247 * x-values from x0 to x1 (inclusive). 248 * 249 * @param dataset the dataset (<code>null</code> not permitted). 250 * @param series the series index. 251 * @param xLow the lower bound of the x-value range. 252 * @param xHigh the upper bound of the x-value range. 253 * 254 * @return The indices of the boundary items. 255 */ 256 public static int[] findLiveItems(XYDataset dataset, int series, 257 double xLow, double xHigh) { 258 // here we could probably be a little faster by searching for both 259 // indices simultaneously, but I'll look at that later if it seems 260 // like it matters... 261 int i0 = findLiveItemsLowerBound(dataset, series, xLow, xHigh); 262 int i1 = findLiveItemsUpperBound(dataset, series, xLow, xHigh); 263 if (i0 > i1) { 264 i0 = i1; 265 } 266 return new int[] {i0, i1}; 267 } 268 269 }