2015年7月11日土曜日

線分と線分の当たり判定

線分同士の当たり判定が欲しくなったので試してみた
C#のFormで、FormにはPictureBoxを親コンテナにドッキングして配置する。またPictureBoxのClickイベントを使用する。
青い斜めの線は固定となり、もう一方の線はマウスで二箇所をクリックすることにより、それらを接続する線を引くことができる。2本の線が交差している場合は赤で、交差していない場合は緑で表示する。
アルゴリズムは 線分交差判定 - juntkの日記 に書いてある物をそのまま使用させてもらった。

線分同士の当たり判定は、例えば現在位置と未来位置で線を作り、進入禁止エリアを囲うラインと当たり判定を行い、衝突が発生した場合は移動させない というような使い方ができると思う。ただし線分同士の衝突範囲では、万が一線で囲ったエリアの中に入り込んでしまった場合には、外にでることができなくなるという問題がある。また、今いる場所が進入禁止エリアの中なのか、それとも稼働可能範囲なのかを判定することは出来ない。

それと、線分同士の当たり判定では始点と終点を使用するため、線のどの部分で衝突しているかを判定することは出来ない。もしもどの場所で衝突しているかを調べる必要があるなら、二分探索等で十分に線が短くなるまで探していくか、他の方法を考える必要がある。


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

namespace LineCollisionDetection {
    public partial class Form1 : Form {
        LinePoint Line1, Line2;
        Point? Start;
        Pen Pen1, Pen2, Pen3;

        public Form1() {
            InitializeComponent();

            Pen1 = new Pen(Color.Red, 3);
            Pen2 = new Pen(Color.Green, 3);
            Pen3 = new Pen(Color.Blue, 3);

            Line1 = new LinePoint(new Point(100, 100), new Point(300, 300));
            Line2 = new LinePoint(new Point(300, 100), new Point(100, 300));

            Size += new Size(400, 400) - pictureBox1.Size;
            MaximumSize = MinimumSize = Size;

            Draw();
        }

        ~Form1() {
            Pen1.Dispose();
            Pen2.Dispose();
            Pen3.Dispose();

            if (pictureBox1.Image != null) {
                pictureBox1.Image.Dispose();
            }
        }

        private void pictureBox1_Click(object sender, EventArgs e) {
            Point mouse = pictureBox1.PointToClient(Cursor.Position);

            if (Start == null) {
                Start = mouse;
            } else {
                Line1 = new LinePoint(Start ?? new Point(), mouse);
                Start = null;

                Draw();
            }
        }

        public void Draw() {
            Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);

            using (Graphics gra = Graphics.FromImage(bmp)) {
                bool Collision = LinePoint.IsCollision(Line1, Line2);

                gra.DrawLine(Pen3, Line2.S, Line2.E);
                gra.DrawLine(Collision ? Pen1 : Pen2, Line1.S, Line1.E);
            }

            if (pictureBox1.Image != null) {
                pictureBox1.Image.Dispose();
            }
            pictureBox1.Image = bmp;
        }

        class LinePoint {
            public Point S;
            public Point E;

            public LinePoint() {
                S = new Point();
                E = new Point();
            }

            public LinePoint(Point S, Point E) {
                this.S = S;
                this.E = E;
            }

            public LinePoint(LinePoint Line) {
                S = Line.S;
                E = Line.E;
            }

            static public bool IsCollision(LinePoint Line1, LinePoint Line2) {
                return ((
                    ((Line2.S.X - Line2.E.X) * (Line1.S.Y - Line2.S.Y) + (Line2.S.Y - Line2.E.Y) * (Line2.S.X - Line1.S.X)) *
                    ((Line2.S.X - Line2.E.X) * (Line1.E.Y - Line2.S.Y) + (Line2.S.Y - Line2.E.Y) * (Line2.S.X - Line1.E.X)) <= 0) && (
                    ((Line1.S.X - Line1.E.X) * (Line2.S.Y - Line1.S.Y) + (Line1.S.Y - Line1.E.Y) * (Line1.S.X - Line2.S.X)) *
                    ((Line1.S.X - Line1.E.X) * (Line2.E.Y - Line1.S.Y) + (Line1.S.Y - Line1.E.Y) * (Line1.S.X - Line2.E.X)) <= 0));
            }
        }
    }
}

0 件のコメント:

コメントを投稿