Skip to content
Snippets Groups Projects
Commit 4c01129e authored by tolgayurt's avatar tolgayurt
Browse files

added header doc_string for the to the packing_algo.py, and commented

polygon_creator.py
parent 07c3db5a
No related branches found
No related tags found
No related merge requests found
"""This module represents a approximation algorithm for the optimal packing of convex polygons.
The guideline and heart of this algorithm is the paper:
Alt H., de Berg M., Knauer C. (2015)
Approximating Minimum-Area Rectangular and Convex Containers for Packing Convex Polygons.
In: Bansal N., Finocchi I. (eds) Algorithms - ESA 2015. Lecture Notes in Computer Science,
vol 9294. Springer, Berlin, Heidelberg. https://doi.org/10.1007/978-3-662-48350-3_3
DOI:
https://doi.org/10.1007/978-3-662-48350-3_3
"""
import copy
import numpy
import math
......
# Zerlegen des Polygons
#import import_ipynb
"""
This module can cut a rectangular with known area into random convex polygons.
These convex polygons can be packed with the approximation algorithm from the module packing_algo.py.
The purpose is to compare the area between the packed container and the optimal rectangular area.
# Voronoi diagramm
import scipy.spatial as spatial
from collections import defaultdict
This module has two different algorithm to cut the rectangular into convex polygons.
"""
import scipy.spatial as spatial # package with voronoi diagramm
import random
import numpy
from shapely.geometry import Polygon, MultiPolygon
from shapely.geometry import Polygon
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]:
"""Cuts the given rectangle into several convex polygons
Workflow of the algorithm:
0. Check the convex polygon area and assign it to his interval, every interval has his own weight partner which
represent the chance to cut the convex polygon.
Weight 0.5 means a 50%, weight 1.0 means a 100% chance that the polygon goes threw steps 1-4 which means
it will be cut into two new convex polygons.
1. Chooses two random viable edges.
2. Takes a random point from each edge chosen in step 1
3. Builds the shortest distance between the points chosen in step 2
4. The distance in step 3 is the border which cuts the old convex polygon into two new ones.
5. Step 0-4 will repeated until the cut_count is 0 or all polygons stopped to get cut because of step 0.
Args:
rect_width (float): the width of the starting rectangular
rect_height (float): the height of the starting rectangular
cut_count (int): the maximal cut count, example cut_count=5 means 2^5 convex polygons as result
which would be the the best case where every convex polygon cut decision was True
intervals ([float]): the area intervals which sort the convex polygons into different weigh groups
weights ([float]): the weights which represent the chance that a polygon will be cut
render (bool): if True the cut steps will also be rendered else not
plot_width (int): the width of the plot objects
plot_height (int): the height of the plot objects
Returns:
all_polygons ([ConvexPolygon]): all cut convex polygons
"""
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)])
......@@ -22,17 +51,17 @@ def rectangle_cutter(rect_width: float, rect_height: float, cut_count: int, inte
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 = []
cut_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)
cut_polygons = cut_polygons + cut_polygon(polygon)
else:
polygon.plot_fill = True
small_area_polygons.append(polygon)
polygons_to_cut = cutted_polygons
polygons_to_cut = cut_polygons
cut_count = cut_count - 1
if render:
current_polygons = polygons_to_cut + small_area_polygons
......@@ -47,17 +76,42 @@ def rectangle_cutter(rect_width: float, rect_height: float, cut_count: int, inte
return all_polygons
def find_weight(x, intervals, weights):
def find_weight(area: float, intervals: [float], weights: [float]) -> float:
"""Finds the cut chance for the input area
Args:
area (float): the input area
intervals ([float]): the intervals in which the input area can fall
weights ([float]): the cut chances for every interval
Returns:
weights[index] (float): the cut chance for the input area
"""
for index in range(0, len(intervals)):
if x <= intervals[index]:
if area <= intervals[index]:
return weights[index]
def cut_polygon(polygon: ConvexPolygon) -> [ConvexPolygon]:
polygon = polygon.shell # polyog
"""Cuts a ConvexPolygon into two new ConvexPolygons
Steps:
1. Chooses two random edges from the input polygon
2. Takes a random point from each edge chosen in step 1
3. Builds the shortest distance between the points chosen in step 2
4. The distance in step 3 is the border which cuts the old convex polygon into two new ones.
Args:
polygon (ConvexPolygon): the convex input polygon which will be cut
Returns:
[ConvexPolygon(polygon_1), ConvexPolygon(polygon_2)]([ConvexPolygon]): two new ConvexPolygons
"""
polygon = polygon.shell
number_vertices = len(polygon)
if number_vertices < 3:
raise ValueError("Polygon has not enough vertices")
# Step 1 in the cut_polygon description
first_edge = numpy.random.randint(1, number_vertices)
second_edge = numpy.random.randint(1, number_vertices)
while first_edge == second_edge:
......@@ -68,17 +122,26 @@ def cut_polygon(polygon: ConvexPolygon) -> [ConvexPolygon]:
vertex_2_first_edge = polygon[first_edge]
vertex_1_second_edge = polygon[second_edge - 1]
vertex_2_second_edge = polygon[second_edge]
# Step 2-4 in the cut_polygon description
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)
"""Returns a random point from an edge, the edge is build by the two input points
Uses the equation of a straight line to return a random point from the edge.
Args:
vertex_1 (Point_xy): the first input vertex/point
vertex_2 (Point_xy): the second input vertex/point
Returns:
new_vertex (Point_xy): the random point on the edge
"""
if vertex_1[0] == vertex_2[0]:
low, high = vertex_1[1], vertex_2[1]
if low > high:
......@@ -92,11 +155,8 @@ def random_point_between_edge(vertex_1: Point_xy, vertex_2: Point_xy) -> Point_x
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])
slope = (vertex_1[1] - vertex_2[1]) / (vertex_1[0] - vertex_2[0]) # m = y2-y1/x2-x1
n = vertex_1[1] - (slope * vertex_1[0]) # n = y-m*x
low, high = vertex_1[1], vertex_2[1]
if low > high:
low, high = high, low
......@@ -106,9 +166,36 @@ def random_point_between_edge(vertex_1: Point_xy, vertex_2: Point_xy) -> Point_x
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):
def voronoi_polygons_wrapper(rect_width: float, rect_height: float, point_count: int) -> [ConvexPolygon]:
"""Cuts the input rectangle into voronoi cell polygons (they are convex)
Steps:
1. Creates n random points in the rectangle area, where n = point_count
2. Adds 4 distant dummy points which are far away from the rectangular to the random created points in steps 1,
the purpose of the dummy points is to be sure that the voronoi diagram is bigger than the rectangular, which
is important for step 4
3. creating the voronoi diagram with the scipy.spatial package
4. Intersect the voronoi regions which are not infinite with the input rectangular these intersections are the
cut convex polygons. The intersection is done with shapely package.
Help/Idea:
Using the scipy.spatial package for the voronoi diagram:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Voronoi.html
The idea with the dummy elements is from:
https://stackoverflow.com/questions/20515554/colorize-voronoi-diagram
Using the shapely package for the intersection between the input rectangle and finite voronoi regions:
https://pypi.org/project/Shapely/
Args:
rect_width (float): the width of the input rectangle
rect_height (float): the height of the input rectangle
point_count (int): the number of random points which will created in the rectangle area, every point create a
convex polygon
Returns:
polygon_list ([ConvexPolygon]): list of voronoi polygons cut from the input rectangle
"""
boundary = numpy.array([[0, 0], [0, rect_height], [rect_width, rect_height], [rect_width, 0], [0, 0]])
boundary_polygon = Polygon(boundary)
......@@ -121,14 +208,14 @@ def voronoi_polygons_wrapper(rect_width, rect_height, point_count):
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
# 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
# clipping the voronoi cell an creating a convex polygon
poly = Polygon(polygon)
clipped_poly = poly.intersection(boundary_polygon)
polygon_list.append(ConvexPolygon(clipped_poly.exterior.coords))
......@@ -136,6 +223,17 @@ def voronoi_polygons_wrapper(rect_width, rect_height, point_count):
def pre_decimal_finder(i: float) -> int:
"""Gives the number of pre_decimal places
This function helps to build the 4 voronoi dummy points which need to be far away from the rectangular which will
cut into voronoi cells.
Args:
i (float): the input value
Returns:
counter (int): the number of pre_decimal places
"""
if i < 0:
i = abs(i)
counter = 0
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment