11

与えられた 2 点間の線を「引く」ことができるように、A から与えられた距離にある、与えられた線 AB に沿った点を計算しようとして、私はかなり怒っています。最初は簡単に思えたのですが、うまく理解できませんでした。さらに悪いことに、どこが間違っているのか理解できません。幾何学 (および数学全般) は私の得意分野ではありません。

私は同様の質問を読んだことがあり、SOに関する回答があります。実際、 Mads Elvheimの答えから直接 CalculatePoint 関数の現在の実装を持ち上げました:開始点と終了点、および距離が与えられたら、線に沿って点を計算します (さらに、後のコメントで訂正します - 彼を正しく理解していれば) )なぜなら、問題を解決するための私の独立した試みは、ファーストクラスの特急券の欲求不満の土地を除いて、私をどこにも連れてこなかったからです.

これが私の更新されたコードです(投稿の下部にある編集ノートを参照してください):

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 です。これは、MazeOfBolton を実行するための A* 最短ルート アルゴリズムの実装です...つまり迷路ランナーです。

私が実際にやりたいことは、「フェンス」内のすべてのポイントが特定の範囲内にあるように、迷路 (マトリックス) 内の 2 つの特定のポイント (原点とゴール) の周りに「フェンス」(つまり、バッファリングされたMBR ) を事前に計算することです。ゴールから遠ざかる数十万通りの経路をすばやく排除するために、「2 点間の直線」から距離を置きます。

このプログラミング チャレンジは数年前に終了しているため、ここでは「競争力のある盗作」の問題はありません。いいえ、これは宿題ではありません。実際、私はプロのプログラマーです...比較的単純なジオメトリであっても、私はここで自分の快適ゾーンから外れています。はぁ。

だから... CalculatePoint関数を正しく取得するのに役立つポインタを誰か教えてください: Aから指定された距離で線ABに沿った点を計算しますか?

ここまでお読みいただき、誠にありがとうございました。

乾杯。キース。


編集:投稿されたソースコードを更新しました:

(1) 自己完結型ではないことに気付きました。別の MainForm を忘れていました。投稿されたコードの末尾に追加したDesigner .cs ファイル。

(2) 最新バージョンには、私がこれまでに試したことが含まれており、各失敗がどのように見えるかの写真へのフォトバケット リンクが含まれています...そしてそれらはすべて同じです。え?なんてこと?

私の問題は、デザイナーが生成したコードを投稿するのを忘れたために他の人が見逃していたファンキーなウィンドウフォーム設定のように、他の場所にある可能性があると思います.では、なぜ計算されたポイントが異なる必要があるのでしょうか。知らない!?!?!?私はかなりイライラして不機嫌になってきているので、これは別の日に置いておこうと思います;-)

コンピューターに何かをさせるのにどれだけの労力がかかるかを、私たちが日常的にどれほど過小評価しているかを示しています...単純な線を描くだけでも...それは曲線でさえありません。大円や横メルカトル図法などは言うまでもありませんファンシー...ただの単純な血まみれのライン!?!?!? ;-)

投稿してくださった皆様、本当にありがとうございました!

乾杯。キース。

4

6 に答える 6

28

ベクトル 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 の計算エラーを修正
  • pow 2 は常に正であるため、余分な Math.Abs​​ 呼び出しを削除しました。
  • 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);
        }
    }
于 2012-09-23T07:13:47.523 に答える
7

より良いアプローチ(必要に応じて構造体の名前を変更できます):

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

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

これはあなたにポイントを与えるでしょう:

50%

output:\> [2, 1]

その後に必要なのは、0.0から1.0までで、ピクセルを描画することだけです。for(the distance; up toone; d += step)

于 2012-09-23T07:20:15.090 に答える
4
    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));
    }
于 2012-09-23T07:24:03.070 に答える
2

X 式と Y 式の両方で、括弧内の「大きさ」の項を削除してみてください。

(int)(  ((double)a.X + vectorX * distance)  +  0.5  )
于 2012-09-23T07:10:18.363 に答える
1
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;
}
于 2012-09-23T07:20:35.557 に答える
1

OKみんな、私は私の大きなバグを見つけました。古典的なドーでした!私のDrawメソッドは、pX、pでペイントしていました。バツ

それで、私はついにうまくいくものを手に入れました。これが「良い解決策」または「唯一の有効な解決策」であると言っているのではないことに注意してください。私が望んでいることを実行していると言っているだけです;-)

これが私の更新された作業コードです:(今回は完全で自己完結型です;-)

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
    }

}

編集-上記のコードを更新:このバージョンは「実線」の線を描画します。以前に投稿されたバージョンでは、ほぼ垂直線のセルがスキップされたため、これらの場合、アルゴリズムを逆にして(Y値ではなく)X値を計算しました...これを使用して「ソリッドフェンス」を設定(および描画)できます「ナビゲート可能なエリア」の周り;-)

これが正しい結果の更新された画像です。

DrawLiness_solid_success.png

助けてくれたみんなにもう一度感謝します...そしてあなたは助けてくれました;-)

乾杯。キース。

于 2012-09-24T04:47:21.103 に答える