2013-07-18 6 views
1

Actuellement, mon logiciel utilise la méthode HitTest() d'un objet graphique dans MSCharts, mais en augmentant le nombre de points de données sur mon graphique, combiné avec d'autres facteurs, cela peut avoir un impact énorme sur les performances. Je me demandais s'il existe des alternatives que vous connaissez pour fournir la même fonctionnalité (obtenir la coordonnée X sur le graphique pour la position du curseur), mais sans le succès de la performance comme essai de coup semble être une façon très brute de obtenir ma réponse.Alternatives à la HitTest()

Mon tableau est créé à partir de la classe System.Windows.Forms.DataVisualization.Charting.Chart

Modifier pour plus de clarté: Je dois trouver la position d'une ligne sur mon graphique pour l'utiliser pour d'autres calculs.

+0

Une fonction HitTest() ne doit être utilisée que dans le code de gestion des événements souris. Cela tourne à l'heure humaine, brûler des dizaines de millisecondes n'est pas un problème. Si cela prend plus de temps que cela, alors vous bourrez beaucoup trop de données dans le graphique, beaucoup plus que ce qu'il pourrait afficher en détail. Donc, filtrez vos données mieux. –

+0

Le graphique nécessite la quantité de données, le HitTest est utilisé pour déterminer les informations de graphique affichées à côté du curseur afin que l'utilisateur peut y exécuter la souris le long du graphique et voir effectivement des données précises à leur emplacement de la souris. Bien que peut-être une solution serait de prendre un ensemble moyen de points de données au lieu d'en utiliser autant, mais cette perte de précision n'est pas idéale. –

+0

Une souris n'a qu'une précision de pixel. Vous n'avez donc jamais besoin de plus de points de données que de pixels sur l'écran. Considérez également ce que cela signifie pour votre fonctionnalité prévue, l'utilisateur ne peut jamais voir les données pour deux points qui se chevauchent dans le graphique. Last but not least, vous pouvez facilement vous assurer que les points de données sont triés, ce qui permet une recherche binaire pour localiser le point de données. Un algorithme O (log n), très rapide. –

Répondre

2

Avait le même problème de performance avec un événement mousewheel.

Voici ma solution:

  1. Pour obtenir les valeurs des axes de la position actuelle de la souris:

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

    Tiré de Showing Mouse Axis Coordinates on Chart Control avec un peu de changement pour obtenir plus précis.

    Mais vous devriez vérifier avant, que la souris est dans un ChartArea, sinon il vous jeter un Exception.

  2. Pour obtenir le ChatElement sur lequel les points de souris:

    // 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; 
    } 
    

Comme on peut le voir, le code est plus long que HitTest(). Mais le temps d'exécution est plus court.