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