简体   繁体   English

C#从直线到用户输入图形路径的最长距离

[英]C# longest distance from a straight line to a user input graphicpath

I have a GraphicsPath defined by a PointF array (created by mouse clicks), which is drawn by Graphics.DrawCurve. 我有一个由PointF数组(通过鼠标单击创建)定义的GraphicsPath,该数组由Graphics.DrawCurve绘制。 The Path is then Transformed to rotate the start and end points to 0 degrees. 然后变换路径以将起点和终点旋转到0度。 The transformed and rotated curve (and the original curve) PointF arrays are still available. 转换和旋转的曲线(以及原始曲线)PointF数组仍然可用。

I need to find the longest perpendicular distance from the path's straight line end points to the curve itself. 我需要找到从路径的直线端点到曲线本身的最长垂直距离。

transformed output example 转换后的输出示例

Being that I've rotated the curve, finding the longest distance should be the same as the height of the bounds... But I also need to know what distance along (what is now) the 0 axis is (which will be easy if I can find the curve). 因为我已经旋转了曲线,所以找到最长的距离应该与边界的高度相同...但是我还需要知道0轴沿(现在是)的距离(如果我可以找到曲线)。 I've searched far and wide- I've tried custom spline functions to try and get more fixed points along the curve, but this ultimately ended with the same result- I don't know what the curve is between the points, and that's 90% likely to be the case given the interpolation between the points. 我进行了广泛的搜索-我尝试了自定义样条函数来尝试沿曲线获取更多固定点,但是最终最终得到了相同的结果-我不知道这些点之间的曲线是什么,给定点之间的插值,很可能是90%。

Now that it's rotated, I've tried a for loop to count across the top, and in each column, tried to see if each point down IsVisible against the transformed path, but it always returns false. 现在它已经旋转了,我尝试了一个for循环以在顶部进行计数,并且在每一列中,尝试查看IsVisible下每个点是否与转换后的路径相对,但始终返回false。

Input points (for ref): {X=499,Y=64} {X=305,Y=117} {X=149,Y=114} 输入点(用于参考):{X = 499,Y = 64} {X = 305,Y = 117} {X = 149,Y = 114}

(From left to right, I know I need to do some checking to see which way they are entered in the future, but for now, just trying to make it work) (从左到右,我知道我需要做一些检查,以查看将来以哪种方式输入它们,但就目前而言,只是尝试使其起作用)

    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);

}

The only other thing I can think of, would be to create a new bitmap based on the bounds, redraw the curve to it, and go column by column and get the pixel color until it matches the color the curve was drawn in. 我唯一能想到的是基于边界创建一个新的位图,重新绘制曲线,然后逐列显示像素颜色,直到它与绘制曲线的颜色匹配为止。

Hoping someone has a better solution than that. 希望有人能提供更好的解决方案。

Flatten() the GraphicsPath, then walk the resulting points and find the largest distance using the standard "Point to Line Distance" measurement: Flatten() GraphicsPath,然后遍历所得的点,并使用标准的“点到线距离”测量找到最大距离:

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

In PointToLineDist() , (Px, Py) is the point in question, and (Ax, Ay), (Bx, By) are the endpoints of the line segment. PointToLineDist() ,(Px,Py)是所讨论的点,而(Ax,Ay),(Bx,By)是线段的端点。

Here is the resulting code for those in the future: 这是将来的结果代码:

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
}

This functionality should all get wrapped up into a class so you can create and edit the graphicpaths and get their properties as necessary. 此功能应该全部包装到一个类中,以便您可以创建和编辑图形路径并根据需要获取其属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM