gpt4 book ai didi

c# - 计算沿直线 A-B 与 A 的给定距离处的点

转载 作者:太空狗 更新时间:2023-10-29 17:27:59 27 4
gpt4 key购买 nike

我非常疯狂地试图计算沿给定线 A-B 的点,与 A 的给定距离,以便我可以“绘制”两个给定点之间的线。一开始听起来很简单,但我似乎做对了。更糟糕的是,我不明白我哪里做错了。几何(和一般数学)不是我的强项。

我已经阅读了类似的问题,并且在 SO 上有答案。事实上,我直接从 Mads ElvheimGiven a start and end point, and a distance, calculate a point along a line 的回答中提取了我当前的 CalculatePoint 函数实现。 (加上后来的评论中的更正 - 如果我理解正确的话)因为我独立尝试解决问题除了头等舱特快票 frusterpationland 之外没有任何进展。

这是我的更新代码(请参阅文章底部的编辑注释):

using System;
using System.Drawing;
using System.Windows.Forms;

namespace DrawLines
{
public class MainForm : Form
{
// =====================================================================
// Here's the part I'm having trouble with. I don't really understand
// how this is suposed to work, so I can't seem to get it right!
// ---------------------------------------------------------------------

// A "local indirector" - Just so I don't have go down and edit the
// actual call everytime this bluddy thing changes names.
private Point CalculatePoint(Point a, Point b, int distance) {
return CalculatePoint_ByAgentFire(a, b, distance);
}

#region CalculatePoint_ByAgentFire
//AgentFire: Better approach (you can rename the struct if you need):
struct Vector2
{
public readonly double X;
public readonly double Y;
public Vector2(double x, double y) {
this.X = x;
this.Y = y;
}
public static Vector2 operator -(Vector2 a, Vector2 b) {
return new Vector2(b.X - a.X, b.Y - a.Y);
}
public static Vector2 operator *(Vector2 a, double d) {
return new Vector2(a.X * d, a.Y * d);
}
public override string ToString() {
return string.Format("[{0}, {1}]", X, Y);
}
}
// For getting the midpoint you just need to do the (a - b) * d action:
//static void Main(string[] args)
//{
// Vector2 a = new Vector2(1, 1);
// Vector2 b = new Vector2(3, 1);
// float distance = 0.5f; // From 0.0 to 1.0.
// Vector2 c = (a - b) * distance;
// Console.WriteLine(c);
//}
private Point CalculatePoint_ByAgentFire(Point a, Point b, int distance) {
var vA = new Vector2(a.X, a.Y);
var vB = new Vector2(b.X, b.Y);
double lengthOfHypotenuse = LengthOfHypotenuseAsDouble(a,b);
double portionOfDistanceFromAtoB = distance / lengthOfHypotenuse;
var vC = (vA - vB) * portionOfDistanceFromAtoB;
Console.WriteLine("vC="+vC);
return new Point((int)(vC.X+0.5), (int)(vC.Y+0.5));
}
// Returns the length of the hypotenuse rounded to an integer, using
// Pythagoras' Theorem for right angle triangles: The length of the
// hypotenuse equals the sum of the square of the other two sides.
// Ergo: h = Sqrt(a*a + b*b)
private double LengthOfHypotenuseAsDouble(Point a, Point b) {
double aSq = Math.Pow(Math.Abs(a.X - b.X), 2); // horizontal length squared
double bSq = Math.Pow(Math.Abs(b.Y - b.Y), 2); // vertical length squared
return Math.Sqrt(aSq + bSq); // length of the hypotenuse
}

#endregion

//dbaseman: I thought something looked strange about the formula ... the question
//you linked was how to get the point at a distance after B, whereas you want the
//distance after A. This should give you the right answer, the start point plus
//distance in the vector direction.
//
// Didn't work as per: http://s1264.photobucket.com/albums/jj496/corlettk/?action=view&current=DrawLinesAB-broken_zps069161e9.jpg
//
private Point CalculatePoint_ByDbaseman(Point a, Point b, int distance) {
// a. calculate the vector from a to b:
double vectorX = b.X - a.X;
double vectorY = b.Y - a.Y;
// b. calculate the length:
double magnitude = Math.Sqrt(vectorX * vectorX + vectorY * vectorY);
// c. normalize the vector to unit length:
vectorX /= magnitude;
vectorY /= magnitude;
// d. calculate and Draw the new vector, which is x1y1 + vxvy * (mag + distance).
return new Point(
(int)((double)a.X + vectorX * distance) // x = col
, (int)((double)a.Y + vectorY * distance) // y = row
);
}

// MBo: Try to remove 'magnitude' term in the parentheses both for X and for Y expressions.
//
// Didn't work as per: http://s1264.photobucket.com/albums/jj496/corlettk/?action=view&current=DrawLinesAB-broken_zps069161e9.jpg
//
//private Point CalculatePoint_ByMBo(Point a, Point b, int distance) {
// // a. calculate the vector from a to b:
// double vectorX = b.X - a.X;
// double vectorY = b.Y - a.Y;
// // b. calculate the length:
// double magnitude = Math.Sqrt(vectorX * vectorX + vectorY * vectorY);
// // c. normalize the vector to unit length:
// vectorX /= magnitude;
// vectorY /= magnitude;
// // d. calculate and Draw the new vector, which is x1y1 + vxvy * (mag + distance).
// return new Point(
// (int)( ((double)a.X + vectorX * distance) + 0.5 )
// , (int)( ((double)a.X + vectorX * distance) + 0.5 )
// );
//}

// Didn't work
//private Point CalculatePoint_ByUser1556110(Point a, Point b, int distance) {
// Double magnitude = Math.Sqrt(Math.Pow(b.Y - a.Y, 2) + Math.Pow(b.X - a.X, 2));
// return new Point(
// (int)(a.X + distance * (b.X - a.X) / magnitude + 0.5)
// , (int)(a.Y + distance * (b.Y - a.Y) / magnitude + 0.5)
// );
//}

// didn't work
//private static Point CalculatePoint_ByCadairIdris(Point a, Point b, int distance) {
// // a. calculate the vector from a to b:
// double vectorX = b.X - a.X;
// double vectorY = b.Y - a.Y;
// // b. calculate the proportion of hypotenuse
// double factor = distance / Math.Sqrt(vectorX*vectorX + vectorY*vectorY);
// // c. factor the lengths
// vectorX *= factor;
// vectorY *= factor;
// // d. calculate and Draw the new vector,
// return new Point((int)(a.X + vectorX), (int)(a.Y + vectorY));
//}

// Returns a point along the line A-B at the given distance from A
// based on Mads Elvheim's answer to:
// https://stackoverflow.com/questions/1800138/given-a-start-and-end-point-and-a-distance-calculate-a-point-along-a-line
private Point MyCalculatePoint(Point a, Point b, int distance) {
// a. calculate the vector from o to g:
double vectorX = b.X - a.X;
double vectorY = b.Y - a.Y;
// b. calculate the length:
double magnitude = Math.Sqrt(vectorX * vectorX + vectorY * vectorY);
// c. normalize the vector to unit length:
vectorX /= magnitude;
vectorY /= magnitude;
// d. calculate and Draw the new vector, which is x1y1 + vxvy * (mag + distance).
return new Point(
(int)(((double)a.X + vectorX * (magnitude + distance)) + 0.5) // x = col
, (int)(((double)a.Y + vectorY * (magnitude + distance)) + 0.5) // y = row
);
}

// =====================================================================

private const int CELL_SIZE = 4; // width and height of each "cell" in the bitmap.

private readonly Bitmap _bitmap; // to draw on (displayed in picBox1).
private readonly Graphics _graphics; // to draw with.

// actual points on _theLineString are painted red.
private static readonly SolidBrush _thePointBrush = new SolidBrush(Color.Red);
// ... and are labeled in Red, Courier New, 12 point, Bold
private static readonly SolidBrush _theLabelBrush = new SolidBrush(Color.Red);
private static readonly Font _theLabelFont = new Font("Courier New", 12, FontStyle.Bold);

// the interveening calculated cells on the lines between actaul points are painted Black.
private static readonly SolidBrush _theLineBrush = new SolidBrush(Color.Black);

// the points in my line-string.
private static readonly Point[] _theLineString = new Point[] {
// x, y
new Point(170, 85), // A
new Point( 85, 70), // B
//new Point(209, 66), // C
//new Point( 98, 120), // D
//new Point(158, 19), // E
//new Point( 2, 61), // F
//new Point( 42, 177), // G
//new Point(191, 146), // H
//new Point( 25, 128), // I
//new Point( 95, 24) // J
};

public MainForm() {
InitializeComponent();
// initialise "the graphics system".
_bitmap = new Bitmap(picBox1.Width, picBox1.Height);
_graphics = Graphics.FromImage(_bitmap);
picBox1.Image = _bitmap;
}

#region actual drawing on the Grpahics

private void DrawCell(int x, int y, Brush brush) {
_graphics.FillRectangle(
brush
, x * CELL_SIZE, y * CELL_SIZE // x, y
, CELL_SIZE, CELL_SIZE // width, heigth
);
}

private void DrawLabel(int x, int y, char c) {
string s = c.ToString();
_graphics.DrawString(
s, _theLabelFont, _theLabelBrush
, x * CELL_SIZE + 5 // x
, y * CELL_SIZE - 8 // y
);
}

// ... there should be no mention of _graphics or CELL_SIZE below here ...

#endregion

#region draw points on form load

private void MainForm_Load(object sender, EventArgs e) {
DrawPoints();
}

// draws and labels each point in _theLineString
private void DrawPoints() {
char c = 'A'; // label text, as a char so we can increment it for each point.
foreach ( Point p in _theLineString ) {
DrawCell(p.X, p.Y, _thePointBrush);
DrawLabel(p.X, p.Y, c++);
}
}

#endregion

#region DrawLines on button click

private void btnDrawLines_Click(object sender, EventArgs e) {
DrawLinesBetweenPointsInTheString();
}

// Draws "the lines" between the points in _theLineString.
private void DrawLinesBetweenPointsInTheString() {
int n = _theLineString.Length - 1; // one less line-segment than points
for ( int i = 0; i < n; ++i )
Draw(_theLineString[i], _theLineString[i + 1]);
picBox1.Invalidate(); // tell the graphics system that the picture box needs to be repainted.
}

// Draws all the cells along the line from Point "a" to Point "b".
private void Draw(Point a, Point b) {
int maxDistance = LengthOfHypotenuse(a, b);
for ( int distance = 1; distance < maxDistance; ++distance ) {
var point = CalculatePoint(a, b, distance);
DrawCell(point.X, point.X, _theLineBrush);
}
}

// Returns the length of the hypotenuse rounded to an integer, using
// Pythagoras' Theorem for right angle triangles: The length of the
// hypotenuse equals the sum of the square of the other two sides.
// Ergo: h = Sqrt(a*a + b*b)
private int LengthOfHypotenuse(Point a, Point b) {
double aSq = Math.Pow(Math.Abs(a.X - b.X), 2); // horizontal length squared
double bSq = Math.Pow(Math.Abs(b.Y - b.Y), 2); // vertical length squared
return (int)(Math.Sqrt(aSq + bSq) + 0.5); // length of the hypotenuse
}

#endregion

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.picBox1 = new System.Windows.Forms.PictureBox();
this.btnDrawLines = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.picBox1)).BeginInit();
this.SuspendLayout();
//
// picBox1
//
this.picBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.picBox1.Location = new System.Drawing.Point(0, 0);
this.picBox1.Name = "picBox1";
this.picBox1.Size = new System.Drawing.Size(1000, 719);
this.picBox1.TabIndex = 0;
this.picBox1.TabStop = false;
//
// btnDrawLines
//
this.btnDrawLines.Location = new System.Drawing.Point(23, 24);
this.btnDrawLines.Name = "btnDrawLines";
this.btnDrawLines.Size = new System.Drawing.Size(77, 23);
this.btnDrawLines.TabIndex = 1;
this.btnDrawLines.Text = "Draw Lines";
this.btnDrawLines.UseVisualStyleBackColor = true;
this.btnDrawLines.Click += new System.EventHandler(this.btnDrawLines_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1000, 719);
this.Controls.Add(this.btnDrawLines);
this.Controls.Add(this.picBox1);
this.Location = new System.Drawing.Point(10, 10);
this.MinimumSize = new System.Drawing.Size(1016, 755);
this.Name = "MainForm";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Draw Lines on a Matrix.";
this.Load += new System.EventHandler(this.MainForm_Load);
((System.ComponentModel.ISupportInitialize)(this.picBox1)).EndInit();
this.ResumeLayout(false);
}

private System.Windows.Forms.PictureBox picBox1;
private System.Windows.Forms.Button btnDrawLines;
#endregion
}

}

对不起,如果它有点长,但这是 SSCCE从我的真实项目中挖掘出来,它是 A* shortest route algorithm 的一个实现运行 MazeOfBolton ...即迷宫赛跑者。

我真正想做的是预先计算迷宫(矩阵)中两个给定点(起点和目标)周围的“围栏”(即缓冲 MBR ),这样“围栏”内的所有点” 与“两点之间的直线”的给定距离内,以便快速消除远离目标的数十万条可能路径。

请注意,这个编程挑战在几年前就结束了,所以这里不存在“竞争性抄袭”问题。不,这不是家庭作业,事实上我是一名专业程序员......我只是在我的舒适区之外,即使是相对简单的几何图形。叹息。

所以...请任何人给我任何指示,帮助我正确获取 CalculatePoint 函数:计算沿 A-B 线与 A 的给定距离处的点?

在此先感谢您的慷慨...甚至阅读到这里为止。

干杯。基思。


编辑:我刚刚更新了发布的源代码,因为:

(1) 我刚刚意识到它不是独立的。我忘记了单独的 MainForm.Designer.cs 文件,我已将其附加到已发布代码的底部。

(2) 最新版本包括我迄今为止尝试过的内容,带有指向每个失败情况的照片的照片桶链接……而且它们都是一样的。咦?什么鬼?

我想我的问题可能出在其他地方,比如以前其他人都错过了一些时髦的 Windows 窗体设置,因为我忘记发布设计器生成的代码...除了其他一切(在我的实际项目中)完全符合我的预期它是,那么为什么计算点应该有任何不同。我不知道!?!?!?我非常沮丧,而且我变得暴躁,所以我想改天再说 ;-)

表明我们通常低估了让计算机做任何事情需要付出的努力……即使只是画一条简单的线……它甚至都不是曲线,更不用说大圆或横向了墨卡托或任何奇特的东西......只是一个简单的模糊线!?!?!? ;-)

非常感谢所有发帖的人!

再次欢呼。基思。

最佳答案

计算向量AB

首先定义从点 A(1,-1) 到点 B(2,4) 从 B 减去 A 的向量。向量将是 Vab(1,5)。

计算AB的长度

利用勾股定理计算向量AB的长度。

|Vab| = SQRT(1²+5²)

长度为(四舍五入)5.1

计算单位向量

将向量除以长度得到单位向量(长度为1的向量)。

V1(1/5.1,5/5.1) = V1(0.2, 0.98)

计算长度为4的向量

现在将 V1 乘以您想要的长度,例如 4,得到 Vt。

Vt(0.2*4,0.98*4) = Vt(0.8,3.92)

计算目标点

将向量 Vt 添加到点 A 得到点 T(目标)。

T = A + Vt = T(1.8,2.92)

编辑:对您的编辑的回答

LengthOfHypotenuse 方法应该是这样的

  • 修正了计算 bSq 时的错误
  • 并删除了多余的 Math.Abs​​ 调用,因为 pow 为 2 始终为正数
  • 删除了 0.5 的加法,不知道你为什么需要它
  • 你至少应该使用一个 float 作为返回值(double 或 decimal 也可以)

    //You should work with Vector2 class instead of Point and use their Length property
    private double LengthOfHypotenuse(Point a, Point b) {
    double aSq = Math.Pow(a.X - b.X, 2); // horizontal length squared
    double bSq = Math.Pow(a.Y - b.Y, 2); // vertical length squared
    return Math.Sqrt(aSq + bSq); // length of the hypotenuse
    }

方法 Draw(Point a, Point b) 应该是这样的:

  • 更正了 DrawCell() 调用

    private void Draw(Point a, Point b) {
    double maxDistance = LengthOfHypotenuse(a, b);
    for (int distance = 0; distance < maxDistance; ++distance) {
    var point = CalculatePoint(new Vector2(a), new Vector2(b), distance);
    DrawCell(point.X, point.Y, _theLineBrush);
    }
    }

您的 CalculatePoint(Point a, Point b, int distance) 方法:

  • 将一些计算移到 Vector2 类中

    private Point CalculatePoint(Vector2 a, Vector2 b, int distance) {
    Vector2 vectorAB = a - b;

    return a + vectorAB.UnitVector * distance;
    }

我已经为您扩展了 Vector 类以添加缺少的运算符(感谢 AgentFire)

    //AgentFire: Better approach (you can rename the struct if you need):
struct Vector2 {
public readonly double X;
public readonly double Y;
public Vector2(Point p) : this(p.X,p.Y) {
}

public Vector2(double x, double y) {
this.X = x;
this.Y = y;
}
public static Vector2 operator -(Vector2 a, Vector2 b) {
return new Vector2(b.X - a.X, b.Y - a.Y);
}
public static Vector2 operator +(Vector2 a, Vector2 b) {
return new Vector2(b.X + a.X, b.Y + a.Y);
}
public static Vector2 operator *(Vector2 a, double d) {
return new Vector2(a.X * d, a.Y * d);
}
public static Vector2 operator /(Vector2 a, double d) {
return new Vector2(a.X / d, a.Y / d);
}

public static implicit operator Point(Vector2 a) {
return new Point((int)a.X, (int)a.Y);
}

public Vector2 UnitVector {
get { return this / Length; }
}

public double Length {
get {
double aSq = Math.Pow(X, 2);
double bSq = Math.Pow(Y, 2);
return Math.Sqrt(aSq + bSq);
}
}

public override string ToString() {
return string.Format("[{0}, {1}]", X, Y);
}
}

关于c# - 计算沿直线 A-B 与 A 的给定距离处的点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12550365/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com