def test_area(triangle: Polygon, is_counterclockwise: bool,
              fraction: Fraction) -> None:
    assume(0 < fraction < 1)
    requirement = triangle.area * fraction
    if not is_counterclockwise:
        triangle = Polygon(triangle.border.to_clockwise())
    pivot, low_area_point, high_area_point = triangle.border.vertices
    new_point = splitter_point(requirement=requirement,
                               pivot=pivot,
                               low_area_point=low_area_point,
                               high_area_point=high_area_point)
    new_triangle = Polygon(Contour((pivot, low_area_point, new_point)))
    assert new_triangle.area == requirement
Exemple #2
0
def splitter_point(requirement: float, pivot: Point, low_area_point: Point,
                   high_area_point: Point) -> Point:
    """Alternative to bisection search since we always have triangles"""
    if requirement <= 0:
        raise ValueError("Can't have a zero or negative requirement")
    p = pivot
    l = low_area_point
    h = high_area_point
    contour = Contour([p, l, h])
    triangle = Polygon(contour)
    if triangle.area < requirement:
        raise ValueError("Can't have a requirement greater than the area of "
                         "the triangle")
    r = requirement
    dx = h.x - l.x
    if dx == 0:
        if contour.orientation is Orientation.COUNTERCLOCKWISE:
            a = (p.y - l.y) / (p.x - l.x)
            b = (p.x * l.y - l.x * p.y - 2 * r) / (p.x - l.x)
            x = l.x
            y = a * l.x + b
            return Point(x, y)
        else:
            t = triangle.area - r
            a = (p.y - h.y) / (p.x - h.x)
            b = (p.x * h.y - h.x * p.y - 2 * t) / (p.x - h.x)
            x = h.x
            y = a * h.x + b
            return Point(x, y)
    k = (h.y - l.y) / dx
    m = h.y - k * h.x
    if contour.orientation is Orientation.COUNTERCLOCKWISE:
        dx = p.x - l.x
        if dx == 0:
            x = (2 * r + l.x * p.y - p.x * l.y) / (p.y - l.y)
            y = k * x + m
            return Point(x, y)
        a = (p.y - l.y) / dx
        b = (p.x * l.y - l.x * p.y - 2 * r) / dx
    else:
        t = triangle.area - r
        dx = p.x - h.x
        if dx == 0:
            x = (2 * t + h.x * p.y - p.x * h.y) / (p.y - h.y)
            y = k * x + m
            return Point(x, y)
        a = (p.y - h.y) / dx
        b = (p.x * h.y - h.x * p.y - 2 * t) / dx
    x = (m - b) / (a - k)
    y = a * x + b
    return Point(x, y)
Exemple #3
0
def to_graph(polygon: Polygon,
             extra_points: List[Point],
             *,
             convex_divisor: ConvexDivisorType) -> nx.Graph:
    """
    Converts polygon to a region-adjacency graph by dividing it to 
    parts using `convex_divisor` function. Resulting parts become 
    nodes connected when they touch each other.
    :param polygon: input polygon that will be split
    :param extra_points: list of points which will be used in
    splitting the polygon to convex parts
    :param convex_divisor: function to split the polygon into convex
    parts
    :return: graph with parts of the polygon as nodes;
    edges will keep `side` attributes with the touching segments.
    """
    graph = nx.Graph()
    polygon_border = polygon.border.raw()
    holes = list(map(Contour.raw, polygon.holes))
    site_points = list(map(Point.raw, extra_points))
    polygon_points = {*polygon_border, *chain.from_iterable(holes)}
    extra_points = list(set(site_points) - polygon_points)
    parts = convex_divisor(polygon_border,
                           holes,
                           extra_points=extra_points,
                           extra_constraints=())
    parts = [Polygon.from_raw((list(part), [])) for part in parts]
    if len(parts) == 1:
        graph.add_nodes_from(parts)
    else:
        if convex_divisor is constrained_delaunay_triangles:
            parts_per_sides = defaultdict(set)
            for part in parts:
                for side in edges(part.border):
                    parts_per_sides[side].add(part)
            for side, parts in parts_per_sides.items():
                if len(parts) == 2:
                    graph.add_edge(*parts, side=side)
        else:
            pairs: Iterator[Tuple[Polygon, Polygon]] = combinations(parts, 2)
            for part, other in pairs:
                intersection = part & other
                if isinstance(intersection, Segment):
                    graph.add_edge(part, other, side=intersection)
    return graph
Exemple #4
0
def joined_constrained_delaunay_triangles(
    border: ContourType,
    holes: Sequence[ContourType] = (),
    *,
    extra_points: Sequence[PointType] = (),
    extra_constraints: Sequence[SegmentType] = ()
) -> ConvexPartsType:
    """Joins polygons to form convex parts of greater size"""
    triangles = constrained_delaunay_triangles(
        border,
        holes,
        extra_points=extra_points,
        extra_constraints=extra_constraints)
    polygons = [
        Polygon.from_raw((list(triangle), [])) for triangle in triangles
    ]
    initial_polygon = polygons.pop()
    result = []
    while True:
        resulting_polygon = initial_polygon
        for index, polygon in enumerate(iter(polygons)):
            polygon_sides = set(edges(polygon.border))
            common_side = next((edge
                                for edge in edges(resulting_polygon.border)
                                if edge in polygon_sides), None)
            if common_side is None:
                continue
            has_point_on_edge = any(
                Point.from_raw(raw_point) in common_side
                for raw_point in extra_points)
            if has_point_on_edge:
                continue
            union_ = unite(resulting_polygon, polygon)
            if isinstance(union_, Polygon) and union_.is_convex:
                polygons.pop(index)
                resulting_polygon = union_
        if resulting_polygon is not initial_polygon:
            initial_polygon = resulting_polygon
            continue
        result.append(resulting_polygon.border.raw())
        if not polygons:
            return result
        initial_polygon = polygons.pop()
Exemple #5
0
 def plr(self,
         polygon: Polygon,
         splitter: Segment) -> RootedPolygons:
     """
     Portion of the current polygon to the right of the splitter
     plus all immediate ancestors accessible through the polygon
     edges that lie on the right of the splitter
     """
     vertices_in_interval = cut(polygon.border,
                                splitter.start,
                                splitter.end)
     if len(vertices_in_interval) < 3:
         current_polygon_part = EMPTY
     else:
         contour = Contour(shrink_collinear_vertices(
             Contour(vertices_in_interval)))
         current_polygon_part = Polygon(contour)
     pred_poly_by_line = self.pred_poly_by_line(polygon, splitter=splitter)
     return RootedPolygons(root=current_polygon_part,
                           predecessors=pred_poly_by_line)
Exemple #6
0
def _(geometry: Polygon) -> Polygon:
    return Polygon(to_fractions(geometry.border),
                   list(map(to_fractions, geometry.holes)))
Exemple #7
0
def orient(polygon: Polygon) -> Polygon:
    """To counterclockwise. No holes"""
    return Polygon(polygon.border.to_counterclockwise())
Exemple #8
0
def split(*,
          polygon: Polygon,
          vertices: List[Point],
          sites: FrozenSet[Requirement],
          graph: Graph
          ) -> Union[Tuple[nx.DiGraph, nx.DiGraph],
                     Tuple[nx.DiGraph, nx.DiGraph, nx.DiGraph]]:
    """Splits a PredPoly to 2 or 3 parts for further division"""
    if polygon.border.orientation is not Orientation.COUNTERCLOCKWISE:
        raise ValueError("Polygon division is implemented only for polygons "
                         "oriented counter-clockwise")
    sites_locations = set(site.point for site in sites)
    first_site_index, first_site_point = next(
        ((index, vertex) for index, vertex in enumerate(vertices[1:], start=1)
         if vertex in sites_locations), (0, vertices[0]))
    first_head_index = max(1, first_site_index)
    plrs = [graph.plr(polygon, Segment(vertices[0], vertex))
            for vertex in vertices[first_head_index:]]
    head_indices = range(first_head_index, len(vertices))
    heads = vertices[first_head_index:]
    if len(sites) == 1:
        sites_per_vertex = [sites] * len(vertices)
        requirements = [list(sites)[0].area] * len(vertices)
    else:
        sites_per_vertex = list(to_requirements_per_vertex(heads, sites))
        requirements = [sum(site.area for site in sites_)
                        for sites_ in sites_per_vertex]
    for (plr, head_index,
         requirement, right_sites) in zip(plrs, head_indices,
                                          requirements, sites_per_vertex):
        if plr.area >= requirement:
            break

    if plr.area >= requirement:
        if head_index == first_site_index:
            origins_indices = range(first_site_index - 1, -1, -1)
            origins = vertices[first_site_index - 1::-1]
            plrs = [graph.plr(polygon, Segment(origin, first_site_point))
                    for origin in origins]
            for plr, origin_index in zip(plrs, origins_indices):
                if plr.area >= requirement:
                    break
            pivot_index = head_index
            low_area_index = origin_index + 1
            high_area_index = origin_index
            splitter = Segment(vertices[low_area_index], vertices[head_index])
        else:
            pivot_index = 0
            low_area_index = head_index - 1
            high_area_index = head_index
            splitter = Segment(vertices[pivot_index], vertices[low_area_index])
        pivot_point = vertices[pivot_index]
        low_area_point = vertices[low_area_index]
        high_area_point = vertices[high_area_index]
        triangle = Polygon(Contour((pivot_point,
                                    low_area_point,
                                    high_area_point)))
        plr_1 = graph.plr(polygon, splitter)
        pll_1 = graph.pll(polygon, splitter)
        edge = (Segment(low_area_point, high_area_point)
                if pivot_index == 0
                else Segment(high_area_point, low_area_point))
        if plr_1.area + triangle.area > requirement:
            triangle_requirement = requirement - plr_1.area
            t = splitter_point(requirement=triangle_requirement,
                               pivot=pivot_point,
                               low_area_point=low_area_point,
                               high_area_point=high_area_point)
            triangle = Polygon(Contour([low_area_point, t, pivot_point]))
            a = to_subgraph(predecessors=plr_1.predecessors,
                            old_root=polygon,
                            new_root=plr_1.root | triangle,
                            sites=right_sites,
                            graph=graph)
            b = to_subgraph(predecessors=pll_1.predecessors,
                            old_root=polygon,
                            new_root=pll_1.root - triangle,
                            sites=sites - right_sites,
                            graph=graph)
            return a, b
        pred_by_line = graph.pred_poly_by_line(polygon, edge)
        plr_and_pred_by_line_area = plr_1.area + pred_by_line.area
        if plr_and_pred_by_line_area < requirement:
            requirement = requirement - plr_and_pred_by_line_area
            t = splitter_point(requirement=requirement,
                               pivot=pivot_point,
                               low_area_point=low_area_point,
                               high_area_point=high_area_point)
            triangle = Polygon(Contour([low_area_point, t, pivot_point]))
            edge = (Segment(low_area_point, t) if pivot_index == 0
                    else Segment(t, low_area_point))
            pred_by_line = graph.pred_poly_by_line(polygon, edge)
            a = to_subgraph(predecessors=plr_1.predecessors | pred_by_line,
                            old_root=polygon,
                            new_root=plr_1.root | triangle,
                            sites=right_sites,
                            graph=graph)
            b = to_subgraph(predecessors=pll_1.predecessors - pred_by_line,
                            old_root=polygon,
                            new_root=pll_1.root - triangle,
                            sites=sites - right_sites,
                            graph=graph)
            return a, b
        else:
            ps = Multipoint(low_area_point, high_area_point).centroid
            triangle = Polygon(Contour([low_area_point, ps, pivot_point]))
            edge = (Segment(low_area_point, high_area_point)
                    if pivot_index == 0
                    else Segment(high_area_point, low_area_point))
            pred_by_line = graph.pred_poly_by_line(polygon, edge)
            a = graph.subgraph(pred_by_line)
            b = to_subgraph(predecessors=plr_1.predecessors,
                            old_root=polygon,
                            new_root=plr_1.root | triangle,
                            sites=right_sites,
                            graph=graph)
            c = to_subgraph(predecessors=pll_1.predecessors - pred_by_line,
                            old_root=polygon,
                            new_root=pll_1.root - triangle,
                            sites=sites - right_sites,
                            graph=graph)
            return b, a, c
    else:
        t = Multipoint(vertices[-1], vertices[0]).centroid
        splitter = Segment(t, first_site_point)
        plr_1 = graph.plr(polygon, splitter)
        pll_1 = graph.pll(polygon, splitter)
        first_site_set = sites_per_vertex[0]
        a = to_subgraph(predecessors=plr_1.predecessors,
                        old_root=polygon,
                        new_root=plr_1.root,
                        sites=first_site_set,
                        graph=graph)
        b = to_subgraph(predecessors=pll_1.predecessors,
                        old_root=polygon,
                        new_root=pll_1.root,
                        sites=sites - first_site_set,
                        graph=graph)
        return b, a