
Среда программирования:
Visual Studio 2015
Перед нами стоит следующая задача:
Создать класс многоугольника и обеспечить его методом проверки попадания точки в этот многоугольник.
Программа рисует один многоугольник и реагирует на щелчок мыши. Если щелчок был произведен по многоугольнику, меняем его цвет.
Код программы:
Класс Polygon представляет собой многоугольник. Он служит для хранения вершин, отрисовки себя на Graphics и определения попадания точки внутрь.
Описание работы методов edgeType(Point, Point, Point) и pointInPolygon(Point) смотрите в статье Метод трассировки лучом.
Polygon:
using System; using System.Drawing; using System.Drawing.Drawing2D; namespace TanyaLab { class Polygon { private Point[] points; //вершины многоугольника private GraphicsPath path; //служит для отрисовки многоугольника private int minX, maxX, minY, maxY; public Polygon(int[] points) { if (points.Length < 6 || points.Length % 2 == 1) throw new Exception(); minX = minY = int.MaxValue; maxX = maxY = int.MinValue; this.points = new Point[points.Length / 2]; for (int i = 0; i<points.Length; i += 2) { minX = Math.Min(minX, points[i]); maxX = Math.Max(maxX, points[i]); minY = Math.Min(minY, points[i + 1]); maxY = Math.Max(maxY, points[i + 1]); this.points[i / 2] = new Point(points[i], points[i + 1]); } path = new GraphicsPath(); } public Point[] getPoints() { return points; } public void fill(Graphics g, SolidBrush brush, float translateX, float translateY, float scale) //отрисовка многоугольника в точке (translateX, translateY) и масштабом scale { path.Reset(); float lastX = (points[0].X - minX) * scale + translateX; float lastY = (points[0].Y - minY) * scale + translateY; for (int i = 1; i < points.Length; i++) { float x = (points[i].X - minX) * scale + translateX; float y = (points[i].Y - minY) * scale + translateY; path.AddLine(lastX, lastY, x, y); lastX = x; lastY = y; } g.FillPath(brush, path); } private PointOverEdge classify(Point p, Point v, Point w) //положение точки p относительно отрезка vw { //коэффициенты уравнения прямой int a = v.Y - w.Y; int b = w.X - v.X; int c = v.X * w.Y - w.X * v.Y; //подставим точку в уравнение прямой int f = a * p.X + b * p.Y + c; if (f > 0) return PointOverEdge.RIGHT; //точка лежит справа от отрезка if (f < 0) return PointOverEdge.LEFT; //слева от отрезка int minX = Math.Min(v.X, w.X); int maxX = Math.Max(v.X, w.X); int minY = Math.Min(v.Y, w.Y); int maxY = Math.Max(v.Y, w.Y); if (minX <= p.X && p.X <= maxX && minY <= p.Y && p.Y <= maxY) return PointOverEdge.BETWEEN; //точка лежит на отрезке return PointOverEdge.OUTSIDE; //точка лежит на прямой, но не на отрезке } private EdgeType edgeType(Point a, Point v, Point w) //тип ребра vw для точки a { switch (classify(a, v, w)) { case PointOverEdge.LEFT: return ((v.Y < a.Y) && (a.Y <= w.Y)) ? EdgeType.CROSSING : EdgeType.INESSENTIAL; case PointOverEdge.RIGHT: return ((w.Y < a.Y) && (a.Y <= v.Y)) ? EdgeType.CROSSING : EdgeType.INESSENTIAL; case PointOverEdge.BETWEEN: return EdgeType.TOUCHING; default: return EdgeType.INESSENTIAL; } } public PointInPolygon pointInPolygon(Point a) //положение точки в многоугольнике { bool parity = true; for (int i = 0; i < points.Length; i++) { Point v = points[i]; Point w = points[(i + 1) % points.Length]; switch (edgeType(a, v, w)) { case EdgeType.TOUCHING: return PointInPolygon.BOUNDARY; case EdgeType.CROSSING: parity = !parity; break; } } return parity ? PointInPolygon.OUTSIDE : PointInPolygon.INSIDE; } public int MinX() { return minX; } public int MaxX() { return maxX; } public int MinY() { return minY; } public int MaxY() { return maxY; } public int width() //ширина { return maxX - minX; } public int height() //высота { return maxY - minY; } public enum PointInPolygon { INSIDE, OUTSIDE, BOUNDARY } //положение точки в многоугольнике private enum EdgeType { TOUCHING, CROSSING, INESSENTIAL } //положение ребра private enum PointOverEdge { LEFT, RIGHT, BETWEEN, OUTSIDE } //положение точки относительно отрезка } }
Form1:
using System; using System.Drawing; using System.Windows.Forms; namespace TanyaLab { public partial class Form1 : Form { private Bitmap bitmap; private Graphics g; private int width, height; //ширина и высота области рисования private float scale; //коэффициент масштабирования private float translateX, translateY; //смещение по х и у private SolidBrush brush; //служит для закрашивания многоугольника private Random random; //служит для выбора случайных цветов private Polygon polygon; public Form1() { InitializeComponent(); } private void draw() { g.Clear(Color.White); polygon.fill(g, brush, translateX, translateY, scale); } private void pictureBox_MouseClick(object sender, MouseEventArgs e) { //так как наш Polygon проверяет попадания без учета translate и scale, преобразуем х и у координаты float x = (e.X - translateX) / scale + polygon.MinX(); float y = (e.Y - translateY) / scale + polygon.MinY(); Point p = new Point((int)x, (int)y); //изменяем цвет многоугольника при попадании if (touch(polygon, p)) { brush.Color = randomColor(); draw(); pictureBox.Invalidate(); } } private bool touch(Polygon polygon, Point a) //проверка попадания точки в Polygon { Polygon.PointInPolygon touch = polygon.pointInPolygon(a); return touch == Polygon.PointInPolygon.INSIDE || touch == Polygon.PointInPolygon.BOUNDARY; } private Color randomColor() //возвращает случайный цвет { if (random == null) random = new Random(); return Color.FromArgb(random.Next(25, 225), random.Next(25, 225), random.Next(25, 225)); //случайный цвет } private void Form1_Load(object sender, EventArgs e) { bitmap = new Bitmap(pictureBox.Width, pictureBox.Height); pictureBox.Image = bitmap; g = Graphics.FromImage(bitmap); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; width = bitmap.Width; height = bitmap.Height; //выбираем случайный цвет закрашивания brush = new SolidBrush(randomColor()); //создаем многоугольник int[] vertex = new int[] { 370, 279, 160, 106, 262, 390, 61, 433, 358, 530, 160, 869, 412, 624, 439, 942, 528, 601, 672, 797, 598, 425, 673, 276, 427, 420, 660, 124, 358, 356, 507, 95 }; //х и y координаты многоугольника против часовой стрелки polygon = new Polygon(vertex); //растягимаем многоугольник на всю область рисования и устанавливаем в центре scale = Math.Min(pictureBox.Width / (float)polygon.width(), pictureBox.Height / (float)polygon.height()); translateX = (pictureBox.Width - polygon.width() * scale) / 2; translateY = (pictureBox.Height - polygon.height() * scale) / 2; draw(); } } }
Прикрепленный файл | Размер |
---|---|
point_in_polygon.zip | 61.49 кб |
Комментарии
Здравствуйте.
Спасибо за проект.
У меня вопрос, по какой причине определение принадлежности точки многоугольнику работает некорректно, если координаты из больших чисел состоят, например:
int[] vertex = new int[]
{
181793, 1306,
200345, 8580,
222066, 11229,
232582, 27101,
217603, 44974,
221515, 70963,
230365, 83562,
236273, 97545,
}; //х и y координаты многоугольника против часовой стрелки
Отрисовка на форме не используется, просто проверяется принадлежность точки.
С такими координатами не работает.
Если разделить на 1000 каждое число и соответственно координаты точки, тогда все работает)) Иначе нет.
В чем может быть загвоздка?
У Вас число превысит максимальное число int. Можно использовать в Вашем случае uint, но лучше все переписать на double.