2017-02-19 2 views
0

J'ai un GraphicsPath défini par un tableau PointF (créé par des clics de souris), qui est dessiné par Graphics.DrawCurve. Le chemin est ensuite transformé pour faire pivoter les points de début et de fin à 0 degré. La courbe transformée et tournée (et la courbe d'origine) Les tableaux PointF sont toujours disponibles.C# distance la plus longue d'une ligne droite à une entrée graphique userpath

J'ai besoin de trouver la distance perpendiculaire la plus longue entre les points d'extrémité de la ligne droite du trajet et la courbe elle-même.

transformed output example

Etant donné que je l'ai fait pivoter la courbe, trouver la plus longue distance devrait être la même que la hauteur des limites ... Mais je dois aussi savoir quelle distance le long (ce qui est maintenant) la 0 axe est (ce qui sera facile si je peux trouver la courbe). J'ai cherché loin - j'ai essayé des fonctions splines personnalisées pour essayer d'obtenir plus de points fixes le long de la courbe, mais cela s'est finalement terminé avec le même résultat - je ne sais pas quelle est la courbe entre les points, et c'est 90% de chances d'être le cas étant donné l'interpolation entre les points.

Maintenant que vous l'avez fait tourner, j'ai essayé de faire une boucle for en haut, et dans chaque colonne, j'ai essayé de voir si chaque point vers le bas IsVisible sur le chemin transformé, mais il retourne toujours false.

points d'entrée (pour ref): {X = 499, Y = 64} {X = 305, Y = 117} {X = 149, Y = 114}

(De gauche à droite, je sais Je dois faire quelques vérifications pour voir de quelle manière ils sont entrés dans l'avenir, mais pour l'instant, juste essayer de le faire fonctionner)

GraphicsPath gP = new GraphicsPath(); 
gP.AddCurve(lPoints.ToArray()); 

Graphics g = pbImage.CreateGraphics(); 
g.SmoothingMode = SmoothingMode.AntiAlias; 

//draws original curve (hiding for now to validate and visualize rotation: 
//g.DrawPath(new Pen(Color.Blue, 1.75f), gP); 
//g.DrawLine(new Pen(Color.Red, 1.75f), lPoints[0], lPoints[lPoints.Count - 1]); 

// get angle in radians 
double theta2 = Math.Atan2(lPoints[0].Y - lPoints[lPoints.Count - 1].Y, lPoints[0].X - lPoints[lPoints.Count - 1].X); 

// convert radians to degrees 
double angle = -1 * (theta2 * (180.0/Math.PI)); 

//set a new path to the old one, to keep both sets of data 
GraphicsPath newPath = gP; 

//create rotational matrix, and transform 
Matrix m = new Matrix(); 
m.RotateAt(Convert.ToSingle(angle), lPoints[0]); 
newPath.Transform(m); 

//draw transformed path to picture box 
g.DrawPath(new Pen(Color.Green, 1f), newPath); 

//get new points from transformed curve (interestingly enough, it adds more points than the oringial input) 
PointF[] tP = newPath.PathPoints; 

//create some temp variables to make the next section easier 
PointF pS = tP[0]; 
PointF pE = tP[tP.Length - 1]; 

//draw the straight line for visual validation 
g.DrawLine(new Pen(Color.Red, 1f), pS, pE); 

//get the bounds of the new path 
RectangleF bRect = newPath.GetBounds(); 
a 
// loop through x's 
for (float i = pE.X; i < pS.X; i = i + 5) 
{ 
    float h = pS.Y - bRect.Y; 
    bool bFound = false; 

    // loop through y's - do loop until found 
    do 
    { 
     if (newPath.IsVisible(i, h, g)) 
     { 
      // never found 
      tbOutPt.Text = tbOutPt.Text + "found!!!!"; 
      bFound = true; 
     } 
     h++; 
    } while (bFound = false && h < bRect.Height); 

} 

la seule autre chose que je peux penser, serait de créer une nouvelle bitmap basé sur les limites, redessinez la courbe, et allez colonne par colonne et obtenez la couleur de pixel jusqu'à ce qu'elle corresponde à la couleur dans laquelle la courbe a été dessinée.

En espérant que quelqu'un a une meilleure solution que cela.

Répondre

0

Flatten() le GraphicsPath, puis marcher points résultants et trouver la plus grande distance en utilisant la norme "Point à la ligne Distance" mesure:

private float PointToLineDist(float Px, float Py, float Ax, float Ay, float Bx, float By) 
{ 
    float q = 0; 
    if ((Ax == Bx) & (Ay == By)) { 
     // A and B passed in define a point, not a line. 
     // Point to Point Distance 
     return PointToPointDist(Px, Py, Ax, Ay); 
    } else { 
     // Distance is the length of the line needed to connect the point to 
     // the(segment)such that the two lines would be perpendicular. 

     // q is the parameterized value needed to get to the intersection 
     q = ((Px - Ax) * (Bx - Ax) + (Py - Ay) * (By - Ay))/((Bx - Ax) * (Bx - Ax) + (By - Ay) * (By - Ay)); 

     // Limit q to 0 <= q <= 1 
     // If q is outside this range then the Point is somewhere past the 
     // endpoints of our segment. By setting q = 0 or q = 1 we are 
     // measuring the actual distacne from the point to one of the 
     // endpoints(instead) 
     if (q < 0) 
      q = 0; 
     if (q > 1) 
      q = 1; 

     // Distance 
     return PointToPointDist(Px, Py, (1 - q) * Ax + q * Bx, (1 - q) * Ay + q * By); 
    } 
} 

private float PointToPointDist(float Ax, float Ay, float Bx, float By) 
{ 
    // PointToPointDist = SquareRoot((Bx - Ax)^2 + (By - Ay)^2) 
    return Math.Sqrt((Bx - Ax) * (Bx - Ax) + (By - Ay) * (By - Ay)); 
} 

En PointToLineDist(), (Px, Py) est le point en question, et (Ax, Ay), (Bx, By) sont les points d'extrémité du segment de ligne.

+0

Merci pour la réponse - Pour tranquillité d'esprit, j'ai tracé les points de sortie de la méthode Flatten(), et je peux comprendre ce que ça fait maintenant. Cependant, je pense que je suis confus quant à quel point je devrais passer à vos méthodes. Ai-je besoin de faire pivoter le chemin, ou puis-je le laisser incliné en entrée par l'utilisateur? Semble que vous pouvez le laisser tourné? – user1778608

+0

Vous pouvez le laisser incliné, la rotation est sans importance. –

+0

l'a obtenu, donc px et py, sont le résultat de l'aplatissement, et l'ax, ay, bx, par sont l'emplacement de début et de fin de la ligne. Je vous remercie beaucoup pour votre aide. Je n'étais vraiment pas sûr d'avoir une réponse pour ça. – user1778608

0

Voici le code qui en résulte pour ceux à l'avenir:

private void processArray() 
{ 

// this is for demo only - real points should come from mouse input, or otherwise 
PointF[] inputArray = new PointF[2]; 

inputArray[0] = new PointF(537, 147); 
inputArray[1] = new PointF(334, 180); 
inputArray[2] = new PointF(150, 167); 

GraphicsPath gP = new GraphicsPath(); 
gP.AddCurve(inputArray); 

Graphics g = pbImage.CreateGraphics(); 
g.SmoothingMode = SmoothingMode.AntiAlias; 

//draws original curve 
g.DrawPath(new Pen(Color.Blue, 1.75f), gP); 

// draw a straight line between the ends of the curve 
//g.DrawLine(new Pen(Color.Red, 1.75f), lPoints[0], lPoints[lPoints.Count - 1]); 

// create second path to flatten 
GraphicsPath pathFlat = gP; 
pathFlat.Flatten(); 

// get list of points to step through 
PointF[] fP = pathFlat.PathPoints; 

//variables to store max distance 
float maxDistance = 0; 
PointF maxDistP = new PointF(0, 0); 

foreach (PointF p in fP) 
{ 
    // get the distance from the point to the closet point on the line segment 
    float curDist = PointToLineDist(p.X, p.Y, lPoints[0].X, lPoints[0].Y, lPoints[lPoints.Count - 1].X, lPoints[lPoints.Count - 1].Y); 

    // check the value against and store the longest point 
    if (curDist > maxDistance) 
    { 
     maxDistance = curDist; 
     maxDistP = new PointF(p.X, p.Y); 
    } 
} 

// mark a dot at the longest point 
g.DrawRectangle(new Pen(Color.Red), new Rectangle(Convert.ToInt32(maxDistP.X), Convert.ToInt32(maxDistP.Y), 2, 2 
} 

Cette fonctionnalité devrait tous s'enveloppé dans une classe de sorte que vous pouvez créer et modifier les graphicpaths et obtenir leurs propriétés au besoin.