Chris Maunder Ответов: 11

Задача кодирования: является ли точка в многоугольнике?


Сегодняшняя задача запоздалая, но простая.

Дано множество (x,y) точки, представляющие собой многоугольник, определяют, является ли данный (x,y) точка находится внутри или снаружи многоугольника.

Например, многоугольник, определенный точками (против часовой стрелки) { (2,0), (4,1), (4,4), (2,5), (1,2) and (2,0) } содержит точку (3,3) но не содержит смысла (5,4).

Вы можете сделать тип данных любым, каким захотите. Двойная точность с плавающей запятой, целое число, локти-это не имеет значения. Вы можете использовать любой язык, какой пожелаете. Вам просто нужно уметь подавать входные данные и получать выходные на вопрос "есть ли эта точка в многоугольнике".

Здесь есть пара статей, которые могут помочь (Вычислительная геометрия, C++ и Викоби[^] и Классы вычислительной геометрии[^])


Вызов на прошлой неделе был выигран (и это свободный термин) Петр Leow[^], с особыми упоминаниями для Реализация C64[^] Корнфельдом Элиягу Петером и реализация LINQ[^] Мацей Лос. Объяснение Питера и его чрезмерно продуманный подход чрезвычайно позабавили судью. Питер-пришлите нам свои данные, и кружка CodeProject уже в пути.

Приношу свои извинения за поздний вызов на этой неделе. Зима в Торонто вызвала хаос в офисе.

Что я уже пробовал:

Новая лопата для снега - но это мало помогло

Kornfeld Eliyahu Peter

Попробуйте это: https://www.youtube.com/watch?v=IfpDDqyJnSk

Patrice T

Вопрос: точка на краю многоугольника рассматривается внутри или снаружи ?

Kornfeld Eliyahu Peter

Если точка на ребре не является частью многоугольника, то многоугольник не может быть определен точками ребра, а только всеми точками, находящимися внутри многоугольника...

11 Ответов

Рейтинг:
66

Peter Leow

Выбрал это новое задание, пока ждал своего рейса домой. Один взгляд, и это будет включать геометрию, то есть декартову систему координат, векторы, ребра и углы. Чтобы убить время, я начал излагать свои мысли по телефону...

Есть три способа решить эту проблему:
1. Самый простой из них-взять и использовать один из многочисленных пакетов Python для манипулирования и анализа геометрических объектов на декартовой плоскости.
2. Самым сложным будет найти свой собственный алгоритм (исключая известные), чтобы определить, находится ли точка внутри или снаружи многоугольника.
3. последним и крайним будет изучение некоторых методов машинного обучения, таких как нейронная сеть, чтобы узнать и классифицировать точку, находящуюся внутри или снаружи многоугольника. Это отнимает много времени, так как для изучения компьютера потребуется большой набор данных полигонов и точек с известными результатами. Я не буду пытаться сделать это здесь.

Давайте начнем...

Самый Простой

Я использую Стройная 1. 6b2[^] для этой демонстрации. Приведенный ниже код комментируется URL-адресами, указывающими на соответствующие онлайн-ссылки:

"""
IsPointInPolygon_1.py
The easier way using shapely package
by Peter Leow the point seeker

"""

from shapely.geometry.polygon import Polygon
from shapely.geometry.point import Point

def pointWithinPolygon(point, polygon):
    # http://toblerity.org/shapely/manual.html#object.within
    return point.within(polygon)

def polygonContainsPoint(point, polygon):
    # http://toblerity.org/shapely/manual.html#object.contains
    return polygon.contains(point)

def main():

    # http://toblerity.org/shapely/manual.html#polygons
    polygon = Polygon([(2,0), (4,1), (4,4), (2,5), (1,2)])
    
    while True:
        # http://toblerity.org/shapely/manual.html#points
        coords = input("Input the x y coordinates of a point separated by space: ").split()
        if len(coords) != 2:
            break

        # Numeric validation omitted
        point= Point(float(coords[0]), float(coords[1]))

        resultWithin = 'Is {} within {}? {}.'.format(point, polygon, pointWithinPolygon(point, polygon))

        resultContains = 'Does {} contain {}? {}.'.format(polygon, point, polygonContainsPoint(point, polygon))

        print(resultWithin)

        print(resultContains)
  
main()

Пример вывода:
Input the x y coordinates of a point separated by space: 3 3
Is POINT (3 3) within POLYGON ((2 0, 4 1, 4 4, 2 5, 1 2, 2 0))? True.
Does POLYGON ((2 0, 4 1, 4 4, 2 5, 1 2, 2 0)) contain POINT (3 3)? True.
Input the x y coordinates of a point separated by space: 5 4
Is POINT (5 4) within POLYGON ((2 0, 4 1, 4 4, 2 5, 1 2, 2 0))? False.
Does POLYGON ((2 0, 4 1, 4 4, 2 5, 1 2, 2 0)) contain POINT (5 4)? False.
Input the x y coordinates of a point separated by space: 4 4
Is POINT (4 4) within POLYGON ((2 0, 4 1, 4 4, 2 5, 1 2, 2 0))? False.
Does POLYGON ((2 0, 4 1, 4 4, 2 5, 1 2, 2 0)) contain POINT (4 4)? False.
Input the x y coordinates of a point separated by space: 

Вы будете иметь, чтобы установить стройная пакет из Shapely 1. 6b2 : индекс пакета Python[^] до запуска этой программы.

Тот Самый Сложный

Когда я изучал различные многоугольники и точки по телефону, я заметил одну особенность, которая, кажется, преобладает только тогда, когда точка находится внутри многоугольника-сумма всех углов от точки до соседних вершин охватывающего многоугольника всегда равна 2pi в радианах (или 360 градусов). Для реализации этого предположения я написал следующий код:
"""
IsPointInPolygon_2.py
The pain-in-the-a** way
by Peter Leow the point seeker

"""

import math

def offsetCoords(polygon, newOrigin):
    offsetPolygon = []
    for v in polygon:
        offsetPolygon.append((v[0]-newOrigin[0], v[1]-newOrigin[1]))
    return offsetPolygon

def dotProduct(v1, v2):
    return v1[0]*v2[0]+v1[1]*v2[1]

def vectorLen(v):
    return (v[0]**2+v[1]**2)**.5

def angleBetweenVectors(v1, v2):
    cosine = dotProduct(v1, v2) / (vectorLen(v1) * vectorLen(v2))
    return math.acos(cosine)

def isWithin(point, polygon):
    # weed out the obvious
    if point in polygon:
        return False
    # Find angle of each adjacent vectors
    sumAngles = 0
    for i in range(len(polygon) - 1):
        sumAngles += angleBetweenVectors(polygon[i], polygon[i+1])
    if math.isclose(sumAngles, math.pi*2):
        return True
    else:
        return False

def main():

    # A list of coords tuples
    polygon = [(2,0), (4,1), (4,4), (2,5), (1,2), (2,0)]
    
    while True:
        coords = input("Input the x y coordinates of a point separated by space: ").split()
        if len(coords) != 2:    # Exit on Enter key
            break

        # Numeric validation omitted
        point = (float(coords[0]), float(coords[1]))

        '''
        Set this point as the origin (0 0) of the cartesian coordinate plane
        by offsetting all vertices of the polygon against it
        '''
        offsetPolygon = offsetCoords(polygon, point)
        offsetPoint = (0, 0)

        result = 'Is POINT {} within POLYGON {}? {}.'.format(point, polygon, isWithin(offsetPoint, offsetPolygon))

        print(result)
  
main()

Пример вывода показан ниже:
Input the x y coordinates of a point separated by space: 3 3
Is POINT (3.0, 3.0) within POLYGON [(2, 0), (4, 1), (4, 4), (2, 5), (1, 2), (2, 0)]? True.
Input the x y coordinates of a point separated by space: 5 4
Is POINT (5.0, 4.0) within POLYGON [(2, 0), (4, 1), (4, 4), (2, 5), (1, 2), (2, 0)]? False.
Input the x y coordinates of a point separated by space: 4 4
Is POINT (4.0, 4.0) within POLYGON [(2, 0), (4, 1), (4, 4), (2, 5), (1, 2), (2, 0)]? False.
Input the x y coordinates of a point separated by space: 

Пока все идет так хорошо, что мое предположение, похоже, остается верным. У кого-нибудь есть свободное время? Пожалуйста, помогите проверить это.


Jon McKee

Просто хотел отметить, что решение 2pi называется "алгоритмом намотки чисел", если вам интересно =)

Peter Leow

Спасибо, что просветили меня. Я прочитаю об этом и посмотрю, смогу ли я придумать лучший код, если позволит время. Этот кодовый вызов стал рассадником обмена знаниями. Ура!

grgran

Мне кажется, что особенно неправильные многоугольники могут давать суммы больше 2pi, потому что" клинья " (треугольники, образованные контрольной точкой и краевыми точками) могут перекрываться. Представьте себе фигуру, где "левая" точка ребра соединена с точкой в многоугольнике внутри и рядом с" правым " краем. Жаль, что я не могу рисовать здесь :-)

Peter Leow

Вы правы. Перекрытие приводит к сумме углов > 2pi. Так что мое предположение неверно. Спасибо.

Рейтинг:
2

Marc Clifton

Если вы позволите мне метод расширения, чтобы я мог свободно кодировать:

public static class GPExt
{
    public static GraphicsPath AddPolygon2(this GraphicsPath gp, PointF[] points)
    {
        gp.AddPolygon(points);
        return gp;
    }
}


Я могу написать это как однострочный метод, разбирая входную строку координат (я ненавижу все эти new Point звонки, ха-ха):

static bool Contains(string points, PointF p)
{
    return new GraphicsPath()
    .AddPolygon2(
        new Regex(@"(?<coord>(\((?<x>([-+]?[\d]+(\.\d+)*)),\s*(?<y>([-+]?[\d]+(\.\d+)*))\),*\s*))")
            .Matches(points)
            .Cast<Match>().Select(m => new PointF(Convert.ToSingle(m.Groups["x"].Value), Convert.ToSingle(m.Groups["y"].Value))).ToArray())
    .IsVisible(p);
}


И вы называете это так:

Console.WriteLine(Contains("(2,0),(4,1),(4,4),(2,5),(1,2)", new PointF(3, 3)));
Console.WriteLine(Contains("(2,0),(4,1),(4,4),(2,5),(1,2)", new PointF(5, 4)));


Результаты были:

Правда
Ложный

Довольно дикое сочетание, используя регулярные выражения, LINQ и удобный ввод многоугольника.

Марк


Рейтинг:
2

CDP1802

using System;
using System.Collections.Generic;
using System.Windows.Media.Media3D;

class Program
{
    public static bool TestPointAgainstPolygon(List<Vector3D> Polygon,
                                               Vector3D TestPoint)
    {
        Vector3D Normal;
        int i;


        if (Polygon == null && Polygon.Count < 3)
        {
           return false;
        }

        for (i = 0; i < (Polygon.Count - 1); i++)
        {
            Normal = Vector3D.CrossProduct(Polygon[i + 1] - Polygon[i],
                                           TestPoint - Polygon[i]);
            if (Normal.Z < 0)
            {
                return false;
            }
        }

        return true;
    }

    static void Main(string[] args)
    {
        bool Result;


        List<Vector3D> Polygon = new List<Vector3D> 
                                           { 
                                                new Vector3D(2,0,0),
                                                new Vector3D(4,1,0),
                                                new Vector3D(4,4,0),
                                                new Vector3D(2,5,0),
                                                new Vector3D(1,2,0),
                                                new Vector3D(2,0,0)
                                           };

        Result = TestPointAgainstPolygon(Polygon, new Vector3D(3, 3, 0));
        if (Result == true)
        {
            Console.WriteLine("Point (3,3) is inside the polygon.");
        }
        else
        {
            Console.WriteLine("Point (3,3) is outside the polygon.");
        }

        Result = TestPointAgainstPolygon(Polygon, new Vector3D(5, 4, 0));
        if (Result == true)
        {
            Console.WriteLine("Point (5,4) is inside the polygon.");
        }
        else
        {
           Console.WriteLine("Point (5,4) is outside the polygon.");
        }

        Console.Read();
    }
}


Выход:
Точка (3,3) находится внутри многоугольника.
Точка (5,4) находится вне многоугольника.


Этот подход немного отличается. Векторы расширяются до 3D, а компоненты Z устанавливаются равными нулю. Это позволяет нам использовать перекрестное произведение для вычисления нормали между точкой и каждой линией многоугольника.

Если многоугольник определен против часовой стрелки, то Z-компонент каждой нормали должен быть положительным, если точка находится внутри многоугольника. Он находится снаружи, если даже Z-компонент одной нормали отрицателен.

Примечание: На самом деле нам не нужно вычислять полный кросс-продукт. Достаточно определить знак Z-компонента. Кроме того, мы можем отказаться от требования, чтобы полигон определялся против часовой стрелки. Если все нормали имеют один и тот же знак (независимо от того, положительный или отрицательный), то точка находится внутри многоугольника. Если только один знак отличается, то точка находится снаружи.

Примечание 2: это был вопрос на одном из моих экзаменов. Профессор также дал нам полигон в 2D и ожидал, что мы расширим его до 3D и используем перекрестное произведение.


Patrice T

Работает ли это, если многоугольник не выпуклый ?

[no name]

Это слабое место. К счастью, вы можете разделить каждый вогнутый многоугольник на выпуклые многоугольники. Другой неприятный случай был бы, если бы многоугольник мог содержать другой многоугольник в качестве "дыры".

Patrice T

Я знаю триангуляцию Делоне. Боюсь, что никакое решение здесь не справится с дырами в полигоне.
Взгляните на:
href="http://www.cs.cmu.edu/~quake/robust.html" & gt;быстрые надежные предикаты для вычислительной геометрии[^]
На этой странице есть 2D-кросс-продукт.

[no name]

Тест на ориентацию выглядит знакомым.

Patrice T

Я нашел эту страницу, изучая Вороного / Делоне.
Интерес заключается в том, чтобы не прибегать к 3D-точкам.

Рейтинг:
1

Kornfeld Eliyahu Peter

Только для JSOP :-)

IDENTIFICATION DIVISION.
    PROGRAM-ID. CPCC4-POINT-IN-POLYGON.
DATA DIVISION.
    WORKING-STORAGE SECTION.
        01 T1 PIC S9(6) VALUE ZERO.
        01 T2 PIC S9(6) VALUE ZERO.
        01 T3 PIC S9(6) VALUE ZERO.
        01 T4 PIC S9(6) VALUE ZERO.
    LOCAL-STORAGE SECTION.
        01 POLYGON.
            02 POINT OCCURS 6 TIMES.
                03 X PIC 9(3) VALUE ZERO.
                03 Y PIC 9(3) VALUE ZERO.
        01 CHECK-POINT.
            02 CP-X PIC 9(3) VALUE ZERO.
            02 CP-Y PIC 9(3) VALUE ZERO.
        01 CNT PIC 9(1) VALUE 1.
        01 WN PIC 9(3) VALUE ZERO.
        01 SIDE PIC S9(3) VALUE ZERO.
PROCEDURE DIVISION.
    PERFORM READ-CHECK-POINT.
    PERFORM INITIALIZE-POLYGON.
    PERFORM COMPUTE-WINDING-NUMBER VARYING CNT FROM 1 BY 1 UNTIL CNT = 6.
    IF WN = 0 THEN
        DISPLAY 'THE POINT (' CP-X ',' CP-Y ') IS OUTSIDE THE POLYGON.',
    ELSE
        DISPLAY 'THE POINT (' CP-X ',' CP-Y ') IS INSIDE THE POLYGON.',
    END-IF.
    STOP RUN.
    
    COMPUTE-WINDING-NUMBER.
        PERFORM CHECK-SIDE.

        IF Y(CNT) <= CP-Y THEN
			IF Y(CNT + 1) > CP-Y AND SIDE > 0 THEN
			    ADD 1 TO WN
			 END-IF
		ELSE
		    IF Y(CNT + 1) <= CP-Y AND SIDE < 0 THEN
		        SUBTRACT 1 FROM WN
		    END-IF
        END-IF.

    CHECK-SIDE.
        SUBTRACT X(CNT) FROM X(CNT + 1) GIVING T1. 
        SUBTRACT Y(CNT) FROM CP-Y GIVING T2.
        MULTIPLY T1 BY T2 GIVING T1.
        
        SUBTRACT Y(CNT) FROM Y(CNT + 1) GIVING T3.
        SUBTRACT X(CNT) FROM CP-X GIVING T4.
        MULTIPLY T3 BY T4 GIVING T3.
        
        SUBTRACT T3 FROM T1 GIVING SIDE.
    
    READ-CHECK-POINT.
        DISPLAY 'ENTER X COORDINATE:'.
        ACCEPT CP-X.
        DISPLAY 'ENTER Y COORDINATE:'.
        ACCEPT CP-Y.

    INITIALIZE-POLYGON.
        MOVE 2 TO X(1).
        MOVE 0 TO Y(1).

        MOVE 4 TO X(2).
        MOVE 1 TO Y(2).

        MOVE 4 TO X(3).
        MOVE 4 TO Y(3).

        MOVE 2 TO X(4).
        MOVE 5 TO Y(4).

        MOVE 1 TO X(5).
        MOVE 2 TO Y(5).

        MOVE 2 TO X(6).
        MOVE 0 TO Y(6).

И немного для математики...
Это сочетание метода намотки и метода пересечения... Из метода пересечения я заимствовал идею луча, а из метода намотки-идею направления...
1. использует воображаемую горизонтальную линию, которая содержит точку, которую мы ищем, как луч
2. Проверяет только ребра пересекают эту линию
3. Использование кромки, как векторы, чтобы увидеть, если мы идем часовой или против часовой стрелки
4. проверяет, на какой стороне находится точка вектора (ребро) - и эта сторона относительно направления...
5. Увеличение (левая сторона) или уменьшение (правая сторона) числа обмоток
6. Ноль находится за пределами


grgran

Большие пальцы вверх за то, что вы делаете это в COBOL :-) !

Chris Maunder

Это произвело на меня такое впечатление, что я добавил окраску синтаксиса COBOL (пропущенное локальное хранилище-добавлю в следующий раз)

Kornfeld Eliyahu Peter

:-) Но не расстраивайтесь, если это не вызовет огромного потока примеров кода COBOL...

Рейтинг:
1

Duncan Edwards Jones

Если я помню, вы берете воображаемую линию от вашей точки (x,y) до (xMax,y) и проверяете, пересекает ли ее каждая из вершин.

Если число, которое пересекает четное, вы находитесь за пределами многоугольника (включая 0), в противном случае вы находитесь внутри многоугольника.


Patrice T

Считается ли это решением ?

Рейтинг:
1

Patrice T

Вот мое представление.
Похоже, я применил немного оригинальный подход. Я предполагаю, что метод известен, но я понятия не имею о нем. Метод работает с многоугольниками по часовой стрелке и без нее, выпуклыми или нет.
Сначала я перемещаю полигон так, как если бы контрольная точка была {0,0}.
Затем я вычисляю угол, охватываемый сегментом, используя функцию касательной дуги, и суммирую его для каждого сегмента.
Если сумма равна 0, то точка находится вне полигона или на границе.
Если сумма равна 2Pi (радианам), то точка находится внутри многоугольника.

*   CCCP Code Challenge Code Project
    * Point in polygon
    clear
    ? "CCCP Code Challenge Code Project"
    ? "Point in polygon"
    /*
         2
     x  x
      1
    x    x
      x
    
    */
    poly= {{2,0}, {4,1}, {4,4}, {2,5}, {1,2}, {2,0}}
    points= {{3,3}, {5,4}, {4,4}}
    FOR scan= 1 to len(points)
        rep= inpoly(points[scan], poly)
        ? rep
        ?
    next
    return
    
    function inpoly(point, poly)
        local scan
        somme= 0
        for scan=1 to len(poly)- 1
            // move segment coordinates as if testing point was {0,0}
            x1= poly[scan,1]- point[1]
            y1= poly[scan,2]- point[2]
            x2= poly[scan+1,1]- point[1]
            y2= poly[scan+1,2]- point[2]
            // calc covering angle of segment
            tmp= atn2(x2,y2)- atn2(x1,y1)
            if tmp > pi()
                tmp -= 2*pi()
            endif
            if tmp < -pi()
                tmp += 2*pi()
            endif
            // sum it
            somme += tmp
        next
        return abs(somme) > 3


Рейтинг:
0

Ramza360

Добавим еще один пример, используя большую часть того, что написал OriginalGriff, за исключением отсутствия цикла foreach.

public class TestPoints {
      GraphicsPath path;
      public TestPoints(Point[] points) {
          path = new GraphicsPath(points);
      }

      public bool CheckVisible(int x, int y) {
          if (null == path) {
               return false;
          }

          Region region = new Region(path);
          bool visible = region.IsVisible(x, y);
          string output = string.Format("Point ({0}, {1}) is {2} the polygon.", x, y, visible ? "within" : "outside");
          Console.WriteLine(output);
          return visible;
      }
}

public class Program {
     public static void Main(string[] args) {
         Point[] points1 = new Point[] {
               new Point(2, 0), new Point(4, 1), new Point(4, 4),
               new Point(2, 5), new Point(1, 2), new Point(2, 0)
         };

         Point[] points2 = new Point[] {
               new Point(2, 0), new Point(44, 1), new Point(44, 46),
               new Point(24, 57), new Point(10, 26), new Point(2, 0)
         };

         TestPoints test = new TestPoints(points1);
         test.CheckVisible(3, 3);
         test.CheckVisible(5, 4);

         test = new TestPoints(points2);
         test.CheckVisible(35, 40);
         test.CheckVisible(45, 43);
     }
}


Выход:
Точка (3, 3) находится внутри многоугольника.
Точка (5, 4) находится вне многоугольника.
Точка (35, 40) находится внутри многоугольника.
Точка (45, 43) находится вне многоугольника.


Kornfeld Eliyahu Peter

Точка (35, 40) находится внутри многоугольника. - Вы уверены???

Рейтинг:
0

Thomas Daniels

Пора заняться математикой! Мой код использует алгоритм литья лучей: он смотрит, сколько раз Луч, начиная с заданной точки в любом направлении, пересекается с многоугольником. Если это число нечетное, то точка находится внутри многоугольника. Если он четный, то число находится за пределами многоугольника.

/* Usage:
Polygon polygon = new Polygon(new Point(2, 0), new Point(4, 1), new Point(4, 4), new Point(2, 5), new Point(1, 2), new Point(2, 0));
Console.WriteLine(polygon.PointInPolygon(new Point(3, 3))); // True
Console.WriteLine(polygon.PointInPolygon(new Point(5, 4))); // False
*/

public class Polygon
{
    Point[] _points;
    List<LineSegment> _segments;

    public Polygon(params Point[] points)
    {
        if (points[0] == points[points.Length - 1])
        {
            _points = points;
        }
        else
        {
            _points = points.Concat(new Point[] { points[0] }).ToArray();
            // auto-close the polygon
        }

        _segments = new List<LineSegment>();
        for (int i = 0; i < _points.Length; i++)
        {
            if (i != _points.Length - 1)
            {
                _segments.Add(new LineSegment(_points[i], _points[i + 1]));
            }
        }
    }

    public bool PointInPolygon(Point point)
    {
        Ray ray = new Ray(point, new Point(point.X + 1, point.Y + 1));
        int counter = 0;
        foreach (LineSegment segment in _segments)
        {
            if (segment.IntersectsWithRay(ray))
            {
                counter++;
            }
        }
        return counter % 2 == 1;
    }
}
public class Point
{
    public double X { get; private set; }
    public double Y { get; private set; }
    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }
}
public class LineSegment
{
    public Point Point1 { get; private set; }
    public Point Point2 { get; private set; }

    public LineSegment(Point p1, Point p2)
    {
        Point1 = p1;
        Point2 = p2;
    }

    public bool IntersectsWithRay(Ray ray)
    {
        Line raySupport = new Line(ray.Origin, ray.AlsoGoesThrough);
        Line segmentSupport = new Line(Point1, Point2);

        Point intersection = raySupport.IntersectionWithOtherLine(segmentSupport);
        if (intersection == null) return false;

        bool intersectionOnRay = !((ray.Direction.Item1 == 1 && intersection.Y < ray.Origin.Y) || (ray.Direction.Item1 == -1 && intersection.Y > ray.Origin.Y) ||
            (ray.Direction.Item2 == 1 && intersection.X < ray.Origin.X) || (ray.Direction.Item1 == -1 && intersection.X > ray.Origin.X));

        double segmentMinX = Math.Min(Point1.X, Point2.X);
        double segmentMaxX = Math.Max(Point1.X, Point2.X);
        double segmentMinY = Math.Min(Point1.Y, Point2.Y);
        double segmentMaxY = Math.Max(Point1.Y, Point2.Y);
        bool intersectionOnSegment = intersection.X >= segmentMinX && intersection.X <= segmentMaxX && intersection.Y >= segmentMinY && intersection.Y <= segmentMaxY;

        return intersectionOnRay && intersectionOnSegment;
     }
}

public class Ray
{
    public Point Origin { get; private set; }
    public Point AlsoGoesThrough { get; private set; }

    public Tuple<int, int> Direction { get; private set; }
    // Item1 specifies up (1) or down (-1), Item2 specifies right (1) or left (-1)

    public Ray(Point origin, Point alsoGoesThrough)
    {
        Origin = origin;
        AlsoGoesThrough = alsoGoesThrough;

        int directionUp;
        if (origin.Y == alsoGoesThrough.Y)
        {
            directionUp = 0;
        }
        else if (origin.Y > alsoGoesThrough.Y)
        {
            directionUp = -1;
        }
        else // origin.Y < alsoGoesThrough.Y
        {
            directionUp = 1;
        }

        int directionRight;
        if (origin.X == alsoGoesThrough.X)
        {
            directionRight = 0;
        }
        else if (origin.X > alsoGoesThrough.X)
        {
            directionRight = -1;
        }
        else // origin.X < alsoGoesThrough.X
        {
            directionRight = 1;
        }

        Direction = new Tuple<int, int>(directionUp, directionRight);
    }
}

public class Line
{
    public double Slope { get; private set; }
    public double YOfIntersectionWithYAxis { get; private set; }

    public Line(Point goesThrough1, Point goesThrough2)
    {
        Slope = (goesThrough1.Y - goesThrough2.Y) / (goesThrough1.X - goesThrough2.X);
        YOfIntersectionWithYAxis = goesThrough1.Y - Slope * goesThrough1.X;
    }

    public Point IntersectionWithOtherLine(Line other)
    {
        if (Slope == other.Slope) return null;

        double intersectionX = (other.YOfIntersectionWithYAxis - YOfIntersectionWithYAxis) / (Slope - other.Slope);
        double intersectionY = Slope * intersectionX + YOfIntersectionWithYAxis;
        return new Point(intersectionX, intersectionY);
    }
}


[no name]

А что, если точка на самом деле находится снаружи, и луч просто касается одной точки? Это вполне возможно.

Thomas Daniels

Когда это происходит? Наиболее логичным ответом на этот вопрос было бы, когда луч пересекается с вершиной многоугольника, но тогда число пересечений все равно равно 2! Поскольку такая вершина является пересечением двух отрезков линии, то IntersectsWithRay вернет true дважды. Неужели я упускаю еще одно дело?

[no name]

Именно так. Геометрически он будет касаться только одной точки, но он будет касаться двух линий многоугольника. Ладно, это может сработать.

Рейтинг:
0

Jon McKee

Во-первых, я хотел бы объяснить свою методологию. Во-вторых, я объясню свой код.

Итак, во-первых, моя техника похожа на алгоритм литья лучей. Я дважды бросаю Луч в обоих направлениях и считаю пересечения по отдельности или вместе, что позволяет мне ловить точки на сторонах, таких как (4,1), (4,4). Моя техника для этого-домашняя. Так что давайте перейдем к делу:

Линия представлена $у = MX + б$ Таким образом, чтобы вычислить линии, которые могут пересекаться, мы проверяем их на соответствие значениям y тестовой точки и тестовых линий. Мы тестируем и то и другое как $> y$ во избежание многократных испытаний на соединительных линиях. Теперь перед нами встает интересный вопрос. Мы можем либо а) вычислить b, подключив одну из существующих точек, либо б) обнулить это вычисление с помощью преобразования оси.

Я выбрал вариант Б. Поясню: чтобы вычислить b, вам нужно создать границу для задачи. В стандартной плоскости xy эта граница может быть относительной, что требует вычисления b вручную. Но если вы преобразуете точки, поворачивая их на 90 градусов, вы можете связать проблему значениями y (которые уже ограничены лучом). Это означает, что вы можете установить b равным одной из уже определенных границ y. Сырой МС демонстрация краски.[^]

Это сохраняет относительное положение точек, одновременно облегчая наши вычисления. Итак, теперь мы определяем пересечение с линией на основе полуплоскости[^Луч, брошенный вправо, вычисляет левую полуплоскость, а Луч, брошенный влево,-правую полуплоскость. Это можно представить следующим образом $x < ((x_1 - x_2)/(y_1-y_2))*(y - y_2 ) + x_2$ и $x > ((x_1 - x_2)/(y_1-y_2))*(y - y_2 ) + x_2$ соответственно.

Мое решение, организованное и прокомментированное, выглядит следующим образом:

>>,>>>,>>,,<<<,>>,,>>>>>>,                                      Read test point and first two polygon points
[                                                               Until end of input
  [<<<<+>+>>>-]<<<[>>>+<<<-]                                    t0 = y2
  <<<<[>>>>+>+<<<<<-]>>>>>[<<<<<+>>>>>-]                        t1 = y
  <<[>>+<[>[-]>+<<-]>>[<<+>>-]<[<<[-]+<<<<<+>>>>>>>-]<-<-]>[-]  s2 = y2 greater than y
  <<<<<[>>>>+>+<<<<<-]>>>>>[<<<<<+>>>>>-]                       t0 = y1                                               
  <<<<[>>>>+>+<<<<<-]>>>>>[<<<<<+>>>>>-]                        t1 = y
  <<[>>+<[>[-]>+<<-]>>[<<+>>-]<[<<[-]+>>>>>+<<<-]<-<-]>[-]      s1 = y1 greater than y
  >>>>[<<<<+>>>>-]                                              t1 = s1
  <<<<<<<<<<[>>>>>>-<+<<<<<-]>>>>>[<<<<<+>>>>>-]>[>>>>+<<<<[-]] s1 = s1 != s2
  >>>>[-                                                        if (s1)
    <<<<<<[>+>+<<-]>>[<<+>>-]                                   t0 = x1
    <<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]                            t1 = x2 
    <<[>>+<[>[-]>+<<-]>>[<<+>>-]<[<<[-]+>>>>>+<<<-]<-<-]>[-]    s1 = x1 greater than x2 
    +>>>>[-<<<<-                                                if (s1)
      <<<[>>+<-<-]>>[<<+>>-]                                    x1 = x1 minus x2
    >>>>>]<<<<[-                                                else
      <<<[>>+>+<<<-]>>>[<<<+>>>-]<<[>-<-]>[<+>-]                x1 = x2 minus x1
    >]
    +<<<<<<[->>>>>>-                                            if (s2)
      >>>[<<<<+>+>>>-]<<<[>>>+<<<-]                             t0 = y2
      <<<<[>>>>+<-<<<-]>>>>[<<<<+>>>>-]                         t0 = y2 minus y
    <<<<<<]>>>>>>[-                                             else
      <<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]                         t0 = y
      >>>[<<<+<->>>>-]<<<[>>>+<<<-]                             t0 = y minus y2
    ]
    <<[>>>+<<<-]                                                t2 = x1
    >>>[<<[>+<<+>-]>[<+>-]>-]<<[-]                              x1 = x1 * t0
    <<<<[>>>>+>+<<<<<-]>>>>>[<<<<<+>>>>>-]                      t0 = y1
    >>>[<<<+>+>>-]<<[>>+<<-]                                    t1 = y2
    <<[>>+<[>[-]>+<<-]>>[<<+>>-]<[<<[-]+>>>>>+<<<-]<-<-]>[-]    s1 = y1 greater than y2
    +>>>>[-<<<<-                                                if (s1)
      >>>[<<+<<<<<<->>>>>>>>-]<<[>>+<<-]                        y1 = y1 minus y2
    >>>]<<<<[-                                                  else
       >>>[<<<+<+>>>>-]                                         t0 = y2
       <<<<<<<<[>>>>-<<<<-]>>>>[<<<<+>>>>-]>[>>>+<<<-]          y1 = y2 minus y1
    ]
    <<[>+<-]                                                    t0 = x1
    >[<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]
    <[>+<<-[>>[-]>+<<<-]>>>[<<<+>>>-]<[<-[<<->>[-]]+>-]<-]<<+>] x1 = x1 / y1
    <<[>+>+<<-]>>[<<+>>-]                                       x1 = x1 plus x2
    <<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]          t0 = x
    <<[>>>+>+<<<<-]>>>[<<<+>>>-]                                t3 = x1  
    <<[>+<-]>>>[<<->+>-]<[>+<-]<[<+>[-]]                        t0 = x != x1
    +<[->-                                                      if (t0)
      <<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]       t0 = x
      <[>>+>[<[-]<+>>-]<<[>>+<<-]>[<<[-]+<<<<<<<<[>>>+       
      <<<-]+>>>[<<<->>>-]>>>>>>>-]>-<<<-]>>>[-]<<<              c1 = x greater than x1 then !c2
      <<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]        t0 = x
      <<[>>>+<<[>>[-]<+<-]>[<+>-]>[<<<[-]+<<<<<<[>>+<<-]+         
      >>[<<->>-]>>>>>>>-]<<-<-]>[-]                             c2 = x less than x1 then !c2
    ]>[-                                                        else
      <<<<<<<<[>>+<<-]+>>[<<->>-]                               C2 = !C2
    >>>>>>]
  >>>>]
  <<<<<<[-]<<<[-]<[-]
  >>>[>+<-],,>>>>>>[<<<<<<<<+>>>>>>>>-],                        Copy (x2 y2) to (x1 y1) read (x2 y2)
]              
<<<<<<<<<<<<[>+<-]>[>>+<<[-]]>>[<<+>>-]                         c2 = c2 || c1
++++++++[<<++++++>>-]<<.                                        Print c

Memory: Pointer movement optimization prioritized.
----------------------------------------------------


Рейтинг:
0

OriginalGriff

Что ж... вы могли бы просто использовать стандартные методы фреймворка:

Point[] points = new Point[] { new Point(2, 0), new Point(4, 1), new Point(4, 4), new Point(2, 5), new Point(1, 2), new Point(2, 0 ) };
GraphicsPath path = new GraphicsPath();
Point last = points[0];
foreach (Point p in points)
    {
    if (p != last)
        {
        path.AddLine(last, p);
        last = p;
        }
    }
Region polygon = new Region(path);
if (polygon.IsVisible(new Point(3, 3)))
    {
    Console.WriteLine("In");
    }
else
    {
    Console.WriteLine("Out");
    }
if (polygon.IsVisible(new Point(5, 4)))
    {
    Console.WriteLine("In");
    }
else
    {
    Console.WriteLine("Out");
    }


Chris Maunder

+1 для краткости и понятности. -5 за переизбыток здравого смысла.

OriginalGriff

Я знал, что это будет моей погибелью! :смеяться:

#realJSOP

Может быть, Марк сможет сделать это в КОБОЛ.

Ramza360

Это хорошо работает, однако вам не нужен foreach, используйте GraphicsPath.AddLines (points) делает то же самое. Быстрое дополнение к вашему решению я добавил ниже.

И да это просто в c#

Рейтинг:
0

Niklas L

Злоупотребляя щедростью Криса в отношении типа данных, я сделал решение, которое имеет довольно хорошую сложность выполнения: p

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class point
{
public:
	bool x, y;
	point(bool _x, bool _y) : x(_x), y(_y) {}
	bool operator==(const point& o) const { return o.x == x && o.y == y; }
};

typedef vector<point> polygon;

bool is_point_in_polygon(const point& pt, const polygon& area)
{
	return find(area.begin(), area.end(), pt) != area.end();
}

int main()
{
	polygon area;
	area.push_back(point(true, true));
	area.push_back(point(true, false));
	area.push_back(point(false, true));

	point pt1(false, false);
	cout << (is_point_in_polygon(pt1, area) ? "Inside :)" : "Outside :(") << endl;

	point pt2(true, false);
	cout << (is_point_in_polygon(pt2, area) ? "Inside :)" : "Outside :(") << endl;

    return 0;
}
давая выход
Outside :(
Inside :)
Булево 2-D пространство-это мир грез при работе с геометрическими алгоритмами, поскольку оно содержит только 4 точки. Координата не может действительно лежать внутри многоугольника, но должна быть на одной вершине или снаружи.
Хотя булево двумерное пространство имеет мало практического применения, чисто теоретическая концепция почти так же плоха, если не хуже.
На следующей неделе я надеюсь на что-то более сложное, например, вычисление выпуклой оболочки или объединение полигонов. :)