且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

从X值获取整个.NET图表系列的Y值

更新时间:2023-01-24 23:37:45

(这是对代码请求的回应,该代码是在给定像素坐标的情况下查找最接近的值.)

(This is in response to the request for code to look up the nearest value given a pixel coord.)

我的操作与您的操作有所不同,因为实际上是在用户移动鼠标时设置图表的光标",但是希望这会为您提供足够的信息,以使其适应您的需求. ..

I'm doing it a bit differently from you, because I'm actually setting the chart's "cursor" as the user moves the mouse around, but hopefully this will give you enough information for you to adapt it to your needs...

这是我为客户的X坐标计算X轴坐标的方法:

Here's how I calculate the X axis coord for a client X coord:

private double calcCursorGraphX(int clientX)
{
    var xAxis = _chart.ChartAreas[CHART_INDEX].AxisX;
    int xRight = (int) xAxis.ValueToPixelPosition(xAxis.Maximum) - 1;
    int xLeft = (int) xAxis.ValueToPixelPosition(xAxis.Minimum);

    if (clientX > xRight)
    {
        return xAxis.Maximum;
    }
    else if (clientX < xLeft)
    {
        return xAxis.Minimum;
    }
    else
    {
        return xAxis.PixelPositionToValue(clientX);
    }
}

鉴于上述方法返回的X值,我们可以查找最接近的前一个值:

Given an X value returned from the above method, we can look up the nearest preceeding value:

private int nearestPreceedingValue(double x)
{
    var bpData  = _chart.Series[SERIES_INDEX].Points;
    int bpIndex = bpData.BinarySearch(x, (xVal, point) => Math.Sign(x - point.XValue));

    if (bpIndex < 0)
    {
        bpIndex = ~bpIndex;                // BinarySearch() returns the index of the next element LARGER than the target.
        bpIndex = Math.Max(0, bpIndex-1);  // We want the value of the previous element, so we must decrement the returned index.
    }                                      // If this is before the start of the graph, use the first valid data point.

    return bpIndex;
}

然后您就有一个索引,可用于从_chart.Series[SERIES_INDEX].Points

Then you have an index which you can use to look up the value from _chart.Series[SERIES_INDEX].Points

我不确定这是否适合您的数据在图表中的存储方式,但这就是我的方法.

I'm not sure if this fits with the way that your data is stored in the charts, but that's how I do it.

这是缺少的BinarySearch扩展方法.将其添加到可访问的静态类中.如果您不使用代码合同,则用自己的错误处理替换合同"内容.

Here's the missing BinarySearch extension method. Add it to a static class somewhere accessible. Replace the "Contracts" stuff with your own error handling if you're not using Code Contracts.

/// <summary>
/// Searches the entire sorted IList{T} for an element using the specified comparer 
/// and returns the zero-based index of the element.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <typeparam name="TSearch">The type of the searched item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value with the list items.</param>
/// <returns>
/// The zero-based index of item in the sorted IList{T}, if item is found; 
/// otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item,
/// or - if there is no larger element - the bitwise complement of Count.
/// </returns>

public static int BinarySearch<TItem, TSearch>(this IList<TItem> list, TSearch value, Func<TSearch, TItem, int> comparer)
{
    Contract.Requires(list != null);
    Contract.Requires(comparer != null);

    int lower = 0;
    int upper = list.Count - 1;

    while (lower <= upper)
    {
        int middle = lower + (upper - lower) / 2;
        int comparisonResult = comparer(value, list[middle]);

        if (comparisonResult < 0)
        {
            upper = middle - 1;
        }
        else if (comparisonResult > 0)
        {
            lower = middle + 1;
        }
        else
        {
            return middle;
        }
    }

    return ~lower;
}