C# наибольшее расстояние от прямой линии до пользовательского ввода graphicpath

У меня есть GraphicsPath, определенный массивом PointF (созданным щелчками мыши), который рисуется Graphics.DrawCurve. Затем путь преобразуется для поворота начальной и конечной точек на 0 градусов. Преобразованная и повернутая кривая (и исходная кривая) массивы PointF по-прежнему доступны.

Мне нужно найти самое длинное перпендикулярное расстояние от конечных точек прямой линии до самой кривой.

пример преобразованного выхода

Поскольку я повернул кривую, нахождение наибольшего расстояния должно совпадать с высотой границ... Но мне также нужно знать, какое расстояние вдоль (что сейчас) оси 0 (что будет легко, если Я могу найти кривую). Я искал далеко и широко - я попробовал пользовательские функции сплайна, чтобы попытаться получить больше фиксированных точек вдоль кривой, но это в конечном итоге закончилось тем же результатом - я не знаю, какая кривая находится между точками, и это 90% вероятности, если учесть интерполяцию между точками.

Теперь, когда он повернут, я попытался использовать цикл for для подсчета в верхней части, и в каждом столбце пытался проверить, соответствует ли каждая точка вниз IsVisible преобразованному пути, но он всегда возвращает false.

Точки ввода (для ссылки): {X = 499, Y = 64} {X = 305, Y = 117} {X = 149, Y = 114}

(Слева направо, я знаю, что мне нужно сделать несколько проверок, чтобы увидеть, как они будут введены в будущем, но сейчас просто пытаюсь заставить это работать)

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

}

Единственное, о чем я могу подумать, - это создать новое растровое изображение на основе границ, перерисовать кривую и переходить от столбца к столбцу и получать цвет пикселя до тех пор, пока он не будет соответствовать цвету, на котором нарисована кривая.

Надеюсь, у кого-то есть лучшее решение, чем это.

2 ответа

Решение

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

В PointToLineDist(), (Px, Py) является рассматриваемой точкой, а (Ax, Ay), (Bx, By) являются конечными точками отрезка.

Вот результирующий код для тех, кто в будущем:

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
}

Эта функциональность должна быть заключена в класс, чтобы вы могли создавать и редактировать графические пути и получать их свойства по мере необходимости.

Другие вопросы по тегам