def find_supporting_pair(
        point: TPoint, polygon: TPolygon, polygon_number: int,
        angles: Optional[TAngles]) -> Optional[Tuple[PointData, PointData]]:
    """
        Find pair of supporting points from point to polygon in log time (binary search).

        :param point: visibility point (x, y)
        :param polygon: convex, given counter-clockwise, first and last points must be equal
        :param polygon_number: sequence number of polygon for PointData
        :param angles: tuple of polar angles from #0 point of polygon to all others except itself
        :return: pair of PointData of supporting points or None if unable to find
    """

    polygon_size = len(polygon) - 1
    assert polygon_size >= 2
    assert compare_points(polygon[0], polygon[-1])
    if polygon_size <= 3:
        return find_supporting_pair_semiplanes(point, polygon, polygon_number)
    assert turn(polygon[0], polygon[1], polygon[2]) >= 0
    assert len(angles) == polygon_size - 1

    start_to_point = localize_convex(point, polygon, angles, False)

    # ray polygon[0], point intersects polygon
    if start_to_point[1] is not None:
        index1 = find_supporting_point(point, polygon, 0, start_to_point[1],
                                       True)
        if index1 is None:
            return None
        index2 = find_supporting_point(point, polygon, start_to_point[1],
                                       polygon_size - 1, False)
        if index2 is None:
            return None
    else:
        point_to_start = localize_convex(point, polygon, angles, True)

        # ray polygon[0], point does not intersect polygon
        if point_to_start[1] is not None:
            index1 = find_supporting_point(point, polygon, 0,
                                           point_to_start[1], False)
            if index1 is None:
                return None
            index2 = find_supporting_point(point, polygon, point_to_start[1],
                                           polygon_size - 1, True)
            if index2 is None:
                return None

        # polygon[0] is supporting point
        else:
            index1 = 0
            index2 = find_supporting_point(
                point, polygon, 1, polygon_size - 1,
                turn(polygon[0], polygon[1], point) >= 0)
            if index2 is None:
                return None

    return (polygon[index1], polygon_number, index1, True,
            0), (polygon[index2], polygon_number, index2, True, 0)
def localize_convex_linear(point: TPoint, polygon: TPolygon) -> bool:
    """
    Localize point inside convex polygon in linear time.

    :param point: point to be localized (x, y)
    :param polygon: convex, given counter-clockwise, first and last points must be equal
    """

    polygon_size = len(polygon) - 1
    assert polygon_size >= 2
    assert compare_points(polygon[0], polygon[-1])
    if polygon_size <= 2:
        return False
    polygon_cross = turn(polygon[0], polygon[1], polygon[2])
    for i in range(polygon_size):
        if turn(polygon[i], polygon[i + 1], point) * polygon_cross <= 0:
            return False
    return True
示例#3
0
def find_supporting_pair_brute(point, polygon, polygon_size, point_number):
    result = list()
    for i in range(polygon_size):
        pi = polygon[i]
        if point_number is not None and i == point_number:
            continue
        if turn(point, pi, polygon[(i - 1) % polygon_size]) * turn(point, pi, polygon[(i + 1) % polygon_size]) < 0:
            continue

        # check intersection with all other points
        for j in range(polygon_size):
            if j in (i - 1, i) or (point_number is not None and j in (point_number - 1, point_number)):
                continue
            if check_ray_segment_intersection(point, pi, polygon[j], polygon[j + 1], False):
                break
        else:
            result.append(i)

    if len(result) != 2:
        return None
    point1, point2 = result
    return point1, point2
def find_supporting_pair_semiplanes(point: TPoint, polygon: TPolygon,
                                    polygon_number: int) -> Optional[tuple]:
    """
        Find pair of supporting points from point to polygon in linear line.

        :param point: visibility point (x, y)
        :param polygon: convex, first and last points must be equal
        :param polygon_number: sequence number of polygon for PointData
        :return: pair of PointData of supporting points or None if unable to find
    """

    polygon_size = len(polygon) - 1
    assert polygon_size >= 2
    assert compare_points(polygon[0], polygon[-1])
    if polygon_size == 2:
        return (polygon[0], polygon_number, 0, True,
                0), (polygon[1], polygon_number, 1, True, 0)

    semiplanes = [1] * polygon_size
    count = 0
    polygon_turn = turn(polygon[0], polygon[1], polygon[2])

    # fill an array of angles containing (1) or not containing (0) point (2 subsets)
    for i in range(polygon_size):
        if turn(polygon[i % polygon_size], polygon[
            (i + 1) % polygon_size], point) * polygon_turn < 0:
            semiplanes[i] = 0
            count += 1

    if count in (0, polygon_size):
        return None

    start = semiplanes.index(1) if semiplanes[0] == 0 else semiplanes.index(0)
    end = (len(semiplanes) - semiplanes[::-1].index(1)) % polygon_size if semiplanes[0] == 0 else \
        (len(semiplanes) - semiplanes[::-1].index(0)) % polygon_size
    return (polygon[start], polygon_number, start, True,
            0), (polygon[end], polygon_number, end, True, 0)
    def get_edges_sweepline(self, point: TPoint) -> List[PointData]:
        # list of points sorted by angle
        points = list()
        for edge in self.__segments:
            points.append((edge[0], edge[1]))  # keep info about pair and PointData
            points.append((edge[1], edge[0]))  # keep info about pair and PointData
        points.sort(key=lambda x: polar_angle(point, x[0][0]))

        # list of segments intersected by 0-angle ray
        intersected = OrderedDict()
        for edge in self.__segments:
            if check_ray_segment_intersection(point, (point[0] + 1, point[1]), edge[0][0], edge[1][0], True):
                index1 = str((edge[0][1], edge[0][2], edge[0][3] | 0))
                index2 = str((edge[1][1], edge[1][2], edge[1][3] | 0))
                intersected[index1 + index2] = (edge[0][0], edge[1][0])
        self.__segments.clear()

        visible_edges = dict()
        for p in points:
            # check if any of the segments in intersected list crosses current segment
            for segment in reversed(intersected.values()):
                if check_segment_intersection(point, p[0][0], segment[0], segment[1]):
                    break
            else:
                if self.__restriction_pair is None:
                    visible_edges[str(p[0][1]) + str(p[0][2]) + str(p[0][3] | 0)] = p[0]
                else:
                    l_point, r_point = self.__restriction_pair
                    if point_in_angle(p[0][0], l_point, self.__restriction_point, r_point) != self.__reverse_angle:
                        visible_edges[str(p[0][1]) + str(p[0][2]) + str(p[0][3] | 0)] = p[0]

            # update intersected list
            index1 = str((p[0][1], p[0][2], p[0][3] | 0))
            index2 = str((p[1][1], p[1][2], p[1][3] | 0))
            if turn(point, p[0][0], p[1][0]) > 0:
                intersected[index1 + index2] = (p[0][0], p[1][0])
            else:
                intersected.pop(index1 + index2, None)

        return list(visible_edges.values())
def find_supporting_point(point: TPoint, polygon: TPolygon, low: int,
                          high: int, low_contains: bool) -> Optional[int]:
    """
    Find supporting point from point to polygon (index between low and high) with binary search.

    :param point: visibility point (x, y)
    :param polygon: convex, given counter-clockwise, first and last points must be equal
    :param low: binary search algorithm parameter (polygon min index)
    :param high: binary search algorithm parameter (polygon max index)
    :param low_contains: angle formed by polygon[low] contains point (True) or not (False)
    :return: index of supporting point from point to polygon or None if unable to find
    """

    polygon_size = len(polygon) - 1
    assert polygon_size >= 3
    assert compare_points(polygon[0], polygon[-1])
    assert turn(polygon[0], polygon[1], polygon[2]) >= 0

    mid = (high + low) // 2
    while low <= high and mid < polygon_size:

        # supporting point separating 2 subsets is found
        if turn(polygon[mid], polygon[(mid + 1) % polygon_size], point) <= 0 and \
                turn(polygon[(mid - 1) % polygon_size], polygon[mid], point) >= 0 or \
                turn(polygon[mid], polygon[(mid + 1) % polygon_size], point) >= 0 and \
                turn(polygon[(mid - 1) % polygon_size], polygon[mid], point) <= 0:
            return mid

        # update mid
        if low_contains and turn(polygon[mid], polygon[(mid + 1) % polygon_size], point) >= 0 or \
                not low_contains and turn(polygon[mid], polygon[(mid + 1) % polygon_size], point) <= 0:
            low = mid + 1
        else:
            high = mid - 1
        mid = (high + low) // 2

    return None
def localize_convex(point: TPoint,
                    polygon: TPolygon,
                    angles: Optional[TAngles],
                    reverse_angle: bool = False) -> Tuple[bool, Optional[int]]:
    """
    Localize point inside convex polygon in log time (Preparata-Shamos algorithm).

    :param point: point to be localized (x, y)
    :param polygon: convex, given counter-clockwise, first and last points must be equal
    :param angles: tuple of polar angles from #0 point of polygon to all others except itself
    :param reverse_angle: point angles should be turned on pi (True) or not (False)
    :return: tuple of 2 elements:
        1. bool - point is inside polygon
        2. None if point is inside polygon else int - number of polygon vertex where point_angle is located.
    """

    polygon_size = len(polygon) - 1
    assert polygon_size >= 2
    assert compare_points(polygon[0], polygon[-1])
    if polygon_size <= 2:
        return False, None
    assert turn(polygon[0], polygon[1], polygon[2]) >= 0
    assert len(angles) == polygon_size - 1

    if compare_points(point, polygon[0]):
        return True, None
    point_angle = polar_angle(point,
                              polygon[0]) if reverse_angle else polar_angle(
                                  polygon[0], point)

    # point angle not between angles
    if angles[-1] < pi < angles[0]:
        if angles[-1] <= point_angle <= angles[0]:
            return False, None
    elif point_angle <= angles[0] or point_angle >= angles[-1]:
        return False, None

    angles_len = len(angles)
    mid = (angles_len - 1) // 2
    low = 0
    high = angles_len - 1
    while low <= high:
        if mid + 1 == angles_len:
            return False, None
        angle1 = angles[mid]
        angle2 = angles[mid + 1]

        # 2 angles contain zero-angle
        if angle1 > pi > angle2:
            if point_angle >= angle1 or point_angle <= angle2:
                return turn(polygon[mid + 1], polygon[mid + 2],
                            point) > 0, mid + 2
            if point_angle > pi:
                high = mid - 1
            if point_angle < pi:
                low = mid + 1
        else:
            if angle1 <= point_angle <= angle2:
                return turn(polygon[mid + 1], polygon[mid + 2],
                            point) > 0, mid + 2
            if point_angle - pi > angle2:
                high = mid - 1
            elif point_angle + pi < angle1:
                low = mid + 1
            elif point_angle < angle1:
                high = mid - 1
            else:
                low = mid + 1
        mid = (high + low) // 2
    return False, None
示例#8
0
def check_polygon_direction(polygon: TPolygon) -> TPolygon:
    return tuple(reversed(polygon)) if len(polygon) >= 3 and turn(
        polygon[0], polygon[1], polygon[2]) < 0 else polygon