2013-07-18 39 views
1

目前我的軟件在MSCharts中使用圖表對象的HitTest()方法,但是隨着我擴展到我的圖表上越來越多的數據點以及其他因素,這可能會導致巨大的性能下降。HitTest的替代方案()

我想知道是否有任何替代方案,你知道提供相同的功能(獲得X座標在圖表上的光標位置),但沒有性能打擊,因爲命中測試似乎是一種非常蠻力的方式獲得我的答案。

我的圖表從類System.Windows.Forms.DataVisualization.Charting.Chart

編輯爲清楚起見發佈:我要找到我的圖上的線的位置,以將其用於其他計算。

+0

HitTest()函數只能用於鼠標事件處理代碼。這在人類的時間運行,燃燒幾十毫秒不是問題。如果花費比這更長的時間,那麼你只是將太多的數據填入圖表中,遠遠超過它所能顯示的細節。因此請更好地過濾數據。 –

+0

圖表需要大量數據,HitTest用於確定光標旁邊顯示的圖表信息,以便用戶可以在圖表上運行鼠標,並在鼠標位置有效查看準確的數據。雖然解決辦法可能是取一組平均數據點而不是使用這麼多,但是精度的損失並不理想。 –

+0

鼠標只有像素精度。所以你不需要比屏幕上的像素更多的數據點。還要考慮對於您的預期功能意味着什麼,用戶永遠不會看到圖中重疊的兩個點的數據。最後但並非最不重要的是,您可以輕鬆確保數據點已排序,從而允許二分搜索定位數據點。 O(log n)算法非常快。 –

回答

2

與mousewheel事件具有相同的性能問題。

這裏是我的解決方案:

  1. 要獲取當前鼠標位置的座標軸值:

    double posX = Math.Round(currentArea.AxisX.PixelPositionToValue(e.X)); 
    double posY = Math.Round(currentArea.AxisY.PixelPositionToValue(e.Y)); 
    

    Showing Mouse Axis Coordinates on Chart Control採取一點點的變化,以獲得更準確。

    但你應該檢查之前,鼠標處於ChartArea,否則它會拋出你一個異常

  2. 要獲得ChatElement上鼠標點:

    // Gets the ChartArea that the mouse points 
    private ChartArea mouseinChartArea(Chart source, Point e) 
    { 
        double relativeX = (double)e.X * 100/source.Width; 
        double relativeY = (double)e.Y * 100/source.Height; 
    
        foreach (ChartArea ca in source.ChartAreas) 
        { 
         if (relativeX > ca.Position.X && relativeX < ca.Position.Right && 
          relativeY > ca.Position.Y && relativeY < ca.Position.Bottom) 
          return ca; 
        } 
        return null; 
    } 
    
    // for my purpose, returns an axis. But you can return anything 
    private Axis findAxisforZooming(Chart source, Point e) 
    { 
        ChartArea currentArea = mouseinChartArea(source, new Point(e.X, e.Y)); // Check if inside 
        if (currentArea == null) 
         return null; 
    
        double axisXfontSize = currentArea.AxisX.LabelAutoFitMinFontSize + ((double)source.Width/SystemInformation.PrimaryMonitorSize.Width) 
         * (currentArea.AxisX.LabelAutoFitMaxFontSize - currentArea.AxisX.LabelAutoFitMinFontSize); 
        double axisYfontSize = currentArea.AxisY.LabelAutoFitMinFontSize + ((double)source.Height/SystemInformation.PrimaryMonitorSize.Height) 
         * (currentArea.AxisY.LabelAutoFitMaxFontSize - currentArea.AxisY.LabelAutoFitMinFontSize); 
        double axisYfontHeightSize = (axisYfontSize - currentArea.AxisY.LabelStyle.Font.Size) + currentArea.AxisY.LabelStyle.Font.Height; 
    
        Graphics g = this.CreateGraphics(); 
        if (currentArea.AxisX.LabelStyle.Font.Unit == GraphicsUnit.Point) 
         axisXfontSize = axisXfontSize * g.DpiX/72; 
        if (currentArea.AxisY.LabelStyle.Font.Unit == GraphicsUnit.Point) 
         axisYfontHeightSize = axisYfontHeightSize * g.DpiX/72; 
        g.Dispose(); 
    
        // Replacing the SystemInformation.PrimaryMonitorSize with the source.Width/Height will give the accurate TickMarks size. 
        // But it doens't count for the gab between the tickMarks and the axis lables (so by replacing, it give a good proximity with the gab) 
        int axisYTickMarks = (int)Math.Round(currentArea.AxisY.MajorTickMark.Size/100 * SystemInformation.PrimaryMonitorSize.Width); // source.Width; 
        int axisXTickMarks = (int)Math.Round(currentArea.AxisX.MajorTickMark.Size/100 * SystemInformation.PrimaryMonitorSize.Height); // source.Height; 
    
        int leftInnerPlot = (int)Math.Round(currentArea.Position.X/100 * source.Width + 
         currentArea.InnerPlotPosition.X/100 * currentArea.Position.Width/100 * source.Width); 
        int rightInnerPlot = (int)Math.Round(currentArea.Position.X/100 * this.chart1.Width + 
         currentArea.InnerPlotPosition.Right/100 * currentArea.Position.Width/100 * source.Width); 
        int topInnerPlot = (int)Math.Round(currentArea.Position.Y/100 * this.chart1.Height + 
         currentArea.InnerPlotPosition.Y/100 * currentArea.Position.Height/100 * source.Height); 
        int bottomInnerPlot = (int)Math.Round(currentArea.Position.Y/100 * source.Height + 
         currentArea.InnerPlotPosition.Bottom/100 * currentArea.Position.Height/100 * source.Height); 
    
        // Now you got the boundaries of every important ChartElement. 
        // Only left to check if the mouse is within your desire ChartElement, 
        // like the following:  
    
        bottomInnerPlot += axisXTickMarks + (int)Math.Round(axisXfontSize); // Include AxisX 
    
        if (e.X > leftInnerPlot && e.X < rightInnerPlot && 
         e.Y > topInnerPlot && e.Y < bottomInnerPlot) // return AxisX if inside the InnerPlot area or on AxisX 
         return currentArea.AxisX; 
        else if (e.X > (leftInnerPlot - axisYTickMarks - (int)Math.Round(axisYfontHeightSize)) && e.X < rightInnerPlot && 
          e.Y > topInnerPlot && e.Y < bottomInnerPlot) // return AxisY if on AxisY only 
         return currentArea.AxisY; 
    
        return null; 
    } 
    

如可以看出,該代碼比HitTest()更長。但運行時間爲較短