これは矩形を回転させ、回転した矩形同士が重なっているかの実験です。
さて任意の点がこのRotatedRectangleの内部にあるか、複数の回転している矩形が重なっているかはどうやって調べればいいでしょうか?
回転した矩形を描画する
最初に回転した矩形を描画してみます。これは回転した矩形を描画するためのクラスです。コンストラクタで中心の座標と幅、高さを指定します。CenterプロパティとAngleOfRotationプロパティを設定することで回転角度と描画位置を変更することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
public class RotatedRectangle { int CenterX = 0; int CenterY = 0; Rectangle Rectangle = Rectangle.Empty; Pen Pen = new Pen(Color.Black); public RotatedRectangle(int centerX, int centerY, int width, int height) { CenterX = centerX; CenterY = centerY; AngleOfRotation = 0; Rectangle = new Rectangle(centerX - width / 2, centerY - height / 2, width, height); } public Point Center { get { return new Point(CenterX, CenterY); } set { CenterX = value.X; CenterY = value.Y; int x = CenterX - Rectangle.Width / 2; int y = CenterY - Rectangle.Height / 2; int w = Rectangle.Width; int h = Rectangle.Height; Rectangle = new Rectangle(x, y, w, h); } } public int AngleOfRotation { get; set; } public void Draw(Graphics graphics) { Matrix matrix = new Matrix(); matrix.RotateAt(AngleOfRotation, new Point(CenterX, CenterY)); Point[] points = { new Point(Rectangle.Left, Rectangle.Top), new Point(Rectangle.Right, Rectangle.Top), new Point(Rectangle.Right, Rectangle.Bottom), new Point(Rectangle.Left, Rectangle.Bottom), new Point(Rectangle.Left, Rectangle.Top), }; matrix.TransformPoints(points); graphics.DrawLines(Pen, points); } } |
上記のクラスをつかって回転された矩形を描画してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class Form1 : Form { RotatedRectangle RotatedRectangle; public Form1() { InitializeComponent(); RotatedRectangle = new RotatedRectangle(150, 150, 150, 100); RotatedRectangle.AngleOfRotation = 45; } protected override void OnPaint(PaintEventArgs e) { RotatedRectangle.Draw(e.Graphics); base.OnPaint(e); } } |
点は矩形の内部にあるか?
ある座標が矩形の内部かどうかは以下の方法で調べることができます。
1 2 3 4 5 6 7 8 9 |
bool IsPointInside(Rectangle rectangle, Point point) { if (rectangle.Left > point.X || rectangle.Right < point.X) return false; if (rectangle.Top > point.Y || rectangle.Bottom < point.Y) return false; return true; } |
回転しているのであれば回転を戻して上記の方法で調べるのはどうでしょうか?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class RotatedRectangle { public bool IsPointInside(Point point) { Matrix matrix = new Matrix(); matrix.RotateAt(-AngleOfRotation, new Point(CenterX, CenterY)); Point[] points = { point, }; // 引数で渡された点を逆方向に回転させる matrix.TransformPoints(points); // あとは上記の方法で調べる if (Rectangle.Left > points[0].X || Rectangle.Right < points[0].X) return false; if (Rectangle.Top > points[0].Y || Rectangle.Bottom < points[0].Y) return false; return true; } } |
これはクリックされた座標が回転した矩形の内部かどうかを調べるプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public partial class Form1 : Form { RotatedRectangle RotatedRectangle = new RotatedRectangle(150, 150, 150, 100); public Form1() { InitializeComponent(); RotatedRectangle.AngleOfRotation = 45; // 45度回転させる } Point LastPoint = Point.Empty; // 最後にクリックされた座標を記憶する protected override void OnMouseDown(MouseEventArgs e) { LastPoint = new Point(e.X, e.Y); Invalidate(); base.OnMouseDown(e); } Font ResultFont = new Font("MS ゴシック", 16); Point ResultPoint = new Point(10, 10); protected override void OnPaint(PaintEventArgs e) { RotatedRectangle.Draw(e.Graphics); // クリックされた部分が矩形の内部かどうか調べて結果を表示する string str; if (RotatedRectangle.IsPointInside(LastPoint)) str = String.Format("{0},{1}は矩形の内部です", LastPoint.X, LastPoint.Y); else str = String.Format("{0},{1}は矩形の内部ではありません", LastPoint.X, LastPoint.Y); e.Graphics.DrawString(str, ResultFont, Brushes.Black, ResultPoint); base.OnPaint(e); } } |
回転した矩形と矩形は重なっているか?
では点ではなく他の矩形と重なっているかどうかを調べるにはどうすればいいでしょうか? 大きくない矩形であれば矩形のまわりの点をあつめてRotatedRectangle.IsPointInside(Point pt)で判定できそうです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class RotatedRectangle { public bool DoesRectangleOverlap(Rectangle rect) { Matrix matrix = new Matrix(); matrix.RotateAt(-AngleOfRotation, new Point(CenterX, CenterY)); // 引数で渡された矩形の境界線上の点を集める List<Point> pointList = new List<Point>(); for (int x = rect.Left; x <= rect.Right; x++) pointList.Add(new Point(x, rect.Top)); for (int y = rect.Top; y <= rect.Bottom; y++) pointList.Add(new Point(rect.Right, y)); for (int x = rect.Right; x >= rect.Left; x--) pointList.Add(new Point(x, rect.Bottom)); for (int y = rect.Bottom; y >= rect.Top; y--) pointList.Add(new Point(rect.Left, y)); // 境界線上の点を逆方向に回転させる Point[] points = pointList.ToArray(); matrix.TransformPoints(points); // ひとつでも矩形の内部にあれば重なっていると判定する foreach(Point pt in points) { if (Rectangle.Left > pt.X || Rectangle.Right < pt.X) continue; if (Rectangle.Top > pt.Y || Rectangle.Bottom < pt.Y) continue; return true; } return false; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form1 : Form { // 別の矩形を用意する Rectangle Rectangle2 = new Rectangle(10, 10, 100, 60); protected override void OnMouseDown(MouseEventArgs e) { // クリックした座標がもうひとつの矩形の中心点になる Rectangle2.X = e.X - Rectangle2.Width / 2; Rectangle2.Y = e.Y - Rectangle2.Height / 2; Invalidate(); base.OnMouseDown(e); } protected override void OnPaint(PaintEventArgs e) { RotatedRectangle.Draw(e.Graphics); e.Graphics.DrawRectangle(Pens.Red, Rectangle2); // もうひとつの矩形はRotatedRectangleと重なっているか? if (RotatedRectangle.DoesRectangleOverlap(Rectangle2)) e.Graphics.DrawString("重なっている", ResultFont, Brushes.Black, ResultPoint); else e.Graphics.DrawString("重なっていない", ResultFont, Brushes.Black, ResultPoint); base.OnPaint(e); } } |
回転した矩形同士は重なっているか?
ではRotatedRectangleをもうひとつ用意してお互いが重なっているかどうかはどうやって調べればよいでしょうか? これも境界線の点の集合を取得して考えることにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class RotatedRectangle { // 自分自身の境界線の点の配列を取得する public Point[] GetBorderPoints() { Matrix matrix = new Matrix(); matrix.RotateAt(AngleOfRotation, new Point(CenterX, CenterY)); for (int x = Rectangle.Left; x <= Rectangle.Right; x++) pointList.Add(new Point(x, Rectangle.Top)); for (int y = Rectangle.Top; y <= Rectangle.Bottom; y++) pointList.Add(new Point(Rectangle.Right, y)); for (int x = Rectangle.Right; x >= Rectangle.Left; x--) pointList.Add(new Point(x, Rectangle.Bottom)); for (int y = Rectangle.Bottom; y >= Rectangle.Top; y--) pointList.Add(new Point(Rectangle.Left, y)); Point[] points = pointList.ToArray(); matrix.TransformPoints(points); return points; } public bool DoesRectangleOverlap(RotatedRectangle rect) { Matrix matrix = new Matrix(); matrix.RotateAt(-AngleOfRotation, new Point(CenterX, CenterY)); // 引数で渡された回転された矩形の境界線上の点の配列を取得し、逆回転させる Point[] points = rect.GetBorderPoints(); matrix.TransformPoints(points); // 矩形の内部の点があるか調べる。ひとつでもみつかれば両者は重なっている foreach (Point pt in points) { if (Rectangle.Left > pt.X || Rectangle.Right < pt.X) continue; if (Rectangle.Top > pt.Y || Rectangle.Bottom < pt.Y) continue; return true; } return false; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public partial class Form1 : Form { RotatedRectangle RotatedRectangle = new RotatedRectangle(150, 150, 150, 100); RotatedRectangle RotatedRectangle2 = new RotatedRectangle(150, 350, 150, 100); public Form1() { InitializeComponent(); RotatedRectangle.AngleOfRotation = 45; RotatedRectangle2.AngleOfRotation = 30; } protected override void OnMouseDown(MouseEventArgs e) { RotatedRectangle2.Center = new Point(e.X, e.Y); Invalidate(); base.OnMouseDown(e); } protected override void OnPaint(PaintEventArgs e) { RotatedRectangle.Draw(e.Graphics); RotatedRectangle2.Draw(e.Graphics); if (RotatedRectangle.DoesRectangleOverlap(RotatedRectangle2)) e.Graphics.DrawString("重なっている", ResultFont, Brushes.Black, ResultPoint); else e.Graphics.DrawString("重なっていない", ResultFont, Brushes.Black, ResultPoint); base.OnPaint(e); } } |