Как определить, находится ли точка внутри многоугольника

Алгоритм прост...

Из проверяемой точки бросим луч. Посчитаем количество пересечений луча со сторонами многоугольника. Если это число чётно - значит точка снаружи, нечётно - внутри.

Если луч проходит через вершину, считаем это одним пересечением, а не двумя (хотя луч проходит через крайние точки двух сторон).

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

Если сторона лежит на луче - пересечение не засчитывается.

Вот и всё. Этот алгоритм работает для любых многоугольников.

Реализуем его при помощи класса TSegment.

type
  TAP = array of TPoint;
...
function IsPointInside(aPoint: TPoint; // Проверяемая точка
                       aPolygon: TAP   // Массив вершин многоугольника.
                      ): boolean;

var
    ray     : TSegment; // Луч.
    segment : TSegment; // Сторона многоугольника.
    i : integer;
    crossCnt : integer; // Счётчик пересечений.
    crossPoint: TPoint;
    epf : boolean;      // Признак того, что было пересечение с вершиной.
    npi : integer;      // Индекс следующей вершины.

    polygon: array of TPoint; // Внутренний массив с вершинами.

    // Циклический сдвиг индекса на Step вершин.
    // По сути - это сумма по модулю.
    procedure ShiftPolygonIndex(var aIndex: integer; Step: integer);
    begin
      aIndex := (aIndex + Step) mod Length(polygon);
    end;

begin
  // Переносим вершины в рабочий массив и
  //  и дублируем первую вершину, чтобы проверить 
  //  и сторону между первой и последней вершиной в массиве.
  SetLength(polygon, Length(aPolygon)+1);
  Move(aPolygon[0], polygon[0], Length(aPolygon)*SizeOf(aPolygon[0]));
  polygon[High(polygon)] := polygon[Low(polygon)];
  // Устанавливаем стартовые значения.
  crossCnt := 0;
  epf := false;
  // Создаём горизонтальный луч из точки вправо на бесконечность.
  ray := TSegment.Create(aPoint, Point(MaxInt, aPoint.Y));
  Try
    // Проходим по всем вершинам многоугольника.
    i := Low(polygon);
    while i<High(polygon) do
    begin
      // Создаём экземпляр стороны. Отрезок от текущей вершины до следующей.
      segment := TSegment.Create(polygon[i], polygon[i+1]);
      try
        // Проверяем пересечение луча с этой стороной.
        case ray.GetCrossPoint(segment, crossPoint) of
          crSinglePoint :    begin // Простое пересечение в середине стороны. 
                               inc(crossCnt);
                             end;
          crMyEndpoint,             // Точка лежит на стороне
          crDoubleEndpoint : begin  //  или совпадает с вершиной.
                               // Мы не внутри. Уходим.
                               result := false;
                               Exit;
                             end;
          crAlienEndpoint : begin // Луч проходит через вершину.
                              // Если луч проходит через первую точку массива
                              //  обрабатывать не нужно.
                              // Мы посчитаем это пересечение в последней точке.
                              if (i>0) or 
                                 ray.IsPointBelong(polygon[1]) then
                              begin
                                // Пересечение с вершиной считаем только первый раз.
                                if not(epf) then
                                begin
                                  // Пересечение с лучом в в вершине polygon[i+1]
                                  // Предыдущая вершина - polygon[i]
                                  // Следующая - polygon[i+2]
                                  // Найдём её индекс.
                                  npi := i;
                                  ShiftPolygonIndex(npi, 2);
                                  // Если следующая сторона вдоль луча,
                                  //  пропустим её.
                                  while ray.IsPointBelong(polygon[npi]) do
                                    ShiftPolygonIndex(npi, 1);
                                  // Если предыдущая и последующая вершины
                                  //  по разные стороны от луча - посчитаем пересечение.
                                  if Ray.IsDifferentSides(polygon[i], polygon[npi])
                                    then inc(crossCnt);
                                end;
                                epf := not(epf);
                              end;
                            end;
          // В остальных случаях ничего делать не нужно.
          // В том числе, и если проверяемая сторона лежит на луче Ray.
        end;
      finally
        segment.Free;
      end;
      inc(i);
    end;
    // Точка внутри, если crossCnt нечётное. 
    result := ((crossCnt and 1) = 1);
  Finally
    ray.Free;
  End;
end;

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