复制距离为 d 的路径的数学运算
Math to copy a path with a distance d
我对复制路径所涉及的数学有疑问。
假设我有这条路:
除黑色路径外,我还想要此路径的精确副本。我写了一个小的 C# 程序来计算两点之间的角度。根据角度,添加 X 或 Y 值的偏移量。
有点效果,这是结果:
如您所见,它并不那么漂亮。
现在,我真正的问题是:用于此的正确数学是什么?
希望有人知道答案,因为我有点卡在这个问题上了。
问候,
萨沙
代码:
void Plot(List<Point> points)
{
Graphics g = pictureBox.CreateGraphics();
g.Clear(Color.White);
for (int i = 0; i < points.Count - 1; i++)
{
g.DrawLine(Pens.Black, points[i], points[i + 1]);
}
List<Point> points2 = new List<Point>();
for (int i = 0; i < points.Count - 1; i++)
{
var angle = getAngleFromPoint(points[i], points[i + 1]);
Debug.WriteLine(angle);
if (angle < 180 && angle >= 135)
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
if (angle < 135 && angle >= 90)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET / 2, points[i].Y + OFFSET));
}
else
{
}
}
if (angle < 90 && angle >= 45)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
else
{
points2.Add(new Point(points[i].X + OFFSET, points[i].Y));
}
}
if (angle < 45 && angle >= 0)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
else
{
points2.Add(new Point(points[i].X + OFFSET, points[i].Y));
}
}
if (angle < 360 && angle >= 315)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X + OFFSET, points[i].Y));
}
else
{
points2.Add(new Point(points[i].X + 10, points[i].Y - OFFSET));
}
}
if (angle < 315 && angle >= 270)
{
points2.Add(new Point(points[i].X, points[i].Y - OFFSET));
}
if (angle < 270 && angle >= 225)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET / 2, points[i].Y - OFFSET));
}
else
{
}
}
if (angle < 225 && angle >= 180)
{
if (points[i].X < points[i + 1].X)
{
points2.Add(new Point(points[i].X, points[i].Y - OFFSET));
}
else
{
if (points[i].Y < points[i + 1].Y) // \
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
else
{
}
}
}
}
for (int i = 0; i < points2.Count - 1; i++)
{
g.DrawLine(Pens.Red, points2[i], points2[i + 1]);
}
}
我认为如果我减小角度(从 45 度步长到 30 度左右)我可以改进结果,但必须有更好的解决方案。
我想解决这个问题的一种方法是将它分成线对(即:三个点)
为这对直线中的每条直线找出平行线(距离为 d)。然后找到这些平行线相交的位置,为您提供新线上的点位置。
非常粗糙的伪代码:
points a, b, c
distance d
lineab = findLineParallelTo(line(a,b), d)
linebc = findLineParallelTo(line(b,c), d)
return intersect(lineab, linebc)
我实施了@Jack 的解决方案,效果很好:
public class Line
{
public PointF P { get; private set; }
public PointF Q { get; private set; }
public float Pitch
{
get; private set;
}
public Line()
{
}
public Line(float px, float py, float qx, float qy) : this(new PointF(px, py), new PointF(qx, qy))
{
}
public Line(PointF p, PointF q)
{
P = p;
Q = q;
}
#region Methods
/// <summary>
///
/// </summary>
public Line FindParallelLine(float distance)
{
float length = (float)Math.Sqrt((P.X - Q.X) * (P.X - Q.X) + (P.Y - Q.Y) * (P.Y - Q.Y));
// This is the second line
float px = P.X + distance * (Q.Y - P.Y) / length;
float qx = Q.X + distance * (Q.Y - P.Y) / length;
float py = P.Y + distance * (P.X - Q.X) / length;
float qy = Q.Y + distance * (P.X - Q.X) / length;
return new Line(px, py, qx, qy);
}
public override string ToString()
{
return string.Format("P({0}|{1}), Q({2}|{3}) - Pitch: {4}", P.X, P.Y, Q.X, Q.Y, Pitch);
}
#endregion
}
private PointF FindIntersection(Line a, Line b)
{
PointF A = a.P;
PointF B = a.Q;
PointF C = b.P;
PointF D = b.Q;
float dy1 = B.Y - A.Y;
float dx1 = B.X - A.X;
float dy2 = D.Y - C.Y;
float dx2 = D.X - C.X;
// Check whether the two line parallel.
if (dy1 * dx2 == dy2 * dx1)
{
return PointF.Empty;
}
else
{
float x = ((C.Y - A.Y) * dx1 * dx2 + dy1 * dx2 * A.X - dy2 * dx1 * C.X) / (dy1 * dx2 - dy2 * dx1);
float y = A.Y + (dy1 / dx1) * (x - A.X);
return new PointF(x, y);
}
}
private PointF FindIntersection(PointF a, PointF b, PointF c, float distance)
{
Line line1 = new Line(a, b);
Line line2 = new Line(b, c);
Line parallel = line1.FindParallelLine(distance);
Line parallel2 = line2.FindParallelLine(distance);
return FindIntersection(parallel, parallel2);
}
private List<PointF> FindIntersections(PointF[] points, float distance)
{
List<PointF> intersections = new List<PointF>();
for (int i = 0; i < points.Length - 2; i++)
{
PointF intersection = FindIntersection(points[i], points[i + 1], points[i + 2], distance);
if (!intersection.IsEmpty && !double.IsNaN(intersection.X) && !double.IsNaN(intersection.Y))
{
intersections.Add(intersection);
}
}
return intersections;
}
private PointF GetFirstPoint(PointF[] points, float distance)
{
Line parallel = new Line(points[0], points[1]).FindParallelLine(distance);
return parallel.P;
}
private PointF GetLastPoint(PointF[] points, float distance)
{
Line parallel = new Line(points[points.Length - 2], points[points.Length - 1]).FindParallelLine(distance);
return parallel.Q;
}
调用示例:
OFFSET = float.Parse(textBox1.Text);
List<PointF> points = new List<PointF>();
points.Add(new PointF(200, 180));
points.Add(new PointF(160, 160));
points.Add(new PointF(100, 160));
points.Add(new PointF(60, 140));
points.Add(new PointF(40, 100));
points.Add(new PointF(80, 60));
points.Add(new PointF(140, 100));
points.Add(new PointF(180, 140));
points.Add(new PointF(220, 80));
List<PointF> intersections = FindIntersections(points.ToArray(), OFFSET);
intersections.Insert(0, GetFirstPoint(points.ToArray(), OFFSET));
intersections.Add(GetLastPoint(points.ToArray(), OFFSET));
Graphics g = pictureBox.CreateGraphics();
g.Clear(Color.White);
g.DrawLines(Pens.Black, points.ToArray());
// Connect the intersection points.
g.DrawLines(Pens.Red, intersections.ToArray());
示例图片:
再次感谢@Jack!
我对复制路径所涉及的数学有疑问。 假设我有这条路:
除黑色路径外,我还想要此路径的精确副本。我写了一个小的 C# 程序来计算两点之间的角度。根据角度,添加 X 或 Y 值的偏移量。 有点效果,这是结果:
如您所见,它并不那么漂亮。 现在,我真正的问题是:用于此的正确数学是什么?
希望有人知道答案,因为我有点卡在这个问题上了。 问候, 萨沙
代码:
void Plot(List<Point> points)
{
Graphics g = pictureBox.CreateGraphics();
g.Clear(Color.White);
for (int i = 0; i < points.Count - 1; i++)
{
g.DrawLine(Pens.Black, points[i], points[i + 1]);
}
List<Point> points2 = new List<Point>();
for (int i = 0; i < points.Count - 1; i++)
{
var angle = getAngleFromPoint(points[i], points[i + 1]);
Debug.WriteLine(angle);
if (angle < 180 && angle >= 135)
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
if (angle < 135 && angle >= 90)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET / 2, points[i].Y + OFFSET));
}
else
{
}
}
if (angle < 90 && angle >= 45)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
else
{
points2.Add(new Point(points[i].X + OFFSET, points[i].Y));
}
}
if (angle < 45 && angle >= 0)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
else
{
points2.Add(new Point(points[i].X + OFFSET, points[i].Y));
}
}
if (angle < 360 && angle >= 315)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X + OFFSET, points[i].Y));
}
else
{
points2.Add(new Point(points[i].X + 10, points[i].Y - OFFSET));
}
}
if (angle < 315 && angle >= 270)
{
points2.Add(new Point(points[i].X, points[i].Y - OFFSET));
}
if (angle < 270 && angle >= 225)
{
if (points[i].Y < points[i + 1].Y)
{
points2.Add(new Point(points[i].X - OFFSET / 2, points[i].Y - OFFSET));
}
else
{
}
}
if (angle < 225 && angle >= 180)
{
if (points[i].X < points[i + 1].X)
{
points2.Add(new Point(points[i].X, points[i].Y - OFFSET));
}
else
{
if (points[i].Y < points[i + 1].Y) // \
{
points2.Add(new Point(points[i].X - OFFSET, points[i].Y));
}
else
{
}
}
}
}
for (int i = 0; i < points2.Count - 1; i++)
{
g.DrawLine(Pens.Red, points2[i], points2[i + 1]);
}
}
我认为如果我减小角度(从 45 度步长到 30 度左右)我可以改进结果,但必须有更好的解决方案。
我想解决这个问题的一种方法是将它分成线对(即:三个点)
为这对直线中的每条直线找出平行线(距离为 d)。然后找到这些平行线相交的位置,为您提供新线上的点位置。
非常粗糙的伪代码:
points a, b, c
distance d
lineab = findLineParallelTo(line(a,b), d)
linebc = findLineParallelTo(line(b,c), d)
return intersect(lineab, linebc)
我实施了@Jack 的解决方案,效果很好:
public class Line
{
public PointF P { get; private set; }
public PointF Q { get; private set; }
public float Pitch
{
get; private set;
}
public Line()
{
}
public Line(float px, float py, float qx, float qy) : this(new PointF(px, py), new PointF(qx, qy))
{
}
public Line(PointF p, PointF q)
{
P = p;
Q = q;
}
#region Methods
/// <summary>
///
/// </summary>
public Line FindParallelLine(float distance)
{
float length = (float)Math.Sqrt((P.X - Q.X) * (P.X - Q.X) + (P.Y - Q.Y) * (P.Y - Q.Y));
// This is the second line
float px = P.X + distance * (Q.Y - P.Y) / length;
float qx = Q.X + distance * (Q.Y - P.Y) / length;
float py = P.Y + distance * (P.X - Q.X) / length;
float qy = Q.Y + distance * (P.X - Q.X) / length;
return new Line(px, py, qx, qy);
}
public override string ToString()
{
return string.Format("P({0}|{1}), Q({2}|{3}) - Pitch: {4}", P.X, P.Y, Q.X, Q.Y, Pitch);
}
#endregion
}
private PointF FindIntersection(Line a, Line b)
{
PointF A = a.P;
PointF B = a.Q;
PointF C = b.P;
PointF D = b.Q;
float dy1 = B.Y - A.Y;
float dx1 = B.X - A.X;
float dy2 = D.Y - C.Y;
float dx2 = D.X - C.X;
// Check whether the two line parallel.
if (dy1 * dx2 == dy2 * dx1)
{
return PointF.Empty;
}
else
{
float x = ((C.Y - A.Y) * dx1 * dx2 + dy1 * dx2 * A.X - dy2 * dx1 * C.X) / (dy1 * dx2 - dy2 * dx1);
float y = A.Y + (dy1 / dx1) * (x - A.X);
return new PointF(x, y);
}
}
private PointF FindIntersection(PointF a, PointF b, PointF c, float distance)
{
Line line1 = new Line(a, b);
Line line2 = new Line(b, c);
Line parallel = line1.FindParallelLine(distance);
Line parallel2 = line2.FindParallelLine(distance);
return FindIntersection(parallel, parallel2);
}
private List<PointF> FindIntersections(PointF[] points, float distance)
{
List<PointF> intersections = new List<PointF>();
for (int i = 0; i < points.Length - 2; i++)
{
PointF intersection = FindIntersection(points[i], points[i + 1], points[i + 2], distance);
if (!intersection.IsEmpty && !double.IsNaN(intersection.X) && !double.IsNaN(intersection.Y))
{
intersections.Add(intersection);
}
}
return intersections;
}
private PointF GetFirstPoint(PointF[] points, float distance)
{
Line parallel = new Line(points[0], points[1]).FindParallelLine(distance);
return parallel.P;
}
private PointF GetLastPoint(PointF[] points, float distance)
{
Line parallel = new Line(points[points.Length - 2], points[points.Length - 1]).FindParallelLine(distance);
return parallel.Q;
}
调用示例:
OFFSET = float.Parse(textBox1.Text);
List<PointF> points = new List<PointF>();
points.Add(new PointF(200, 180));
points.Add(new PointF(160, 160));
points.Add(new PointF(100, 160));
points.Add(new PointF(60, 140));
points.Add(new PointF(40, 100));
points.Add(new PointF(80, 60));
points.Add(new PointF(140, 100));
points.Add(new PointF(180, 140));
points.Add(new PointF(220, 80));
List<PointF> intersections = FindIntersections(points.ToArray(), OFFSET);
intersections.Insert(0, GetFirstPoint(points.ToArray(), OFFSET));
intersections.Add(GetLastPoint(points.ToArray(), OFFSET));
Graphics g = pictureBox.CreateGraphics();
g.Clear(Color.White);
g.DrawLines(Pens.Black, points.ToArray());
// Connect the intersection points.
g.DrawLines(Pens.Red, intersections.ToArray());
示例图片:
再次感谢@Jack!