C# 从直线到用户输入图形路径的最长距离
C# longest distance from a straight line to a user input graphicpath
我有一个由 PointF 数组定义的 GraphicsPath(通过鼠标单击创建),它由 Graphics.DrawCurve 绘制。然后将路径转换为将起点和终点旋转到 0 度。变换和旋转的曲线(和原始曲线)PointF 数组仍然可用。
我需要找到从路径的直线端点到曲线本身的最长垂直距离。
transformed output example
因为我已经旋转了曲线,找到最长的距离应该与边界的高度相同......但我还需要知道沿着(现在是什么)0轴的距离是(如果我能找到曲线,这将很容易)。我进行了广泛的搜索-我尝试了自定义样条函数来尝试沿曲线获得更多固定点,但这最终以相同的结果结束-我不知道这些点之间的曲线是什么,那就是给定点之间的插值,90% 的可能性是这种情况。
现在它已经旋转了,我尝试了一个 for 循环来计算顶部,并且在每一列中,尝试查看每个点是否相对于转换后的路径可见,但它总是 returns 错误.
输入点数(供参考):{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);
}
我唯一能想到的另一件事是基于边界创建一个新的位图,重新绘制曲线,然后逐列获取像素颜色,直到它与曲线的颜色相匹配绘制。
希望有人有比这更好的解决方案。
Flatten()
GraphicsPath,然后遍历结果点并使用标准 "Point to Line Distance" 测量找到最大距离:
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
}
此功能应全部包含在一个 class 中,以便您可以创建和编辑图形路径并根据需要获取它们的属性。
我有一个由 PointF 数组定义的 GraphicsPath(通过鼠标单击创建),它由 Graphics.DrawCurve 绘制。然后将路径转换为将起点和终点旋转到 0 度。变换和旋转的曲线(和原始曲线)PointF 数组仍然可用。
我需要找到从路径的直线端点到曲线本身的最长垂直距离。
transformed output example
因为我已经旋转了曲线,找到最长的距离应该与边界的高度相同......但我还需要知道沿着(现在是什么)0轴的距离是(如果我能找到曲线,这将很容易)。我进行了广泛的搜索-我尝试了自定义样条函数来尝试沿曲线获得更多固定点,但这最终以相同的结果结束-我不知道这些点之间的曲线是什么,那就是给定点之间的插值,90% 的可能性是这种情况。
现在它已经旋转了,我尝试了一个 for 循环来计算顶部,并且在每一列中,尝试查看每个点是否相对于转换后的路径可见,但它总是 returns 错误.
输入点数(供参考):{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);
}
我唯一能想到的另一件事是基于边界创建一个新的位图,重新绘制曲线,然后逐列获取像素颜色,直到它与曲线的颜色相匹配绘制。
希望有人有比这更好的解决方案。
Flatten()
GraphicsPath,然后遍历结果点并使用标准 "Point to Line Distance" 测量找到最大距离:
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
}
此功能应全部包含在一个 class 中,以便您可以创建和编辑图形路径并根据需要获取它们的属性。