Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
polygon_creator.py 6.25 KiB
# Zerlegen des Polygons
#import import_ipynb

# Voronoi diagramm
import scipy.spatial as spatial
from collections import defaultdict
import random
import numpy
from shapely.geometry import Polygon, MultiPolygon
from .packing_algo import ConvexPolygon, Point_xy, plot_polygons_in_single_plot


def rectangle_cutter(rect_width: float, rect_height: float, cut_count: int, intervals=[0, 0.01, 0.05, 1],
                     weights=[0, 0, 0.5, 1], render=False,
                     plot_width=700, plot_height=700) -> [ConvexPolygon]:
    if len(intervals) != len(weights):
        raise ValueError("the number of values from weights and intervals need to be the same")
    rectangle_polygon = ConvexPolygon([(0, 0), (0, rect_height), (rect_width, rect_height), (rect_width, 0), (0, 0)])
    max_area = rect_width * rect_height
    small_area_polygons = []
    polygons_to_cut = [rectangle_polygon]
    if cut_count < 0:
        raise ValueError("the cut cut_count need to be positive to cut the polygon")
    while cut_count > 0 and len(polygons_to_cut) > 0:
        cutted_polygons = []
        for polygon in polygons_to_cut:
            related_area = polygon.area / max_area
            weight = find_weight(related_area, intervals, weights)
            rand = random.random()
            if rand <= weight:
                cutted_polygons = cutted_polygons + cut_polygon(polygon)
            else:
                polygon.plot_fill = True
                small_area_polygons.append(polygon)
        polygons_to_cut = cutted_polygons
        cut_count = cut_count - 1
        if render:
            current_polygons = polygons_to_cut + small_area_polygons
            title = "Rectangle to Polygons=P.  cut_count: {}  P. count: {}  P. to cut: {}  P. stopped cutting: {}".format(
                cut_count, len(current_polygons), len(polygons_to_cut), len(small_area_polygons))
            fig = plot_polygons_in_single_plot(current_polygons, title=title, plot_width=plot_width,
                                               plot_height=plot_height)
    all_polygons = polygons_to_cut + small_area_polygons
    if render:
        for polygon in all_polygons:
            polygon.plot_fill = False
    return all_polygons


def find_weight(x, intervals, weights):
    for index in range(0, len(intervals)):
        if x <= intervals[index]:
            return weights[index]


def cut_polygon(polygon: ConvexPolygon) -> [ConvexPolygon]:
    polygon = polygon.shell  # polyog
    number_vertices = len(polygon)
    if number_vertices < 3:
        raise ValueError("Polygon has not enough vertices")
    first_edge = numpy.random.randint(1, number_vertices)
    second_edge = numpy.random.randint(1, number_vertices)
    while first_edge == second_edge:
        second_edge = numpy.random.randint(1, number_vertices)
    if first_edge > second_edge:
        first_edge, second_edge = second_edge, first_edge
    vertex_1_first_edge = polygon[first_edge - 1]
    vertex_2_first_edge = polygon[first_edge]
    vertex_1_second_edge = polygon[second_edge - 1]
    vertex_2_second_edge = polygon[second_edge]

    new_vertex_1 = random_point_between_edge(vertex_1_first_edge, vertex_2_first_edge)
    new_vertex_2 = random_point_between_edge(vertex_1_second_edge, vertex_2_second_edge)
    polygon_1 = polygon[:first_edge] + [new_vertex_1] + [new_vertex_2] + polygon[second_edge:]
    polygon_2 = [new_vertex_1] + polygon[first_edge:second_edge] + [new_vertex_2] + [new_vertex_1]
    #  s = [1,2,5,6,7] ,s[6:] => []; daher können wir für den spezial Fall second_edge== vertices_number so vorgehen
    return [ConvexPolygon(polygon_1), ConvexPolygon(polygon_2)]


def random_point_between_edge(vertex_1: Point_xy, vertex_2: Point_xy) -> Point_xy:
    new_vertex = (0, 0)
    if vertex_1[0] == vertex_2[0]:
        low, high = vertex_1[1], vertex_2[1]
        if low > high:
            low, high = high, low
        rand_number = numpy.random.uniform(low, high)
        new_vertex = (vertex_1[0], rand_number)
    elif vertex_1[1] == vertex_2[1]:
        low, high = vertex_1[0], vertex_2[0]
        if low > high:
            low, high = high, low
        rand_number = numpy.random.uniform(low, high)
        new_vertex = (rand_number, vertex_1[1])
    else:
        # y = m*x+n
        # n = y-m*x
        slope = (vertex_1[1] - vertex_2[1]) / (vertex_1[0] - vertex_2[
            0])  # m = y2-y1/x2-x1 Formel für die Geradensteigung mithilfe aus zwei verschiedenen Punkten der Geraden
        n = vertex_1[1] - (slope * vertex_1[0])
        low, high = vertex_1[1], vertex_2[1]
        if low > high:
            low, high = high, low
        rand_number = numpy.random.uniform(low, high)
        new_vertex_x = (rand_number - n) / slope  # x=(y-n)/m
        new_vertex = (new_vertex_x, rand_number)
    return new_vertex


# https://gist.github.com/pv/8036995
# https://stackoverflow.com/questions/20515554/colorize-voronoi-diagram/20678647#20678647
def voronoi_polygons_wrapper(rect_width, rect_height, point_count):
    boundary = numpy.array([[0, 0], [0, rect_height], [rect_width, rect_height], [rect_width, 0], [0, 0]])
    boundary_polygon = Polygon(boundary)

    x_values = numpy.random.uniform(low=0 + 1, high=rect_width - 1, size=(point_count,))
    y_values = numpy.random.uniform(low=0 + 1, high=rect_height - 1, size=(point_count,))
    points = list(zip(x_values, y_values))
    bigger = max(rect_width, rect_height)
    pre_number = pre_decimal_finder(bigger) + 3
    border_point = int(str(9) * pre_number)
    help_border = [[border_point, border_point], [-border_point, border_point], [border_point, -border_point],
                   [-border_point, -border_point]]
    points = numpy.append(points, help_border, axis=0)
    # compute Voronoi tesselation
    vor = spatial.Voronoi(points)
    polygon_list = []
    for region in vor.regions:
        if not -1 in region:
            polygon = [vor.vertices[i] for i in region]
            if len(polygon) > 2:
                # Clipping polygon
                poly = Polygon(polygon)
                clipped_poly = poly.intersection(boundary_polygon)
                polygon_list.append(ConvexPolygon(clipped_poly.exterior.coords))
    return polygon_list


def pre_decimal_finder(i: float) -> int:
    if i < 0:
        i = abs(i)
    counter = 0
    while i != 0:
        i = i // 10
        counter += 1
    return counter