C# 沿线 AB 计算距 A 给定距离处的点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12550365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 23:38:12  来源:igfitidea点击:

Calculate a point along the line A-B at a given distance from A

c#mathgraphicsgeometry

提问by corlettk

I'm going quite mad tring to calculate the point along the given line A-B, at a given distance from A, so that I can "draw" the line between two given points. It sounded simple enough at the outset, but I can't seem to get it right. Worse still, I don't understand where I've gone wrong. Geometry (and math in general) is NOT my strong suite.

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

I have read similar questions and there answers on SO. In fact I lifted my current implementation of CalculatePoint function directly from Mads Elvheim's answer to: Given a start and end point, and a distance, calculate a point along a line(plus a correction in a later comment - if I understand him correctly) because my indepedent attempts to solve the problem were getting me nowhere, except a first class express ticket frusterpationland.

我已经阅读过类似的问题,并且有关于 SO 的答案。事实上,我直接从Mads Elvheim的回答中提升了我当前的 CalculatePoint 函数实现:给定起点和终点以及距离,计算沿线的一个点(加上后面评论中的更正 - 如果我理解正确的话) 因为我解决问题的独立尝试使我无处可去,除了头等舱特快票 frusterpationland。

Here's my UPDATEDcode (please see the EDIT notes a bottom of post):

这是我更新后的代码(请参阅帖子底部的编辑说明):

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
    }

}

Sorry if it's a bit long, but this an is SSCCEexhumed from my real project, which is an implementation of the A* shortest route algorithmto run the MazeOfBolton... i.e. a maze runner.

抱歉,如果它有点长,但这是从我的真实项目中挖掘出来的SSCCE,这是运行MazeOfBoltonA* 最短路线算法实现......即迷宫赛跑者。

What I actually want to do is pre-calculate a "fence" (i.e. a buffered MBR) around two given points (origin and goal) in the maze (a matrix), such that all points within the "fence" are within a given distance from "the straight line between the two points", in order to quickly eliminate the hundreds-of-thousands of possible paths which are heading away from the goal.

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

Note that this programming challenge closed years ago, so there's no issue with "competitive plagerism" here. No this is not homework, in fact I'm a professional programmer... I'm just WAAAAY out of my comfort zone here, even with relatively simple geometry. Sigh.

请注意,此编程挑战已于多年前结束,因此此处不存在“竞争性剽窃”问题。不,这不是家庭作业,事实上我是一名专业的程序员......我只是远离我的舒适区,即使是相对简单的几何图形。叹。

So... Please can anyone give me any pointers to help me get the CalculatePoint function to correctly: Calculate a point along the line A-B at the given distance from A?

所以......请谁能给我任何指示来帮助我正确地获得CalculatePoint函数:在距A的给定距离处沿着AB线计算一个点?

Thanks in advance for your generosity... even in reading this far.

在此先感谢您的慷慨……即使阅读到此为止。

Cheers. Keith.

干杯。基思。



EDIT:I just updated the posted source code becuase:

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

(1) I just realised that it wasn't self contained. I forgot about the seperate MainForm.Designer.cs file, which I've appended to the bottom of the posted code.

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

(2) The latest version includes what I've tried so far, with a photobucket link to a picture of what each failure looks like... and they're all the same. Huy? WTF?

(2) 最新版本包括我迄今为止尝试过的内容,带有指向每个失败情况的图片的 photobucket 链接……而且它们都是相同的。咦?跆拳道?

I suppose my problem may be elsewhere, like some funky windows form setting that was previously missed by everyone else because I forgot to post the designer-generated code... Except everythingelse (in my actual project) paints exactly where I expect it to, so why should a calculated point be any different. I don't know!?!?!? I'm pretty frustrated and I'm getting cranky, so I think I'll leave this for another day ;-)

我想我的问题可能出在其他地方,比如以前因为我忘记发布设计者生成的代码而被其他人忽略的一些时髦的窗口窗体设置......那么为什么计算点应该有所不同。我不知道!?!?!?我很沮丧,我变得暴躁,所以我想我会再留一天;-)

Goes to show how much we routinely underestimate how much effort it'll take to make a computer do ANYthing... even just draw a simple line... it's not even a curve, let alone a great circle or a transverse mercator or anything fancy... just a simple bluddy line!?!?!? ;-)

去展示我们通常低估了多少努力让计算机做任何事情......即使只是画一条简单的线......它甚至不是一条曲线,更不用说一个大圆或横向墨卡托或任何东西花哨......只是一个简单的bluddy台词!?!?!?;-)

A BIG Thank You to everyone who posted!

非常感谢所有发帖的人!

Cheers again. Keith.

再次欢呼。基思。

回答by MBo

Try to remove 'magnitude' term in the parentheses both for X and for Y expressions:

尝试删除 X 和 Y 表达式括号中的“幅度”项:

(int)(  ((double)a.X + vectorX * distance)  +  0.5  )

回答by dwonisch

Calculate the vector AB

计算向量AB

First define the vector from point A(1,-1) to point B(2,4) substracting A from B. The vector would be Vab(1,5).

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

Calculate the length of AB

计算AB的长度

Use Pythagorean theorem to calculate the length of vector AB.

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

|Vab| = SQRT(12+52)

The Length is (rounded) 5.1

长度为(四舍五入)5.1

Calculate the unit vector

计算单位向量

Divide the vector by its length to get the unit vector (the vector with length 1).

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

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

Calculate the vector with length 4

计算长度为 4 的向量

Now multiply V1 with the length you want, for example 4, to get Vt.

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

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

Calculate target point

计算目标点

Add the vector Vt to point A to get point T (target).

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

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

EDIT: Answer to your edit

编辑:回答您的编辑

The method LengthOfHypotenuse should look like that

LengthOfHypotenuse 方法应该是这样的

  • fixed an error on calculating bSq
  • and removed redundant Math.Abs call, because a pow of 2 is always positive
  • removed the addition of 0.5, don't know why you would need that
  • you should at least use a float as return value (double or decimal would work also)

    //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
    }
    
  • 修正了计算 bSq 的错误
  • 并删除了多余的 Math.Abs​​ 调用,因为 2 的 pow 始终为正
  • 删除了 0.5 的添加,不知道您为什么需要它
  • 您至少应该使用浮点数作为返回值(双精度或十进制也可以)

    //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
    }
    

The method Draw(Point a, Point b) should look like that:

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

  • Corrected DrawCell() call

    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);
        }
    }
    
  • 更正的 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);
        }
    }
    

Your CalculatePoint(Point a, Point b, int distance) method:

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

  • Moved some calculations into Vector2 class

    private Point CalculatePoint(Vector2 a, Vector2 b, int distance) {
        Vector2 vectorAB = a - b;
    
        return a + vectorAB.UnitVector * distance;
    }
    
  • 将一些计算移动到 Vector2 类中

    private Point CalculatePoint(Vector2 a, Vector2 b, int distance) {
        Vector2 vectorAB = a - b;
    
        return a + vectorAB.UnitVector * distance;
    }
    

I have extended the Vector class for you to add the missing operators (credits to AgentFire)

我为您扩展了 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);
        }
    }

回答by AgentFire

Better approach (you can rename the struct if you need):

更好的方法(如果需要,您可以重命名结构):

struct Vector2
{
    public readonly float X;
    public readonly float Y;

    public Vector2(float x, float 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(a.X + b.X, a.Y + b.Y);
    }
    public static Vector2 operator *(Vector2 a, float 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 + aaction:

要获得中点,您只需要执行以下(a - b) * d + a操作:

class Program
{
    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 + a;
        Console.WriteLine(c);
    }
}

This will give you the point:

这将为您提供要点:

50%

50%

output:\> [2, 1]

output:\> [2, 1]

All you need after that is to for(the distance; up toone; d += step)from 0.0to 1.0and draw your pixels.

之后你需要的只是从0.01.0并绘制你的像素。for(the distance; up toone; d += step)

回答by swiftgp

private Point CalculatePoint(Point a, Point b, int distance) {
      Point newPoint = new Point(10,10);
      Double Magnitude = Math.Sqrt(Math.Pow((b.Y - a.Y),2) + Math.Pow((b.X - a.X),2));
      newPoint.X = (int)(a.X + (distance * ((b.X - a.X)/magnitude)));
      newPoint.Y = (int)(a.Y + (distance * ((b.Y - a.Y)/magnitude)));
      return newPoint;
}

回答by Cadair Idris

    private static Point CalculatePoint(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 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));
    }

回答by corlettk

OK guys, I found my major bug. It was a classic Doh! My Draw method was painting at p.X, p.X

好的,伙计们,我发现了我的主要错误。这是一个经典的 Doh!我的 Draw 方法是在 pX, p 上绘画。X

So, I finally got something that works. Please note that I am not saying that this a "good solution", or "the only working solution" I'm just saying that it does what I want it to do ;-)

所以,我终于得到了一些有效的东西。请注意,我并不是说这是一个“好的解决方案”或“唯一可行的解​​决方案”,我只是说它可以完成我想要它做的事情;-)

Here's my UPDATEDworking code: (complete and selfcontained this time ;-)

这是我更新后的工作代码:(这次完整且独立;-)

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

namespace DrawLines
{
    public class MainForm : Form
    {
        #region constants and readonly attributes

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

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

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

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

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

        #endregion

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

        #region DrawPoints upon MainForm_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 _thePoints ) {
                DrawCell(p.X, p.Y, _thePointBrush);
                DrawLabel(p.X, p.Y, c++);
            }
        }

        #endregion

        #region DrawLines on button click

        // =====================================================================
        // Here's the interesting bit. DrawLine was called Draw

        // Draws a line from A to B, by using X-values to calculate the Y values.
        private void DrawLine(Point a, Point b)
        {
            if ( a.Y > b.Y ) // A is below B
                Swap(ref a, ref b); // make A the topmost point (ergo sort by Y)
            Debug.Assert(a.Y < b.Y, "A is still below B!");

            var left = Math.Min(a.X, b.X);
            var right = Math.Max(a.X, b.X);
            int width = right - left;
            Debug.Assert(width >= 0, "width is negative!");

            var top = a.Y;
            var bottom = b.Y;
            int height = bottom - top;
            Debug.Assert(height >= 0, "height is negative!");

            if ( width > height ) {
                // use given X values to calculate the Y values, 
                // otherwise it "skips" some X's
                double slope = (double)height / (double)width; 
                Debug.Assert(slope >= 0, "slope is negative!");
                if (a.X <= b.X)     // a is left-of b, so draw left-to-right.
                    for ( int x=1; x<width; ++x ) // xOffset
                        DrawCell( (left+x), (a.Y + ((int)(slope*x + 0.5))), _theLineBrush);
                else                // a is right-of b, so draw right-to-left.
                    for ( int x=1; x<width; ++x ) // xOffset
                        DrawCell( (right-x), (a.Y + ((int)(slope*x + 0.5))), _theLineBrush);
            } else {
                // use given Y values to calculate the X values, 
                // otherwise it "skips" some Y's
                double slope = (double)width/ (double)height; 
                Debug.Assert(slope >= 0, "slope is negative!");
                if (a.X <= b.X) {     // a is left-of b, so draw left-to-right. (FG)
                    for ( int y=1; y<height; ++y ) // yOffset
                        DrawCell( (a.X + ((int)(slope*y + 0.5))), (top+y), _theLineBrush);
                } else {              // a is right-of b, so draw right-to-left. (DE,IJ)
                    for ( int y=1; y<height; ++y ) // yOffset
                        DrawCell( (b.X + ((int)(slope*y + 0.5))), (bottom-y), _theLineBrush);
                }
            }
        }

        private void btnDrawLines_Click(object sender, EventArgs e) {
            DrawLines();  // join the points
            DrawPoints(); // redraw the labels over the lines.
        }

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

        private void Swap(ref Point a, ref Point b) {
            Point tmp = a;
            a = b;
            b = tmp;
        }

        #endregion

        #region actual drawing on _myGraphics

        // there should be no calls to Draw or Fill outside of this region

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

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

        #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
    }

}

EDIT - UPDATED ABOVE CODE:This version draws "solid" lines. The previously posted version skipped cells in nearly vertical lines, so I inverted the algorithm to calculate the X value (instead of the Y value) in these cases... now I can use it to set (and draw) a "solid fence" around a "navigable area" ;-)

编辑 - 以上代码更新:此版本绘制“实线”。之前发布的版本跳过了几乎垂直的单元格,因此在这些情况下我反转了算法来计算 X 值(而不是 Y 值)……现在我可以用它来设置(和绘制)“实心围栏”围绕“可航行区域”;-)

Here's an UPDATEDpicture of the correct results.

这是正确结果的更新图片。

DrawLiness_solid_success.png

DrawLines_solid_success.png

Thanks againto everybody who helped... and you did help ;-)

再次感谢所有帮助过的人......你确实帮助了;-)

Cheers. Keith.

干杯。基思。