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 }