Ejemplo n.º 1
0
def is_valid_polygon(polygon: Polygon) -> bool:
    """Check that no pair of edges in the given polygon properly intersects. Additionally check for general position."""
    for i, edge_i in enumerate(polygon.edges()):
        for j, edge_j in enumerate(polygon.edges()):
            if i != j and edge_i.properly_intersects(edge_j):
                return False
            if j not in ((i + 1) % len(polygon),
                         (i - 1) % len(polygon)) and edge_i.intersects(edge_j):
                return False

    # Make sure there are no three points on a line
    for p in polygon.points:
        for q in polygon.points:
            if p == q:
                continue
            for r in polygon.points:
                if p == r or q == r:
                    continue
                if Point.turn(p, q, r) == Point.NO_TURN:
                    return False

    return True
Ejemplo n.º 2
0
def in_subpolygon(polygon: Polygon, q1: Point, q2: Point, t: Point,
                  t_trapezoid: Trapezoid) -> bool:
    """O(1): Return whether `t` lies inside the subpolygon of `polygon` to the right of `q1`,`q2`."""
    assert isinstance(q1, (PolygonPoint, EdgePoint))
    assert isinstance(q2, (PolygonPoint, EdgePoint))
    assert isinstance(t, Point)
    assert isinstance(t_trapezoid, Trapezoid)
    assert isinstance(polygon, Polygon)

    # Because our line (q1,q2) can start and/or end on edges we need to be careful in taking decisions.

    # Find the vertex indices for q1,q2. If q1 lies on an edge we need to increase the index by one to have the smaller
    # subpolygon.
    ix1 = q1.index
    if isinstance(q1, EdgePoint):
        ix1 = polygon.next(ix1)
    # The second index does not need special treatment since the index already shrinks the polygon.
    ix2 = q2.index

    if ix1 == ix2:
        assert isinstance(q1, EdgePoint)
        return Point.turn(q1, q2, t) != Point.CCW_TURN

    # First we check in which part of the (possibly) smaller subpolygon our trapezoid lies.
    small_polygon_position = trapezoid_subpolygon_position(
        polygon, ix1, ix2, t_trapezoid)

    # If the trapezoid lies right to the line it is safe.
    if small_polygon_position == 1:
        return True
    # In the other cases we should have a closer look.

    # Now we widen the subpolygon s.t. we can check whether t lies completely outside our range.
    if isinstance(q1, EdgePoint):
        ix1 = polygon.prev(ix1)
    if isinstance(q2, EdgePoint):
        ix2 = polygon.next(ix2)

    # If the point does not lie inside this bigger subpolygon we can safely say it does not lie in the actual
    # subpolygon
    if ix1 != ix2:
        big_polygon_position = trapezoid_subpolygon_position(
            polygon, ix1, ix2, t_trapezoid)
        if big_polygon_position == small_polygon_position == -1:
            return False

    # Now the trapezoid lies somewhere between the small and the big subpolygon.
    # First we can decide with respect to the x-coordinates -- just checking where t lies with respect to line(q1,q2)
    # does not work!
    if t.is_right_of(q1) and t.is_right_of(q2):
        if ((isinstance(q1, EdgePoint) and q1.index == t_trapezoid.bot_edge_ix)
                or (isinstance(q2, EdgePoint)
                    and q2.index == t_trapezoid.top_edge_ix)):
            return True
        if ((isinstance(q1, EdgePoint) and q1.index == t_trapezoid.top_edge_ix)
                or (isinstance(q2, EdgePoint)
                    and q2.index == t_trapezoid.bot_edge_ix)):
            return False
    if t.left_of(q1) and t.left_of(q2):
        if ((isinstance(q1, EdgePoint) and q1.index == t_trapezoid.top_edge_ix)
                or (isinstance(q2, EdgePoint)
                    and q2.index == t_trapezoid.bot_edge_ix)):
            return True
        if ((isinstance(q1, EdgePoint) and q1.index == t_trapezoid.bot_edge_ix)
                or (isinstance(q2, EdgePoint)
                    and q2.index == t_trapezoid.top_edge_ix)):
            return False

    return Point.turn(q1, q2, t) != Point.CCW_TURN
Ejemplo n.º 3
0
def shortest_path(polygon: Polygon, s: Point, t: Point) -> Iterable[Point]:
    """O(n^2): Return the shortest path from s to t in the given polygon.

    This function uses only constant additional space and takes time O(n^2).

    Args:
        polygon: A polygon in counter-clockwise order.
        s: The start point (inside the polygon).
        t: The end point (inside the polygon).

    Returns:
        An iterator over the list of all vertex points of the polygonal chain
        representing the shortest geodesic path from s to t inside the polygon.

    Raises:
        AssertionError: A type check fails.
    """
    # ==========================================================================
    # Reset properties which can be accessed later on.
    # ==========================================================================
    shortest_path.properties = dict(iterations=0)

    # ==========================================================================
    # Type checking.
    # ==========================================================================

    assert isinstance(polygon, Polygon)
    assert isinstance(s, Point)
    assert isinstance(t, Point)

    # ==========================================================================
    # Trivial case: s == t.
    # ==========================================================================

    # In the very trivial case the start and end point are identical thus we can
    # just return without any calculation.
    if s == t:
        yield s
        return

    # Save s and t so we can output the original values even though we modify the
    # original ones
    original_s = s
    original_t = t

    # ==========================================================================
    # Locate s and t. Trivial case: both in same trapezoid.
    # ==========================================================================

    # Locate start and end point inside our polygon.
    s_trapezoid = polygon.trapezoid(s)
    t_trapezoid = polygon.trapezoid(t)

    # If s lies directly on trapezoid boundary shift it by some small value
    if s.x in (s_trapezoid.x_left, s_trapezoid.x_right):
        shift = min(s_trapezoid.x_right - s_trapezoid.x_left, 0.00002) / 2
        if s.x == s_trapezoid.x_left:
            s = Point(s.x + shift, s.y)
        else:
            s = Point(s.x - shift, s.y)
        s_trapezoid = polygon.trapezoid(s)

    # If t lies directly on trapezoid boundary shift it by some small value
    if t.x in (t_trapezoid.x_left, t_trapezoid.x_right):
        shift = min(t_trapezoid.x_right - t_trapezoid.x_left, 0.00002) / 2
        if t.x == t_trapezoid.x_left:
            t = Point(t.x + shift, t.y)
        else:
            t = Point(t.x - shift, t.y)
        t_trapezoid = polygon.trapezoid(t)

    # If both points are located inside the same trapezoid just return both in
    # order
    if s_trapezoid == t_trapezoid:
        yield original_s
        yield original_t
        return

    # ==========================================================================
    # Find next trapezoid when going from s to t. This is needed for
    # initialisation.
    # ==========================================================================

    # Find out whether we have to go left or right to find t
    go_left = t_trapezoid.is_left_of(s_trapezoid)
    # Get the neighbouring trapezoids only on the side we are looking at
    if go_left:
        neighbours = polygon.neighbour_trapezoids(s_trapezoid, 0b10)
    else:
        neighbours = polygon.neighbour_trapezoids(s_trapezoid, 0b01)

    # Each trapezoid side has at most 2 neighbours (due to not two
    # x-coordinates being the same). Furthermore if we need to go to one
    # side there has to be at least one neighbour.
    assert len(neighbours) in (1, 2)

    # Choose the first neighbour if we only have one or it lies in the right
    # direction
    if (len(neighbours) == 1
            or (go_left and t_trapezoid.is_left_of(neighbours[0]))
            or (not go_left and t_trapezoid.is_right_of(neighbours[0]))):
        next_trapezoid = neighbours[0]
    else:
        next_trapezoid = neighbours[1]

    # Get the boundary between the old and the new current trapezoid
    boundary = next_trapezoid.intersection(s_trapezoid)
    # Edges need to be oriented in counter-clockwise direction
    if Point.turn(original_s, boundary.a, boundary.b) == Point.CW_TURN:
        boundary.reverse()
    elif Point.turn(original_s, boundary.a, boundary.b) == Point.NO_TURN:
        # Edge is always oriented from top to bottom -- so if we go right it
        # should be reversed so to have it correct
        if not go_left:
            boundary.reverse()

    # We can now define our triple (p, q1, q2) as in the algorithm
    p = PolygonPoint(original_s)

    q1 = boundary.a
    if q1.edge is not None:
        q1 = EdgePoint(q1, q1.edge)
    else:
        q1 = PolygonPoint(q1, q1.index)

    q2 = boundary.b
    if q2.edge is not None:
        q2 = EdgePoint(q2, q2.edge)
    else:
        q2 = PolygonPoint(q2, q2.index)

    # ==========================================================================
    # Call make_step until we can see t.
    # ==========================================================================
    while not polygon.point_sees_other_point(p, original_t):
        shortest_path.properties['iterations'] += 1
        point, p, q1, q2 = make_step(p, q1, q2, original_t, polygon,
                                     t_trapezoid)
        if point:
            if point.tuple() == original_s.tuple():
                yield original_s
            else:
                yield point

    # ==========================================================================
    # Finish
    # ==========================================================================
    if p.tuple() == original_s.tuple():
        yield original_s
    else:
        yield p
    yield original_t
Ejemplo n.º 4
0
def make_step(p: PolygonPoint, q1: Point, q2: Point, t: Point,
              polygon: Polygon, t_trapezoid: Trapezoid) -> MakeStepResult:
    """O(n): Advance the given triple (p, q1, q2) towards t.

    Args:
        p:
        q1:
        q2:
        t:
        polygon:

    Returns:
    """
    # ==========================================================================
    # Type checking.
    # ==========================================================================
    assert isinstance(p, PolygonPoint)
    assert isinstance(q1, (PolygonPoint, EdgePoint))
    assert isinstance(q2, (PolygonPoint, EdgePoint))
    assert isinstance(t, Point)
    assert isinstance(t_trapezoid, Trapezoid)
    assert isinstance(polygon, Polygon)

    if isinstance(q1, PolygonPoint) and Point.turn(
            p, q1, polygon.succ(q1)) == Point.CW_TURN:
        q_prime = hit_polygon_boundary(p, q1, polygon)
        if in_subpolygon(polygon, q1, q_prime, t, t_trapezoid):
            return MakeStepResult(old_cusp=p,
                                  cusp=q1,
                                  right=polygon.succ(q1),
                                  left=q_prime)
        else:
            return MakeStepResult(old_cusp=None,
                                  cusp=p,
                                  right=q_prime,
                                  left=q2)

    elif isinstance(q2, PolygonPoint) and Point.turn(
            p, q2, polygon.pred(q2)) == Point.CCW_TURN:
        q_prime = hit_polygon_boundary(p, q2, polygon)
        if in_subpolygon(polygon, q_prime, q2, t, t_trapezoid):
            return MakeStepResult(old_cusp=p,
                                  cusp=q2,
                                  right=q_prime,
                                  left=polygon.pred(q2))
        else:
            return MakeStepResult(old_cusp=None,
                                  cusp=p,
                                  right=q1,
                                  left=q_prime)

    else:
        succ_q1 = polygon.succ(q1)
        if Point.turn(p, q1, succ_q1) != Point.CW_TURN and Point.turn(
                p, q2, succ_q1) != Point.CCW_TURN:
            # succ(q1) lies in wedge q1,p,q2
            q_prime = hit_polygon_boundary(p, succ_q1, polygon)
            if q_prime != q2:
                if p.squared_distance_to(q_prime) >= p.squared_distance_to(
                        succ_q1):
                    q_prime = succ_q1
                if p.index is None:
                    p_ = hit_polygon_boundary(q_prime, p, polygon)
                else:
                    p_ = p
                if in_subpolygon(polygon, p_, q_prime, t, t_trapezoid):
                    return MakeStepResult(old_cusp=None,
                                          cusp=p,
                                          right=q1,
                                          left=q_prime)
                else:
                    return MakeStepResult(old_cusp=None,
                                          cusp=p,
                                          right=q_prime,
                                          left=q2)

        pred_q2 = polygon.pred(q2)
        q_prime = hit_polygon_boundary(p, pred_q2, polygon)
        if p.squared_distance_to(q_prime) >= p.squared_distance_to(pred_q2):
            q_prime = pred_q2
        if p.index is None:
            p_ = hit_polygon_boundary(q_prime, p, polygon)
        else:
            p_ = p
        if in_subpolygon(polygon, q_prime, p_, t, t_trapezoid):
            return MakeStepResult(old_cusp=None,
                                  cusp=p,
                                  right=q_prime,
                                  left=q2)
        else:
            return MakeStepResult(old_cusp=None,
                                  cusp=p,
                                  right=q1,
                                  left=q_prime)
Ejemplo n.º 5
0
def jarvis_march(
    polygon: Polygon,
    start_index: int,
    end_index: int,
    direction: int,
    good_turn: int,
    predicate: Callable[[Point], T],
    ignore: Callable[[Point, Point],
                     bool] = lambda x: False) -> Iterable[Point]:
    """Do a Jarvis march on the given polygon.

    We start at start_index going into direction stopping at end_index. For
    every vertex we consider appropriate predicate is applied. If it yields
    something which not evaluates to False we immediately stop the march and
    return the predicate result together with the vertex and a list of all
    points visited beforehand.

    Args:
        ignore: A function which is called for every vertex and should return True iff this vertex is to be ignored
            during the jarvis march.
        polygon: A polygon.
        start_index: The index of the starting vertex.
        end_index: The index of the last vertex to consider.
        direction: The direction in which we walk along the polygon edge. Needs
            to be either 1 or -1.
        good_turn: If the current, the next and a third vertex form a turn that
            is the same as good_turn, the third will be chosen over the next.
        predicate: A function that takes a vertex as an argument and decides
            whether to continue the march or stop.

    Returns:
        A 3-tuple (result, point, visited) in which result is the result of the
        predicate function, point is the point which fulfils the predicate and
        visited is a list of vertices visited in between.

    Raises:
        AssertionError:
            a) A type check fails.
            b) None of the vertices in the specified range fulfilled
                the predicate.
    """
    # ==========================================================================
    # Type checking.
    # ==========================================================================
    assert isinstance(polygon, Polygon)
    assert isinstance(start_index, int)
    assert isinstance(end_index, int)
    assert isinstance(direction, int)
    assert isinstance(good_turn, int)

    first = polygon.point(start_index)
    while True:
        point_loc.properties['predicates'] += 1
        result = predicate(first)

        # If the result does not evaluate to False return
        if result:
            return result, first

        # If this assertion fails none of the vertices fulfilled the predicate
        assert first.index != end_index

        second = polygon.point(first.index + direction)

        if second.index != end_index:
            for index in polygon.indices(second.index + direction, end_index,
                                         direction):
                point = polygon.point(index)
                point_loc.properties['ignores_theo'] += 1
                if Point.turn(first, second, point) == good_turn:
                    point_loc.properties['ignores'] += 1
                    if not ignore(first, point):
                        second = point

        yield first
        first = second
Ejemplo n.º 6
0
def point_loc(polygon: Polygon, s: Point, t: Point) -> Iterable[Point]:
    """Return the shortest path from s to t in the given polygon.

    This function uses only constant additional space and takes time O(n^2).

    Args:
        polygon: A polygon in counter-clockwise order.
        s: The start point (inside the polygon).
        t: The end point (inside the polygon).

    Returns:
        An iterator over the list of all vertex points of the polygonal chain
        representing the shortest geodesic path from s to t inside the polygon.

    Raises:
        AssertionError:
            a) A type check fails.
            b) The number of neighbours found on one side of a trapezoid is not
                1 or 2.
            c) The Jarvis march throws an AssertionError.
    """
    # ==========================================================================
    # Reset properties which can be accessed later on.
    # ==========================================================================
    point_loc.properties = dict(iterations=0,
                                jarvis_marches=0,
                                predicates=0,
                                ignores=0,
                                ignores_theo=0)

    # ==========================================================================
    # Type checking.
    # ==========================================================================
    assert isinstance(polygon, Polygon)
    assert isinstance(s, Point)
    assert isinstance(t, Point)

    # ==========================================================================
    # Imports.
    # ==========================================================================
    from geometry import Funnel

    # ==========================================================================
    # Trivial case: s == t.
    # ==========================================================================
    # In the very trivial case the start and end point are identical thus we can
    # just return without any calculation.
    if s == t:
        yield s
        return

    # Save s and t so we can output the original values even though we modify the
    # original ones
    original_s = s
    original_t = t

    # ==========================================================================
    # Locate s and t. Trivial case: both in same trapezoid.
    # ==========================================================================
    # Locate start and end point inside our polygon.
    s_trapezoid = polygon.trapezoid(s)
    t_trapezoid = polygon.trapezoid(t)

    # If s lies directly on trapezoid boundary shift it by some small value
    if s.x in (s_trapezoid.x_left, s_trapezoid.x_right):
        shift = min(s_trapezoid.x_right - s_trapezoid.x_left, 0.00002) / 2
        if s.x == s_trapezoid.x_left:
            s = Point(s.x + shift, s.y)
        else:
            s = Point(s.x - shift, s.y)
        s_trapezoid = polygon.trapezoid(s)

    # If t lies directly on trapezoid boundary shift it by some small value
    if t.x in (t_trapezoid.x_left, t_trapezoid.x_right):
        shift = min(t_trapezoid.x_right - t_trapezoid.x_left, 0.00002) / 2
        if t.x == t_trapezoid.x_left:
            t = Point(t.x + shift, t.y)
        else:
            t = Point(t.x - shift, t.y)
        t_trapezoid = polygon.trapezoid(t)

    # If both points are located inside the same trapezoid just return both in
    # order
    if s_trapezoid == t_trapezoid:
        yield original_s
        yield original_t
        return

    # ==========================================================================
    # Preparation.
    # ==========================================================================
    # The cusp is the point we are always standing at and from which we can see
    # the trapezoid boundaries we are visiting. It gets updates in case we lose
    # visibility. We obviously start at the starting point.
    cusp = s
    # The funnel is our visibility angle.
    funnel = None
    # We also need to save the trapezoid we are currently in and the one we are
    # coming from
    current_trapezoid = s_trapezoid
    previous_trapezoid = None
    boundary = None
    previous_boundary = None

    # ==========================================================================
    # Walking the trapezoids.
    # ==========================================================================
    while current_trapezoid != t_trapezoid:
        point_loc.properties['iterations'] += 1
        # ----------------------------------------------------------------------
        # Finding the next trapezoid.
        # ----------------------------------------------------------------------

        # Find out whether we have to go left or right to find t
        go_left = t_trapezoid.is_left_of(current_trapezoid)
        # Get the neighbouring trapezoids only on the side we are looking at
        if go_left:
            neighbours = polygon.neighbour_trapezoids(current_trapezoid, 0b10)
        else:
            neighbours = polygon.neighbour_trapezoids(current_trapezoid, 0b01)

        # Each trapezoid side has at most 2 neighbours (due to not two
        # x-coordinates being the same). Furthermore if we need to go to one
        # side there has to be at least one neighbour.
        assert len(neighbours) in (1, 2)

        # Since we are going to select a new current trapezoid save it already
        previous_trapezoid = current_trapezoid

        # Choose the first neighbour if we only have one or it lies in the right
        # direction
        if (len(neighbours) == 1
                or (go_left and t_trapezoid.is_left_of(neighbours[0]))
                or (not go_left and t_trapezoid.is_right_of(neighbours[0]))):
            current_trapezoid = neighbours[0]
        else:
            current_trapezoid = neighbours[1]

        # Get the boundary between the old and the new current trapezoid
        previous_boundary = boundary
        boundary = current_trapezoid.intersection(previous_trapezoid)
        # Edges need to be oriented in counter-clockwise direction
        if Point.turn(cusp, boundary.a, boundary.b) == Point.CW_TURN:
            boundary.reverse()
        elif Point.turn(cusp, boundary.a, boundary.b) == Point.NO_TURN:
            # Edge is always oriented from top to bottom -- so if we go right it
            # should be reversed so to have it correct
            if not go_left:
                boundary.reverse()

        # On encountering the first boundary we do not have a funnel yet. We
        # then create it and start looking for the next trapezoid
        if funnel is None:
            funnel = Funnel(cusp, boundary.a, boundary.b)
            continue

        # ----------------------------------------------------------------------
        # Checking (and possibly updating) the visibility.
        # ----------------------------------------------------------------------

        # Check where both boundary end points are in respect to the funnel
        position_of_a = funnel.position_of(boundary.a)
        position_of_b = funnel.position_of(boundary.b)

        # Save whether both end points are on the same side of the funnel
        both_right_of = position_of_a == position_of_b == Funnel.RIGHT_OF
        both_left_of = position_of_a == position_of_b == Funnel.LEFT_OF

        # ----------------------------------------------------------------------
        # CASE 1: We do not see the boundary any more.
        # ----------------------------------------------------------------------
        if both_left_of or both_right_of:
            # The current view point will definitely change now.
            # We have to take care of the special case in which the cusp is the
            # starting point, because it might have been shifted by a small bit.
            if cusp == s:
                yield original_s
            else:
                yield cusp

            # ------------------------------------------------------------------
            # Prepare the Jarvis march
            # ------------------------------------------------------------------
            point_loc.properties['jarvis_marches'] += 1
            params = prepare_jarvis_march(polygon, funnel, current_trapezoid,
                                          both_right_of, go_left, boundary)

            # ------------------------------------------------------------------
            # Actually perform the Jarvis march
            # ------------------------------------------------------------------
            if previous_boundary is None:
                x_bound_point = boundary.a
            else:
                x_bound_point = previous_boundary.a
            if both_right_of:
                ignore_func = ignore_function((cusp, x_bound_point), funnel,
                                              Funnel.RIGHT_OF)
            else:
                ignore_func = ignore_function((cusp, x_bound_point), funnel,
                                              Funnel.LEFT_OF)

            # Since polygon.point_sees_edge2 returns a tuple of the two funnel
            # points we directly extract them. Additionally we get the new cusp
            # and yield all vertices visited until finding cusp.
            (v1, v2), cusp = yield from jarvis_march(
                polygon=polygon,
                predicate=partial(polygon.point_sees_edge2, edge=boundary),
                # ignore=lambda first, second: not polygon.point_sees_other_point(first, second),
                ignore=ignore_func,
                # ignore=lambda u: u.x > max(s.x, cusp.x, boundary.a.x) or
                #                  u.x < min(s.x, cusp.x, boundary.a.x),
                **params)

            # ------------------------------------------------------------------
            # Update the cusp and the funnel
            # ------------------------------------------------------------------

            # In the special case in which the cusp falls together with an end
            # point of our edge we advance the funnel point on the next edge
            # into the right direction.
            # (We only compare cusp with v1 since polygon.point_sees_edge
            # guarantees to return the funnel point which falls together first.)
            if v1 == cusp:
                # If the cusp falls together with the top right or bottom left
                # edge we choose the next counter-clockwise point
                if v1.index in (current_trapezoid.top_right_ix,
                                current_trapezoid.bot_left_ix):
                    v1 = polygon.point(v1.index + 1)
                # If the cusp falls together with the top left or bottom right
                # edge we choose the next clockwise point
                elif v1.index in (current_trapezoid.bot_right_ix,
                                  current_trapezoid.top_left_ix):
                    v1 = polygon.point(v1.index - 1)

                # Since v1 and v2 will be the funnel boundary points they
                # shall be in the right (counter-clockwise) order
                if Point.turn(cusp, v1, v2) == Point.CW_TURN:
                    v1, v2 = v2, v1

            # In some cases the funnel points returned by
            # polygon.point_sees_edge are not vertices but lie on polygon edges.
            # We do not want to have those points as funnel points since they
            # can suffer from floating point inaccuracies.
            # This should only be a problem iff the point lies on an edge
            # incident to the cusp for then the other endpoint may or may not be
            # found inside the funnel.
            if isinstance(
                    v1, IntersectionPoint) and v1.index is None and isinstance(
                        cusp, PolygonPoint):
                if v1.edge in (cusp.index, (cusp.index - 1) % polygon.len):
                    # For v1 we can safely choose the endpoint of the edge which is
                    # "more counter-clockwise"
                    v1 = polygon.point(v1.edge + 1)
            if isinstance(
                    v2, IntersectionPoint) and v2.index is None and isinstance(
                        cusp, PolygonPoint):
                if v2.edge in (cusp.index, (cusp.index - 1) % polygon.len):
                    # For v2 we can safely choose the endpoint of the edge which is
                    # "more clockwise"
                    v2 = polygon.point(v2.edge)

            funnel.cusp = cusp
            funnel.first = v1
            funnel.second = v2

        # ----------------------------------------------------------------------
        # CASE 2: We see the boundary but we need to shrink the visibility.
        # ----------------------------------------------------------------------
        else:
            # If needed (i.e. if the edge reduces the funnel) update the
            # second and first funnel point
            if funnel.contains(boundary.a) and boundary.a.index is not None:
                funnel.first = boundary.a
            if funnel.contains(boundary.b) and boundary.b.index is not None:
                funnel.second = boundary.b

    # ==========================================================================
    # Do the final Jarvis march
    # ==========================================================================
    # We have to take care of the special case in which the cusp is the
    # starting point, because it might have been shifted by a small bit.
    if cusp == s:
        yield original_s
    else:
        yield cusp

    if not polygon.point_sees_other_point(cusp, t):
        # Save whether the previous polygon is left or right of the
        # current one
        go_left = previous_trapezoid.is_right_of(current_trapezoid)

        point_loc.properties['jarvis_marches'] += 1
        params = prepare_jarvis_march(polygon, funnel, current_trapezoid,
                                      funnel.position_of(t) == Funnel.RIGHT_OF,
                                      go_left)

        # ------------------------------------------------------------------
        # Actually perform the Jarvis march
        # ------------------------------------------------------------------

        if funnel.position_of(t) == Funnel.RIGHT_OF:
            ignore_func = ignore_function((cusp, boundary.a), funnel,
                                          Funnel.RIGHT_OF)
        else:
            ignore_func = ignore_function((cusp, boundary.a), funnel,
                                          Funnel.LEFT_OF)

        # Since we are nearly finished we only care about the list of visited
        # nodes and the new cusp (which is not contained in the list)
        _, cusp = yield from jarvis_march(
            polygon=polygon,
            predicate=partial(polygon.point_sees_other_point, other_point=t),
            # ignore=lambda first, second: not polygon.point_sees_other_point(first, second),
            ignore=ignore_func,
            # ignore=lambda u: u.x > max(s.x, cusp.x, t.x, boundary.a.x) or
            #                  u.x < min(s.x, cusp.x, t.x, boundary.a.x),
            **params)

        yield cusp

    yield original_t
Ejemplo n.º 7
0
def test_can_create_circle(p1, p2, p3):
    """Check that we can always create a circle if the points do not lie on one line."""
    assume(Point.turn(p1, p2, p3) != Point.NO_TURN)
    Circle(p1, p2, p3)