Bug Report: RendererUtilities.findLiveItems() w/ bar chart

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
Commander Salamander
Posts: 21
Joined: Sat Jan 05, 2008 2:14 am

Bug Report: RendererUtilities.findLiveItems() w/ bar chart

Post by Commander Salamander » Mon Aug 31, 2009 1:33 am

Hi,

Just wanted to let you know that I'm fairly sure there is a bug in RendererUtilities.findLiveItems(). When searching for live items, dataset.getXValue() is called. If the dataset is an XYIntervalSeriesCollection, this means that an item is only considered live if the x midpoint is in the plot (as opposed to including an item if any part of the x interval is within the viewable domain range).

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: Bug Report: RendererUtilities.findLiveItems() w/ bar chart

Post by david.gilbert » Mon Aug 31, 2009 8:37 pm

Thanks for the report. I think this is the same bug:

https://sourceforge.net/tracker/?func=d ... tid=115494

I'll look at it now...
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: Bug Report: RendererUtilities.findLiveItems() w/ bar chart

Post by david.gilbert » Mon Aug 31, 2009 9:53 pm

It's trickier than I thought...I'll need some more time on this one.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

equalyang
Posts: 2
Joined: Fri Apr 25, 2008 6:52 am

Re: Bug Report: RendererUtilities.findLiveItems() w/ bar chart

Post by equalyang » Mon Oct 11, 2010 6:15 pm

I really need this to be fixed so decided to try myself. Attached is the code I modified for the RendererUtilities. One thing to notice is that I use XYIntervalSeriesCollection and it always return dataset.getDomainOrder() = DomainOrder.NONE so I can only test the "else" part of the if statement. Anyway it works for me so far.

/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2009, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ----------------------
* RendererUtilities.java
* ----------------------
* (C) Copyright 2007-2009, by Object Refinery Limited.
*
* Original Author: David Gilbert (for Object Refinery Limited);
* Contributor(s): -;
*
* Changes
* -------
* 19-Apr-2007 : Version 1 (DG);
* 27-Mar-2009 : Fixed results for unsorted datasets (DG);
*
*/

package org.jfree.chart.renderer;

import org.jfree.data.DomainOrder;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYDataset;

/**
* Utility methods related to the rendering process.
*
* @since 1.0.6
*/
public class RendererUtilities {
/**
* Gets the start x value if isInterval is true otherwise
* return the x value.
* @return x value or start x value
* @author Yi-Kun Yang
*/
static private double getStartXValue(boolean isInterval, XYDataset dataset,
int series, int item) {
if (isInterval) {
return ((IntervalXYDataset) dataset).getStartXValue(series, item);
} else {
return dataset.getXValue(series, item);
}
}

/**
*
* @param isInterval
* @param dataset
* @param series
* @param item
* @return
* @author Yi-Kun Yang
*/
static private double getEndXValue(boolean isInterval, XYDataset dataset,
int series, int item) {
if (isInterval) {
return ((IntervalXYDataset) dataset).getEndXValue(series, item);
} else {
return dataset.getXValue(series, item);
}
}

/**
* Finds the lower index of the range of live items in the specified data
* series.
*
* @param dataset the dataset (<code>null</code> not permitted).
* @param series the series index.
* @param xLow the lowest x-value in the live range.
* @param xHigh the highest x-value in the live range.
*
* @return The index of the required item.
*
* @since 1.0.6
*
* @see #findLiveItemsUpperBound(XYDataset, int, double, double)
*/
public static int findLiveItemsLowerBound(XYDataset dataset, int series,
double xLow, double xHigh) {
if (dataset == null) {
throw new IllegalArgumentException("Null 'dataset' argument.");
}
if (xLow >= xHigh) {
throw new IllegalArgumentException("Requires xLow < xHigh.");
}
int itemCount = dataset.getItemCount(series);
if (itemCount <= 1) {
return 0;
}

boolean isInterval = (dataset instanceof IntervalXYDataset);

if (dataset.getDomainOrder() == DomainOrder.ASCENDING) {
// for data in ascending order by x-value, we are (broadly) looking
// for the index of the highest x-value that is less than xLow
int low = 0;
int high = itemCount - 1;

// double lowValue = dataset.getXValue(series, low);
double lowValue = getStartXValue(isInterval, dataset, series, low);// Modified
// by
// Yi-Kun
// Yang
if (lowValue >= xLow) {
// special case where the lowest x-value is >= xLow
return low;
}
// double highValue = dataset.getXValue(series, high);
double highValue = getStartXValue(isInterval, dataset, series, high);// Modified
// by
// Yi-Kun
// Yang
if (highValue < xLow) {
// special case where the highest x-value is < xLow
return high;
}
while (high - low > 1) {
int mid = (low + high) / 2;
// double midV = dataset.getXValue(series, mid);
double midV = getStartXValue(isInterval, dataset, series, mid);// Modified
// by
// Yi-Kun
// Yang
if (midV >= xLow) {
high = mid;
} else {
low = mid;
}
}
return high;
} else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) {
// when the x-values are sorted in descending order, the lower
// bound is found by calculating relative to the xHigh value
int low = 0;
int high = itemCount - 1;
// double lowValue = dataset.getXValue(series, low);
double lowValue = getStartXValue(isInterval, dataset, series, low);// Modified
// by
// Yi-Kun
// Yang
if (lowValue <= xHigh) {
return low;
}
// double highValue = dataset.getXValue(series, high);
double highValue = getStartXValue(isInterval, dataset, series, high);// Modified
// by
// Yi-Kun
// Yang
if (highValue > xHigh) {
return high;
}
while (high - low > 1) {
int mid = (low + high) / 2;
// double midV = dataset.getXValue(series, mid);
double midV = getStartXValue(isInterval, dataset, series, mid);// Modified
// by
// Yi-Kun
// Yang
if (midV > xHigh) {
low = mid;
} else {
high = mid;
}
mid = (low + high) / 2;
}
return high;
} else {
// we don't know anything about the ordering of the x-values,
// but we can still skip any initial values that fall outside the
// range...
int index = 0;
// skip any items that don't need including...
double x = dataset.getXValue(series, index);
// double x = getEndXValue(isInterval, dataset, series, index);
double xEnd = getEndXValue(isInterval, dataset, series, index); // Modified
// by
// Yi-Kun
// Yang
while (index < itemCount) { // Modified by Yi-Kun Yang
if (isInterval) { // Modified by Yi-Kun Yang
if (xEnd > xLow) {
break;
}
} else {
if (!(x < xLow || x > xHigh)) {
break;
}
}

index++;

if (index < itemCount) {
x = dataset.getXValue(series, index);
xEnd = getEndXValue(isInterval, dataset, series, index);// Modified by
// Yi-Kun Yang
}

}
return Math.min(Math.max(0, index), itemCount - 1);
}
}

/**
* Finds the upper index of the range of live items in the specified data
* series.
*
* @param dataset the dataset (<code>null</code> not permitted).
* @param series the series index.
* @param xLow the lowest x-value in the live range.
* @param xHigh the highest x-value in the live range.
*
* @return The index of the required item.
*
* @since 1.0.6
*
* @see #findLiveItemsLowerBound(XYDataset, int, double, double)
*/
public static int findLiveItemsUpperBound(XYDataset dataset, int series,
double xLow, double xHigh) {
if (dataset == null) {
throw new IllegalArgumentException("Null 'dataset' argument.");
}
if (xLow >= xHigh) {
throw new IllegalArgumentException("Requires xLow < xHigh.");
}
int itemCount = dataset.getItemCount(series);
if (itemCount <= 1) {
return 0;
}

boolean isInterval = (dataset instanceof IntervalXYDataset);

if (dataset.getDomainOrder() == DomainOrder.ASCENDING) {
int low = 0;
int high = itemCount - 1;
// double lowValue = dataset.getXValue(series, low);
double lowValue = getEndXValue(isInterval, dataset, series, low);// Modified
// by
// Yi-Kun
// Yang
if (lowValue > xHigh) {
return low;
}
// double highValue = dataset.getXValue(series, high);
double highValue = getEndXValue(isInterval, dataset, series, high);// Modified
// by
// Yi-Kun
// Yang
if (highValue <= xHigh) {
return high;
}
int mid = (low + high) / 2;
while (high - low > 1) {
// double midV = dataset.getXValue(series, mid);
double midV = getEndXValue(isInterval, dataset, series, mid);// Modified
// by
// Yi-Kun
// Yang
if (midV <= xHigh) {
low = mid;
} else {
high = mid;
}
mid = (low + high) / 2;
}
return mid;
} else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) {
// when the x-values are descending, the upper bound is found by
// comparing against xLow
int low = 0;
int high = itemCount - 1;
int mid = (low + high) / 2;
// double lowValue = dataset.getXValue(series, low);
double lowValue = getEndXValue(isInterval, dataset, series, low);// Modified
// by
// Yi-Kun
// Yang
if (lowValue < xLow) {
return low;
}
// double highValue = dataset.getXValue(series, high);
double highValue = getEndXValue(isInterval, dataset, series, high);// Modified
// by
// Yi-Kun
// Yang
if (highValue >= xLow) {
return high;
}
while (high - low > 1) {
// double midV = dataset.getXValue(series, mid);
double midV = getEndXValue(isInterval, dataset, series, mid);// Modified
// by
// Yi-Kun
// Yang
if (midV >= xLow) {
low = mid;
} else {
high = mid;
}
mid = (low + high) / 2;
}
return mid;
} else {
// we don't know anything about the ordering of the x-values,
// but we can still skip any trailing values that fall outside the
// range...
int index = itemCount - 1;
// skip any items that don't need including...
double x = dataset.getXValue(series, index);
double xStart = getStartXValue(isInterval, dataset, series, index);// Modified
// by
// Yi-Kun
// Yang
while (index >= 0) {// Modified by Yi-Kun Yang
if (isInterval) {// Modified by Yi-Kun Yang
if (xStart < xHigh) {
break;
}
} else {
if (!(x < xLow || x > xHigh)) {
break;
}
}

index--;
if (index >= 0) {
x = dataset.getXValue(series, index);
xStart = getStartXValue(isInterval, dataset, series, index);// Modified
// by
// Yi-Kun
// Yang
}
}
return Math.max(index, 0);
}
}

/**
* Finds a range of item indices that is guaranteed to contain all the
* x-values from x0 to x1 (inclusive).
*
* @param dataset the dataset (<code>null</code> not permitted).
* @param series the series index.
* @param xLow the lower bound of the x-value range.
* @param xHigh the upper bound of the x-value range.
*
* @return The indices of the boundary items.
*/
public static int[] findLiveItems(XYDataset dataset, int series, double xLow,
double xHigh) {
// here we could probably be a little faster by searching for both
// indices simultaneously, but I'll look at that later if it seems
// like it matters...
int i0 = findLiveItemsLowerBound(dataset, series, xLow, xHigh);
int i1 = findLiveItemsUpperBound(dataset, series, xLow, xHigh);
if (i0 > i1) {
i0 = i1;
}
return new int[] { i0, i1 };
}

}

Locked