From 07c3db5ade30f7153df11d395e1d9a65efe9e905 Mon Sep 17 00:00:00 2001
From: Tolga Yurtseven <tolgayurt02@outlook.de>
Date: Mon, 16 Nov 2020 13:12:02 +0100
Subject: [PATCH] refactored and wrote doc_string for the whole packing_algo.py

---
 mysite/plots/packing_algo.py                  | 564 +++++++++++-------
 .../templates/plots/packed_polygons.html      |   2 +-
 mysite/plots/views.py                         |   2 +-
 3 files changed, 351 insertions(+), 217 deletions(-)

diff --git a/mysite/plots/packing_algo.py b/mysite/plots/packing_algo.py
index d791ef49..e2710c59 100644
--- a/mysite/plots/packing_algo.py
+++ b/mysite/plots/packing_algo.py
@@ -3,7 +3,7 @@ import numpy
 import math
 import itertools
 
-# tree datastructure
+# tree data structure
 from . import avl_tree
 
 # shapely/polygon packages
@@ -12,7 +12,7 @@ from shapely import affinity
 
 # plotting packages
 from bokeh.plotting import figure, Figure
-from bokeh.io import output_notebook, show
+from bokeh.io import show
 from bokeh.layouts import gridplot, layout, column, Column, Row
 from bokeh.models import Div
 from bokeh.models import Legend
@@ -67,7 +67,7 @@ class ConvexPolygon(object):
         self.__slope = self.set_slope()
         self.plot_fill = False
 
-    # all these @property decorators helps to make the attributes private
+    # all these @property decorators helps to make the attributes "private"
     @property
     def area(self):
         return self.__area
@@ -124,7 +124,8 @@ class ConvexPolygon(object):
     def slope(self):
         return self.__slope
 
-    def convex_hull_and_area(self, shell: [Point_xy]) -> ([Point_xy], float):
+    @staticmethod
+    def convex_hull_and_area(shell: [Point_xy]) -> ([Point_xy], float):
         """Builds the convex hull of the input points and creates a convex polygon with his area
 
         This function creates a shapely (a python package) polygon to use the shapely convex hull function,
@@ -138,7 +139,7 @@ class ConvexPolygon(object):
         Returns:
             shell, area ([Point_xy], float): a tuple of the convex polygon shell and polygon area
         """
-        if shell == None:
+        if shell is None:
             raise ValueError("a convex polygon can't intialized with None")
         elif shell == [] or len(shell) < 3:
             raise ValueError("a convex polygon needs at least 3 points")
@@ -284,7 +285,7 @@ class ConvexPolygon(object):
         spine_top_found = False
         spine_bottom_not_horizontal = False
         # cycling threw the vertices and searching for the spine bottom, which will be the start point
-        while spine_bottom_found == False:
+        while not spine_bottom_found:
             if help_array[0] == spine_bottom:
                 # if the bottom_spine_vertex has a horizontal neighbour, do not take it to the right_visible_vertices
                 if spine_bottom[1] != help_array[-1][1]:
@@ -293,7 +294,7 @@ class ConvexPolygon(object):
                 left.append(help_array.pop(0))
             else:
                 help_array.append(help_array.pop(0))
-        while spine_top_found == False:  # searching the spine top
+        while not spine_top_found:  # searching the spine top
             if help_array[0] == spine_top:
                 spine_top_found = True
                 # if the top_spine_vertex has a horizontal neighbour, do not take it to the left_visible_vertices
@@ -333,7 +334,7 @@ class HighClass(object):
         self.h_max_polygon = h_max_polygon
         self.w_max_polygon = w_max_polygon
         self.min_border = alpha ** (i + 1) * h_max_polygon
-        self.max_border = alpha ** (i) * h_max_polygon
+        self.max_border = alpha ** i * h_max_polygon
         self.polygons = []
         self.spine_ordered_polygons = []
 
@@ -349,7 +350,7 @@ class HighClass(object):
         spine_ordered_polygons = sorted(self.polygons, key=lambda polygon: polygon.slope)
         self.spine_ordered_polygons = spine_ordered_polygons
 
-    def plot_hc(self, ordered=False, render=True, plot_width=300, plot_height=300) -> Column:
+    def plot_hc(self, ordered=False, render=True, plot_width=300, plot_height=300, columns=4) -> Column:
         """Plot object of the polygons of the HighClass with ordered or unordered spines
 
         Args:
@@ -357,9 +358,9 @@ class HighClass(object):
             render (bool): if True -> the plot will be rendered else not
             plot_width (int): width of the plot figure
             plot_height (int): height of the plot figure
-
+            columns (int): the number of columns in which the plot is shown
         Returns:
-            grid_layout (Colums): plot object which can be rendered with show(grid_layout)
+            grid_layout (Column): plot object which can be rendered with show(grid_layout)
         """
         plots = []
         if ordered:
@@ -369,7 +370,7 @@ class HighClass(object):
         for counter, polygon in enumerate(polygon_list):
             title = "Polygon {}".format(counter)
             plots.append(polygon.plot_polygon(title=title, render=False))
-        grid = gridplot(plots, ncols=4, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
+        grid = gridplot(plots, ncols=columns, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
         grid_title = Div(text="<b>Hc_{}&nbsp;&nbsp;height:&nbsp;{}-{}&nbsp;&nbsp;alpha: {}<b>".format(self.i, truncate(
             self.min_border, 1), truncate(self.max_border, 1), self.alpha))
         grid_layout = column(grid_title, grid)
@@ -389,7 +390,7 @@ class Container(object):
         y_boundary (float): y coordinate boundary for the container dependent of the High_Class max_boundary
         x_boundary (float): x coordinate boundary fot the container
         Tree (Tree): avl-tree data structure
-        root (TreeNode): the root of the avl-tree datastructure
+        root (TreeNode): the root of the avl-tree data structure
         sigma ([ConvexPolygon]): the list of packed convex polygons from the HighClass
         plot_steps ([Figure]): list of steps which are done to pack the container as plot objects
 
@@ -404,7 +405,8 @@ class Container(object):
         self.root = None
         self.sigma, self.plot_steps = self.packing_container(self.hc.spine_ordered_polygons)
 
-    def distance(self, edge_points: (Point_xy, Point_xy), point: Point_xy) -> float:
+    @staticmethod
+    def distance(edge_points: (Point_xy, Point_xy), point: Point_xy) -> float:
         """Calculates the shortest distance between an edge and a point
 
         Args:
@@ -429,7 +431,8 @@ class Container(object):
             distance = math.sqrt((intersection_point[0] - point[0]) ** 2 + (intersection_point[1] - point[1]) ** 2)
         return distance
 
-    def find_corresponding_edge(self, vertex: Point_xy, vertices_visible_left: [Point_xy]) -> (Point_xy, Point_xy):
+    @staticmethod
+    def find_corresponding_edge(vertex: Point_xy, vertices_visible_left: [Point_xy]) -> (Point_xy, Point_xy):
         """This function finds the corresponding edge for a vertex which is already packed and visible from right.
 
         To find the corresponding edge the vertices visible from left of the polygon which will be moved to the left
@@ -597,7 +600,6 @@ class Container(object):
                 x = vertex_r[0]
                 corresponding_point_x = x - distance
                 y = vertex_r[1]
-                text_point_x, text_point_y = (x - distance / 2, y)
                 distance_int = int(distance)
                 color = "blue"
                 if distance_int == int(min_distance):
@@ -618,7 +620,6 @@ class Container(object):
                 x = vertex_l[0]
                 corresponding_point_x = x + distance
                 y = vertex_l[1]
-                text_point_x, text_point_y = (x + distance / 2, y)
                 distance_int = int(distance)
                 color = "green"
                 if distance_int == int(min_distance):
@@ -651,21 +652,21 @@ class Container(object):
             show(fig)
         return fig
 
-    def plot_container_steps(self, render=True, colums=2, plot_width=600, plot_height=600) -> Column:
+    def plot_container_steps(self, render=True, columns=2, plot_width=600, plot_height=600) -> Column:
         """Build a plot object which includes all container packing steps/plots
 
         This plot is a grid plot, which means that it is result of merging several plots.
 
         Args:
             render (bool): If True the plot will be rendered else not
-            colums (int): the number of colums in which the packing steps are listed
+            columns (int): the number of columns in which the packing steps are listed
             plot_width (int): the width of the single plot objects
             plot_height (int): the height of the single plot objects
 
         Returns:
             grid_layout(Column): A plot object with all packing steps inside
         """
-        grid = gridplot(self.plot_steps, ncols=colums, plot_width=plot_width, plot_height=plot_height,
+        grid = gridplot(self.plot_steps, ncols=columns, plot_width=plot_width, plot_height=plot_height,
                         toolbar_location="left")
         min_border = (int(self.hc.min_border * 10)) / 10
         max_border = (int(self.hc.max_border * 10)) / 10
@@ -684,7 +685,7 @@ class MiniContainer(object):
     The Container which got packed/build from a HighClass got "theoretically" divided  into boxes. A mini-container is
     an extension of the width of one these boxes.
     The purpose is to have mini-containers with fixed widths so they easily can be stacked, when the
-    RectengularContainer is build.
+    RectangularContainer is build.
 
     Args:
         max_width (float): is the maximum width for all mini-containers which will be created
@@ -755,12 +756,12 @@ class MiniContainer(object):
         # building the custom legend
         fig_boundary = fig.line([0, self.max_width, self.max_width, 0, 0],
                                 [0 + y_offset, 0 + y_offset, self.height + y_offset, self.height + y_offset,
-                                 0 + y_offset]
-                                , line_color="black", alpha=0.7, line_width=1, muted_alpha=0.2, )
+                                 0 + y_offset],
+                                line_color="black", alpha=0.7, line_width=1, muted_alpha=0.2)
         fig_polygon_boundary = fig.line([self.current_right_boundary, self.current_right_boundary],
-                                        [0 + y_offset, self.height + y_offset]
-                                        , line_color="black", line_dash="dotted", alpha=0.7, line_width=1,
-                                        muted_alpha=0.2, )
+                                        [0 + y_offset, self.height + y_offset],
+                                        line_color="black", line_dash="dotted", alpha=0.7, line_width=1,
+                                        muted_alpha=0.2)
         items = legend_polygons + [("spine", legend_spine)]
         items.append(("container-boundary", [fig_boundary]))
         items.append(("last polygon-boundary", [fig_polygon_boundary]))
@@ -977,7 +978,7 @@ class ConvexContainer(object):
 
     Args:
         polygons ([ConvexPolygon]): convex input polygons to pack into the smallest ConvexContainer
-        steps (int): the angle for the rotation and backrotation increments
+        steps (int): the angle for the rotation and back rotation increments
     Attributes:
         angle_0_rectangular_container (ConvexContainer): the input polygons packed to a not rotated RectangularContainer
         angle_0_rectangular_container_polygons ([ConvexPolygon]):  the polygons of the 0° RectangularContainer
@@ -1005,7 +1006,7 @@ class ConvexContainer(object):
         self.rotated_rectangular_container_list = []  # Liste aller RectangularContainer
         self.rotated_container_plots = []
         self.back_rotated_polygons_plots = []
-        self.smallest_rectangular_container, self.polygons, self.angle, self.area = self.find_convex_container(steps,build_plots)
+        self.smallest_rectangular_container, self.polygons, self.angle, self.area = self.find_convex_container(steps, build_plots)
         self.polygon_shells = self.collect_polygon_shells()
         self.width = self.smallest_rectangular_container.x_boundary
         self.height = self.smallest_rectangular_container.y_boundary
@@ -1047,9 +1048,10 @@ class ConvexContainer(object):
                     truncate(rotated_rectangular_container.container_area, 1),
                     truncate(rotated_rectangular_container.container_not_clipped_area, 1),
                     rotated_rectangular_container.angle)
-                self.rotated_container_plots.append(rotated_rectangular_container.plot_container(render=False, title=title))
+                self.rotated_container_plots.append(rotated_rectangular_container.plot_container(render=False,
+                                                                                                 title=title))
                 back_rotated_polygons = rotate_polygons(rotated_rectangular_container.polygons, angle,
-                                                        origin=self.rotate_center)  # kreiert neue Polygone
+                                                        origin=self.rotate_center)
                 box_x_v = rotated_rectangular_container.x_values_border
                 box_y_v = rotated_rectangular_container.y_values_border
                 boundaries = list(zip(box_x_v, box_y_v))
@@ -1057,7 +1059,8 @@ class ConvexContainer(object):
                                                                              origin=self.rotate_center)
                 title2 = 'Convex-Container  area: {}  not-clipped-area: {} angle: {}'.format(
                     truncate(rotated_rectangular_container.container_area, 1),
-                    truncate(rotated_rectangular_container.container_not_clipped_area, 1), -rotated_rectangular_container.angle)
+                    truncate(rotated_rectangular_container.container_not_clipped_area, 1),
+                    -rotated_rectangular_container.angle)
                 back_rotated_polygon_plot = plot_polygons_in_single_plot(back_rotated_polygons, render=False,
                                                                          border=(rotated_x_values, rotated_y_values),
                                                                          title=title2)
@@ -1136,37 +1139,37 @@ class ConvexContainer(object):
             show(self.plot_steps_all)
         return self.plot_steps_all
 
-    def plot_rotated_containers(self, render=True, colums=9, plot_width=600, plot_height=600) -> Row:
+    def plot_rotated_containers(self, render=True, columns=9, plot_width=600, plot_height=600) -> Row:
         """Plot including all rotated RectangularContainers
 
         Args:
             render (bool): if True render the plot else not
-            colums (int): the colum count in which the container plots will be rendered
+            columns (int): the column count in which the container plots will be rendered
             plot_width (int): the width of the plot figure
             plot_height (int): the height of the plot figure
 
         Returns:
             grid (Row): grid plot including all rotated RectangularContainers
         """
-        grid = gridplot(self.rotated_container_plots, ncols=colums, plot_width=plot_width, plot_height=plot_height,
+        grid = gridplot(self.rotated_container_plots, ncols=columns, plot_width=plot_width, plot_height=plot_height,
                         toolbar_location="left")
         if render:
             show(grid)
         return grid
 
-    def plot_back_rotated_polygons(self, render=True, colums=9, plot_width=700, plot_height=600) -> Row:
+    def plot_back_rotated_polygons(self, render=True, columns=9, plot_width=700, plot_height=600) -> Row:
         """Plot including all back rotated polygons of the RectangularContainers
 
         Args:
             render (bool): if True render the plot else not
-            colums (int): the colum count in which the container plots will be rendered
+            columns (int): the column count in which the container plots will be rendered
             plot_width (int): the width of the plot figure
             plot_height (int): the height of the plot figure
 
         Returns:
             grid (Row): grid plot including all back rotated polygons of the RectangularContainers
         """
-        grid = gridplot(self.back_rotated_polygons_plots, ncols=colums, plot_width=plot_width, plot_height=plot_height,
+        grid = gridplot(self.back_rotated_polygons_plots, ncols=columns, plot_width=plot_width, plot_height=plot_height,
                         toolbar_location="left")
         if render:
             show(grid)
@@ -1193,76 +1196,96 @@ class ConvexContainer(object):
         return fig
 
 
-def rotate_points_and_split(point_list: [Point_xy], angle, origin=(0, 0)) -> ([float], [float]):
-    """Rotates the input points and give a tuple with x and y values back
+def create_multiple_convex_polygons(number: int, max_ngon: int, max_rand=1000) -> [ConvexPolygon]:
+    """Creates multiple convex polygons
 
-    Uses the affinity.rotated function from the shapely package which creates a dependency, another way would be to use
-    a own function (rotation matrix).
+    Args:
+        number (int): the number of the polygons to create
+        max_ngon (int): the maximum vertices a polygon can have
+        max_rand (int): the maximum value for the x and y coordinate of the polygon vertices
 
-    Hint:
-        when writing an own function to rotate the points, their are some special cases in python which need to be
-        handled,
+    Returns:
+        polygon_list ([ConvexPolygon]): a list of convex polygons
+    """
+    polygon_list = []
+    for count in range(0, number):
+        polygon_list.append(create_convex_polygon(max_ngon, max_rand))
+    return polygon_list
 
-        this is a code snipped from the shapely package to show these cases
-        code snipped from: https://github.com/Toblerity/Shapely/blob/master/shapely/affinity.py
-        angle = angle * pi/180.0
-        cosp = cos(angle)
-        sinp = sin(angle)
-        if abs(cosp) < 2.5e-16:  # if not checked this case could create confusion
-            cosp = 0.0
-        if abs(sinp) < 2.5e-16:  # if not checked this case could create confusion
-            sinp = 0.0
+
+def create_convex_polygon(max_ngon: int, max_rand=1000) -> ConvexPolygon:
+    """Creates a convex Polygon
+
+    Uses the convex hull function from the shapely package.
+    The way the polygon is created is to create max_ngon points and then use the convex hull on these points which means
+    that the output polygon does not have allways max_ngon vertices.
 
     Args:
-        point_list ([Point_xy]): a list of points which need to be rotated
-        angle (int): the rotation angle
-        origin (float): the rotation point for all rotations
+        max_ngon (int): the maximum vertices a polygon can have
+        max_rand (int): the maximum value for the x and y coordinate of the polygon vertices
 
     Returns:
-        poly_wrapper_rotated.exterior.xy (([float], [float])): tuple with rotated x and y values
+        c_polygon (ConvexPolygon):
     """
-    # wrapping the points into a shapely Polygon to use the rotation function
-    poly_wrapper = Polygon(point_list)
-    poly_wrapper_rotated = affinity.rotate(poly_wrapper, angle, origin=origin)
-    return poly_wrapper_rotated.exterior.xy
-
+    ngon = numpy.random.randint(3, max_ngon + 1)
+    convex_polygon = None
+    while type(convex_polygon) is not Polygon:  # the shapely convex hull could create a point or stretch
+        polygon_points = []
+        while len(polygon_points) < ngon:
+            x = numpy.random.randint(0, max_rand + 1)
+            y = numpy.random.randint(0, max_rand + 1)
+            while (x, y) in polygon_points:
+                x = numpy.random.randint(0, max_rand + 1)
+                y = numpy.random.randint(0, max_rand + 1)
+            polygon_points.append((x, y))
+        convex_polygon = Polygon(polygon_points).convex_hull
+    c_polygon = ConvexPolygon(list(convex_polygon.exterior.coords))
+    return c_polygon
 
-def rotate_and_create_new_convex_polygon(polygon: ConvexPolygon, angle: int, origin=(0, 0)) -> ConvexPolygon:
-    """Creates a new rotated convex polygon from an input convex polygon
 
-    This function uses again the affinity.rotate() function from shapely.
-    This creates a dependency to the shapely package.
+def build_height_classes(polygon_list: [ConvexPolygon]) -> [HighClass]:
+    """Creates HighClasses and assigns the input polygons to them
 
     Args:
-        polygon (ConvexPolygon): convex polygon which will "copied" and rotated
-        angle (int): the rotation angle
-        origin (float): the rotation point for all vertex rotations
+        polygon_list ([ConvexPolygons]): list of the convex input polygons
 
     Returns:
-        rotated_convex_polygon (ConvexPolygon): rotated convex polygon
+        height_classes ([HeightClass]): a list of created height classes
     """
-    poly_wrapper = Polygon(polygon.shell)
-    poly_wrapper_rotated = affinity.rotate(poly_wrapper, angle, origin=origin)
-    rotated_convex_polygon = ConvexPolygon(poly_wrapper_rotated.exterior.coords)
-    return rotated_convex_polygon
+    ordered_polygons = sorted(polygon_list, key=lambda polygon: polygon.height, reverse=True)
+    h_max_polygon = ordered_polygons[0].height
+    w_max_polygon = max([polygon.width for polygon in polygon_list])
+    alpha = 0.407
+    i = 0
+    height_classes = []
+    polygon_count = len(ordered_polygons)
+    while polygon_count > 0:
+        hc = HighClass(i, alpha, h_max_polygon, w_max_polygon)
+        hc_polygons = []
+        while polygon_count > 0 and hc.min_border < ordered_polygons[0].height <= hc.max_border:
+            hc_polygons.append(ordered_polygons.pop(0))
+            polygon_count -= 1
+        hc.set_polygons(hc_polygons)
+        if len(hc.polygons) > 0:
+            height_classes.append(hc)
+        i += 1
+    return height_classes
 
 
-def rotate_polygons(polygons: [ConvexPolygon], angle: int, origin=(0, 0)) -> [ConvexPolygon]:
-    """Creates several new rotated convex polygons
+def build_containers(height_classes: [HighClass]) -> [Container]:
+    """Wrapper function for building a Container for each HighClass
 
     Args:
-        polygons ([ConvexPolygon]): convex polygons which will "copied" and rotated
-        angle (int): the rotation angle
-        origin (float): the rotation point for all vertex rotations
+        height_classes ([HeighClass]): list of input high classes
 
     Returns:
-        rotated_polygons ([ConvexPolygon]): rotated convex polygons
+        container ([Container]): a list of build containers
     """
-    rotated_polygons = []
-    for polygon in polygons:
-        rotated_polygon = rotate_and_create_new_convex_polygon(polygon, angle, origin)
-        rotated_polygons.append(rotated_polygon)
-    return rotated_polygons
+    containers = []
+    for height_class in height_classes:
+        container = Container(height_class)
+        containers.append(container)
+    return containers
 
 
 def build_mini_containers_and_plots(container_array: [Container], c=2.214) -> ([MiniContainer], [[Figure]]):
@@ -1276,7 +1299,7 @@ def build_mini_containers_and_plots(container_array: [Container], c=2.214) -> ([
 
     Returns:
         mini_container_array, mini_container_plots_list ([MiniContainer], [[Figure]]): a tuple of all build
-                                                                                       mini-containers and all import
+                                                                                       mini-containers and all important
                                                                                        plot steps, every list of plots
                                                                                        represent a Container and the
                                                                                        resulting mini-containers
@@ -1299,8 +1322,8 @@ def build_mini_containers_and_plots(container_array: [Container], c=2.214) -> ([
             background_color_list.append(next(colors))
         box_boundaries_x_values_colors_tuple = (box_boundaries_x_values, background_color_list)
         container_and_mini_container_plots.append(container.plot_container(container_sigma, render=False,
-                                                                           box_boundaries_x_values_colors_tuple=
-                                                                           box_boundaries_x_values_colors_tuple))
+                                                                           box_boundaries_x_values_colors_tuple
+                                                                           =box_boundaries_x_values_colors_tuple))
         max_width_mini_container = box_width + container.hc.w_max_polygon
         background_counter = 0
         while len(container_sigma) > 0:
@@ -1329,7 +1352,7 @@ def build_mini_containers_and_plots(container_array: [Container], c=2.214) -> ([
             mini_container.height = mini_container_y_border
             mini_container.current_right_boundary = mini_container_x_border
             mini_container_array.append(mini_container)
-            if (box_width * box_counter) < (container.x_boundary):
+            if (box_width * box_counter) < container.x_boundary:
                 title = "Mini-Container{}  height: {}  (hc_{})".format(box_counter, truncate(mini_container.height, 1),
                                                                        container.hc.i)
                 b_color = background_color_list[background_counter]
@@ -1337,7 +1360,7 @@ def build_mini_containers_and_plots(container_array: [Container], c=2.214) -> ([
                     mini_container.plot_container(title=title, render=False, background_c=b_color))
                 box_counter += 1
                 background_counter += 1
-            # for the smaller box
+            # for the last box which can be smaller
             else:
                 title = "Mini-Container{}  height: {}  (hc_{})".format(box_counter, truncate(mini_container.height, 1),
                                                                        container.hc.i)
@@ -1346,57 +1369,162 @@ def build_mini_containers_and_plots(container_array: [Container], c=2.214) -> ([
     return mini_container_array, mini_container_plots_list
 
 
-def plot_mini_containers(plot_steps: [[Figure]], render=True, colums=3, plot_width=600, plot_height=600) -> Column:
-    plots = []
-    for plot in plot_steps:
-        grid = gridplot(plot, ncols=colums, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
-        plots.append(grid)
-    if render:
-        show(layout(plots))
-    return layout(plots)
+def pack_polygons(polygons: [ConvexPolygon], angle=0) -> RectangularContainer:
+    """Wrapper for the packing algorithm, packs the  convex input polygons into a RectangularContainer
 
+    Packing Steps:
+        0. Condition that the input polygons are convex
+        1. Sort the polygons into HighClasses
+        2. For every HighClass build a Container with fixed height
+        3. Divide every Container into MiniContainers with fixed width
+            3.1 optimization: clip the mini-container height to the highest vertex point of the mini-container
+        4. Stack the MiniContainer to a RectangularContainer
+            4.1 optimization: clip the RectangularContainer width to the rightest polygon vertex of all polygons in the
+                RectangularContainer
 
-def create_multiple_convex_polygons(number: int, max_ngon: int, max_rand=1000) -> [ConvexPolygon]:
-    polygon_list = []
-    for count in range(0, number):
-        polygon_list.append(create_convex_polygon(max_ngon, max_rand))
-    return polygon_list
+    The pack_polygons function also bundles all important packing steps in one plot.
 
+    Args:
+        polygons ([ConvexPolygon]): convex input polygon which should packed
+        angle (int): the angle of the Rectangular container default 0, this argument helps for finding the
+                     ConvexContainer
 
-def create_convex_polygon(max_ngon: int,
-                          max_rand=1000) -> ConvexPolygon:  # die erste Koordinate der konvexen Polygone wird dupliziert zum schließen des Polygons
-    ngon = numpy.random.randint(3, max_ngon + 1)
-    # polygon_points=[]
-    convex_polygon = None
-    while type(convex_polygon) is not Polygon:  # da bei der Convexen Hülle ein Punkt oder String rauskommen kann
-        polygon_points = []
-        while len(polygon_points) < ngon:
-            x = numpy.random.randint(0, max_rand + 1)
-            y = numpy.random.randint(0, max_rand + 1)
-            while (x, y) in polygon_points:
-                x = numpy.random.randint(0, max_rand + 1)
-                y = numpy.random.randint(0, max_rand + 1)
-            polygon_points.append((x, y))
-        convex_polygon = Polygon(polygon_points).convex_hull
-    c_polygon = ConvexPolygon(list(convex_polygon.exterior.coords))
-    return c_polygon
+    Returns:
+        rectangular_container (RectangularContainer): the packed polygons in a RectangularContainer, the packing steps
+                                                      are saved in this object
+    """
+    # building the RectangularContainer
+    list_hc = build_height_classes(polygons)
+    list_containers = build_containers(list_hc)
+    list_mini_containers, mini_container_plot_steps = build_mini_containers_and_plots(list_containers)
+    rectangular_container = RectangularContainer(list_mini_containers, angle)
+
+    # plots
+    p_tab = polygons_to_tab_plot(polygons)
+    p_header_tab = Panel(child=p_tab, title="Polygons")
+    hc_tab = hc_to_tab_plot(list_hc)
+    hc_header_tab = Panel(child=hc_tab, title="Height-Classes")
+    c_tab = containers_to_tab_plot(list_containers)
+    c_header_tab = Panel(child=c_tab, title="Containers")
+    mc_tab = mini_container_plots_to_tab_plot(mini_container_plot_steps)
+    mc_header_tab = Panel(child=mc_tab, title="Mini-Containers")
+    ec_tab = rectangular_container_to_tab_plot(rectangular_container)
+    ec_header_tab = Panel(child=ec_tab, title="Rectangular-Container")
+    all_tabs_with_header = Tabs(tabs=[p_header_tab, hc_header_tab, c_header_tab, mc_header_tab, ec_header_tab])
+
+    rectangular_container.plot_steps_all = all_tabs_with_header
+    return rectangular_container
+
+
+def rotate_points_and_split(point_list: [Point_xy], angle, origin=(0, 0)) -> ([float], [float]):
+    """Rotates the input points and give a tuple with x and y values back
+
+    Uses the affinity.rotated function from the shapely package which creates a dependency, another way would be to use
+    a own function (rotation matrix).
+
+    Hint:
+        when writing an own function to rotate the points, their are some special cases in python which need to be
+        handled,
+
+        this is a code snipped from the shapely package to show these cases
+        code snipped from: https://github.com/Toblerity/Shapely/blob/master/shapely/affinity.py
+        angle = angle * pi/180.0
+        cosp = cos(angle)
+        sinp = sin(angle)
+        if abs(cosp) < 2.5e-16:  # if not checked this case could create confusion
+            cosp = 0.0
+        if abs(sinp) < 2.5e-16:  # if not checked this case could create confusion
+            sinp = 0.0
+
+    Args:
+        point_list ([Point_xy]): a list of points which need to be rotated
+        angle (int): the rotation angle
+        origin ((float,float)): the rotation point for all rotations
+
+    Returns:
+        poly_wrapper_rotated.exterior.xy (([float], [float])): tuple with rotated x and y values
+    """
+    # wrapping the points into a shapely Polygon to use the rotation function
+    poly_wrapper = Polygon(point_list)
+    poly_wrapper_rotated = affinity.rotate(poly_wrapper, angle, origin=origin)
+    return poly_wrapper_rotated.exterior.xy
 
 
-def plot_polygons(polygon_list: [ConvexPolygon], render=True, plot_width=450, plot_height=450, ncols=4) -> Column:
+def rotate_and_create_new_convex_polygon(polygon: ConvexPolygon, angle: int, origin=(0, 0)) -> ConvexPolygon:
+    """Creates a new rotated convex polygon from an input convex polygon
+
+    This function uses again the affinity.rotate() function from shapely.
+    This creates a dependency to the shapely package.
+
+    Args:
+        polygon (ConvexPolygon): convex polygon which will "copied" and rotated
+        angle (int): the rotation angle
+        origin ((float,float)): the rotation point for all vertex rotations
+
+    Returns:
+        rotated_convex_polygon (ConvexPolygon): rotated convex polygon
+    """
+    poly_wrapper = Polygon(polygon.shell)
+    poly_wrapper_rotated = affinity.rotate(poly_wrapper, angle, origin=origin)
+    rotated_convex_polygon = ConvexPolygon(poly_wrapper_rotated.exterior.coords)
+    return rotated_convex_polygon
+
+
+def rotate_polygons(polygons: [ConvexPolygon], angle: int, origin=(0, 0)) -> [ConvexPolygon]:
+    """Creates several new rotated convex polygons
+
+    Args:
+        polygons ([ConvexPolygon]): convex polygons which will "copied" and rotated
+        angle (int): the rotation angle
+        origin ((float,float)): the rotation point for all vertex rotations
+
+    Returns:
+        rotated_polygons ([ConvexPolygon]): rotated convex polygons
+    """
+    rotated_polygons = []
+    for polygon in polygons:
+        rotated_polygon = rotate_and_create_new_convex_polygon(polygon, angle, origin)
+        rotated_polygons.append(rotated_polygon)
+    return rotated_polygons
+
+
+def truncate(number: float, decimals=0) -> float:
+    """Returns a value truncated to a specific number of decimal places.
+
+    Source of the this function code is from: https://kodify.net/python/math/truncate-decimals/
+    References:
+    Python.org (n.d.). math — Mathematical functions. Retrieved on October 22, 2019,from https://docs.python.org/3.8/library/math.html
+    """
+    if not isinstance(decimals, int):
+        raise TypeError("decimal places must be an integer.")
+    elif decimals < 0:
+        raise ValueError("decimal places has to be 0 or more.")
+    elif decimals == 0:
+        return math.trunc(number)
+    factor = 10.0 ** decimals
+    return math.trunc(number * factor) / factor
+
+
+def plot_polygons(polygon_list: [ConvexPolygon], render=True, plot_width=450, plot_height=450, columns=4) -> Column:
+    """Build a plot object for a list of convex polygons
+
+    Args:
+        polygon_list ([ConvexPolygon]): list of convex polygons
+        render (bool): if True the plot will be render else not
+        plot_width (int): the width of the plot figure
+        plot_height (int): the height of the plot figure
+        columns (int): the column count in which polygons of the plot are shown
+
+    Returns:
+        grid (Column): a plot of all input polygons
+    """
     plots = []
     colors = itertools.cycle(palette)
     for counter, polygon in enumerate(polygon_list):
         color = next(colors)
         title = "Polygon {}".format(counter)
         plots.append(polygon.plot_polygon(title=title, color=color, render=False))
-    grid = gridplot(plots, ncols=ncols, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
-    if render:
-        show(grid)
-    return grid
-
-
-def plot_figures_as_grid(plot_list: [Figure], render=True, plot_width=600, plot_height=500, ncols=4) -> Column:
-    grid = gridplot(plot_list, ncols=ncols, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
+    grid = gridplot(plots, ncols=columns, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
     if render:
         show(grid)
     return grid
@@ -1404,7 +1532,20 @@ def plot_figures_as_grid(plot_list: [Figure], render=True, plot_width=600, plot_
 
 def plot_polygons_in_single_plot(polygon_list: [ConvexPolygon], title="", plot_width=750, plot_height=500, render=True,
                                  border=None, reverse_legend=False) -> Figure:
-    polygon_number = len(polygon_list)
+    """Creates a plot object for  several polygons in one coordinate system
+
+    Args:
+        polygon_list ([ConvexPolygon]): a list of convex polygons
+        title (str): the title of the plot
+        plot_width (int): the width of the plot figure
+        plot_height (int): the height of the plot figure
+        render (bool): if True the plot will be rendered else not
+        border (([float],[float])): a tuple of x and y values to create border for the polygons
+        reverse_legend (bool): if True the plot legend will be reversed
+
+    Returns:
+        fig (Figure): a plot object which shows all input polygons in one coordinate system
+    """
 
     tooltips = [("index", "$index"), ("(x,y)", "($x{0.0}, $y{0.0})"), ]
     fig = figure(title=title, x_axis_label='x', y_axis_label='y', width=plot_width, height=plot_height,
@@ -1428,7 +1569,7 @@ def plot_polygons_in_single_plot(polygon_list: [ConvexPolygon], title="", plot_w
         legend_polygons.append((legend_label, [poly_fig]))
         legend_vertices.append(circle_fig)
         legend_all_polygons = legend_all_polygons + [poly_fig]
-    if border != None:
+    if border is not None:
         fig_border = fig.line(border[0], border[1], line_color="red", line_width=1, muted_alpha=0.2)
         legend_items.append(("border", [fig_border]))
 
@@ -1446,61 +1587,20 @@ def plot_polygons_in_single_plot(polygon_list: [ConvexPolygon], title="", plot_w
     return fig
 
 
-def build_height_classes(polygon_list: [ConvexPolygon]) -> [HighClass]:
-    ordered_polygons = sorted(polygon_list, key=lambda polygon: polygon.height, reverse=True)
-    h_max_polygon = ordered_polygons[0].height
-    w_max_polygon = max([polygon.width for polygon in polygon_list])
-    alpha = 0.407
-    i = 0
-    height_classes = []
-    polygon_count = len(ordered_polygons)
-    while polygon_count > 0:
-        hc = HighClass(i, alpha, h_max_polygon, w_max_polygon)
-        hc_polygons = []
-        while polygon_count > 0 and hc.min_border < ordered_polygons[0].height and \
-              ordered_polygons[0].height <= hc.max_border:
-            hc_polygons.append(ordered_polygons.pop(0))
-            polygon_count -= 1
-        hc.set_polygons(hc_polygons)
-        if len(hc.polygons) > 0:
-            height_classes.append(hc)  # will man höhen Klassen ohne Polygone drinne ?
-        i += 1
-    return height_classes
-
-
-def building_containers(height_classes: [HighClass]) -> [Container]:
-    containers = []
-    for height_class in height_classes:
-        container = Container(height_class)
-        containers.append(container)
-    return containers
-
-
-def pack_polygons(polygons: [ConvexPolygon], angle=0) -> RectangularContainer:
-    # building the RectangularContainer
-    list_hc = build_height_classes(polygons)
-    list_containers = building_containers(list_hc)
-    list_mini_containers, mini_container_plot_steps = build_mini_containers_and_plots(list_containers)
-    rectangular_container = RectangularContainer(list_mini_containers, angle)
-
-    # plots
-    p_tab = polygons_to_tab_plot(polygons)
-    p_header_tab = Panel(child=p_tab, title="Polygons")
-    hc_tab = hc_to_tab_plot(list_hc)
-    hc_header_tab = Panel(child=hc_tab, title="Height-Classes")
-    c_tab = containers_to_tab_plot(list_containers)
-    c_header_tab = Panel(child=c_tab, title="Containers")
-    mc_tab = mini_container_plots_to_tab_plot(mini_container_plot_steps)
-    mc_header_tab = Panel(child=mc_tab, title="Mini-Containers")
-    ec_tab = rectangular_container_to_tab_plot(rectangular_container)
-    ec_header_tab = Panel(child=ec_tab, title="Rectangular-Container")
-    all_tabs_with_header = Tabs(tabs=[p_header_tab, hc_header_tab, c_header_tab, mc_header_tab, ec_header_tab])
-
-    rectangular_container.plot_steps_all = all_tabs_with_header
-    return rectangular_container
+def polygons_to_tab_plot(polygons: [ConvexPolygon], tab_poly_count=9, columns=3, plot_width=450, plot_height=450) -> \
+                        Tabs:
+    """Plot of convex input polygons structured with tabs
 
+    Args:
+        polygons ([ConvexPolyogn]): convex input Polygons
+        tab_poly_count (int): number of polygons in one tab
+        columns (int): the columns to present the polygons
+        plot_width (int): the width of the plot figure
+        plot_height (int): the width of the plot figure
 
-def polygons_to_tab_plot(polygons: [ConvexPolygon], tab_poly_count=9, ncols=3, plot_width=450, plot_height=450) -> Tabs:
+    Returns:
+        polygon_tabs (Tabs): a plot object which represents all convex input polygons structured with tabs
+    """
     polygon_tab_list = []
     polygons_in_one_tab = []
     colors = itertools.cycle(palette)
@@ -1510,13 +1610,12 @@ def polygons_to_tab_plot(polygons: [ConvexPolygon], tab_poly_count=9, ncols=3, p
         polygon_plot = polygon.plot_polygon(title=polygon_title, color=color, render=False)
         polygons_in_one_tab.append(polygon_plot)
         if len(polygons_in_one_tab) >= tab_poly_count or (counter + 1 == len(polygons)):
-            # tab_layout= ([polygons_in_one_tab])
             if counter + 1 - tab_poly_count < 0:
                 title = "Polygons {}-{}".format(0, counter)
             else:
                 title = "Polygons {}-{}".format(counter + 1 - len(polygons_in_one_tab), counter)
             grid_title = Div(text="<b>{}<b>".format(title))
-            polygons_grid = gridplot(polygons_in_one_tab, ncols=ncols, plot_width=plot_width, plot_height=plot_height,
+            polygons_grid = gridplot(polygons_in_one_tab, ncols=columns, plot_width=plot_width, plot_height=plot_height,
                                      toolbar_location="left")
             tab_layout = layout(grid_title, polygons_grid)
             tab = Panel(child=tab_layout, title=title)
@@ -1527,6 +1626,16 @@ def polygons_to_tab_plot(polygons: [ConvexPolygon], tab_poly_count=9, ncols=3, p
 
 
 def hc_to_tab_plot(list_hc: [HighClass], plot_width=450, plot_height=450) -> Tabs:
+    """Plot of the input HighClasses structured with tabs
+
+    Args:
+        list_hc ([HighClass]): list of the input HighClasses
+        plot_width (int): width of the plot figure
+        plot_height (int): height of the plot figure
+
+    Returns:
+       hc_tabs (Tabs): a plot object which represents the input HighClasses structured with tabs
+    """
     hc_tab_list = []
     for counter, hc in enumerate(list_hc):
         hc_plot = hc.plot_hc(render=False, plot_width=plot_width, plot_height=plot_height)
@@ -1536,10 +1645,21 @@ def hc_to_tab_plot(list_hc: [HighClass], plot_width=450, plot_height=450) -> Tab
     return hc_tabs
 
 
-def containers_to_tab_plot(list_containers: [Container], plot_width=700, plot_height=700) -> Tabs:
+def containers_to_tab_plot(list_containers: [Container], plot_width=700, plot_height=700, columns=3) -> Tabs:
+    """Plot of the input Containers structured with tabs
+
+    Args:
+        list_containers (): list of the input Containers
+        plot_width (int): width of the plot figure
+        plot_height (int): height of the plot figure
+        columns (int): the columns to present the polygons
+
+    Returns:
+        container_tabs (Tabs): a plot object which represents the input Containers structured with tabs
+    """
     container_tab_list = []
     for counter, container in enumerate(list_containers):
-        container_plot = container.plot_container_steps(plot_width=plot_width, colums=3, plot_height=plot_height,
+        container_plot = container.plot_container_steps(plot_width=plot_width, columns=columns, plot_height=plot_height,
                                                         render=False)
         tab = Panel(child=container_plot, title="Hc_{} Container".format(container.hc.i))
         container_tab_list.append(tab)
@@ -1547,12 +1667,25 @@ def containers_to_tab_plot(list_containers: [Container], plot_width=700, plot_he
     return container_tabs
 
 
-def mini_container_plots_to_tab_plot(mini_container_plot_steps: [Figure], plot_width=700, plot_height=700, ncols=3) \
+def mini_container_plots_to_tab_plot(mini_container_plot_steps: [[Figure]], plot_width=700, plot_height=700, columns=3)\
         -> Tabs:
+    """Plot of the input mini-container plot object structured with tabs
+
+    Args:
+        mini_container_plot_steps ([[Figure]]): a list of plot objects lists, every list includes all steps from packing
+                                                a container to mini-containers
+        plot_width (int): the width of the plot figure
+        plot_height (int): the height of the plot figure
+        columns (int): the columns to present the polygons
+
+    Returns:
+        container_tabs (Tabs): a plot object which represents the MiniContainers structured with tabs
+    """
     mini_plots_tabs = []
     for counter, m_plot in enumerate(mini_container_plot_steps):
         title = m_plot[0].title.text
-        m_plot_grid = gridplot(m_plot, ncols=ncols, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
+        m_plot_grid = gridplot(m_plot, ncols=columns, plot_width=plot_width, plot_height=plot_height,
+                               toolbar_location="left")
         grid_title = Div(text="<b>{}<b>".format(title))
         mc_layout = layout(grid_title, m_plot_grid)
         tab = Panel(child=mc_layout, title=title)
@@ -1561,7 +1694,18 @@ def mini_container_plots_to_tab_plot(mini_container_plot_steps: [Figure], plot_w
     return mini_tabs
 
 
-def rectangular_container_to_tab_plot(rectangular_container: RectangularContainer, plot_width=800, plot_height=800) -> Tabs:
+def rectangular_container_to_tab_plot(rectangular_container: RectangularContainer, plot_width=800, plot_height=800)\
+        -> Tabs:
+    """Plot of the input RectangularContainer structured with tabs, one tab shows the mini-containers and the other hide
+
+    Args:
+        rectangular_container (RectangularContainer):
+        plot_width (int): the width of the plot figure
+        plot_height (int): the height of the plot figure
+
+    Returns:
+        end_tabs (Tabs) -> a plot object which represents the RectangularContainer structured with tabs
+    """
     rectangular_container_tabs = []
     polygons_plot = rectangular_container.plot_polygons(render=False, plot_width=plot_width, plot_height=plot_height)
     tab = Panel(child=polygons_plot, title="Rectangular-Container")
@@ -1573,30 +1717,20 @@ def rectangular_container_to_tab_plot(rectangular_container: RectangularContaine
     return end_tabs
 
 
-def plot_containers(container_list: [Container], render=True, colums=3, plot_width=500, plot_height=500) -> Column:
-    container_plots = []
-    for container in container_list:
-        container_plots.append(container.plot_container(render=False))
-    grid = gridplot(container_plots, ncols=colums, plot_width=plot_width, plot_height=plot_height,
-                    toolbar_location="left")
-    if render:
-        show(grid)
-    return grid
+def plot_figures_as_grid(plot_list: [Figure], render=True, plot_width=600, plot_height=500, columns=4) -> Column:
+    """Creates a grid plot for multiple plot objects
 
+    Args:
+        plot_list ([Figure]): list of plot objects
+        render (bool): if True the build plot object will be rendered else not
+        plot_width (int): the width of the for the plot objects
+        plot_height (height): the height of the plot objects
+        columns (int): shows the plot objects in columns
 
-# https://kodify.net/python/math/truncate-decimals/
-# Python.org (n.d.). math — Mathematical functions. Retrieved on October 22, 2019, from https://docs.python.org/3.8/library/math.html
-def truncate(number: float, decimals=0) -> float:
-    """
-    Returns a value truncated to a specific number of decimal places.
-    """
-    if not isinstance(decimals, int):
-        raise TypeError("decimal places must be an integer.")
-    elif decimals < 0:
-        raise ValueError("decimal places has to be 0 or more.")
-    elif decimals == 0:
-        return math.trunc(number)
-    factor = 10.0 ** decimals
-    return math.trunc(number * factor) / factor
+    Returns:
 
-# output_notebook()  # for using Bokeh in Jupyter Lab
\ No newline at end of file
+    """
+    grid = gridplot(plot_list, ncols=columns, plot_width=plot_width, plot_height=plot_height, toolbar_location="left")
+    if render:
+        show(grid)
+    return grid
diff --git a/mysite/plots/templates/plots/packed_polygons.html b/mysite/plots/templates/plots/packed_polygons.html
index 97e35ffd..33081653 100644
--- a/mysite/plots/templates/plots/packed_polygons.html
+++ b/mysite/plots/templates/plots/packed_polygons.html
@@ -19,7 +19,7 @@
 </div>
 <div>
 <h1>Final Container Polygons Coordinates</h1>
-<p>every List stands for one Polygon<p>
+<p>every list stands for one polygon<p>
 {% for polygon in coordinates%}
 {{polygon}}<br>
 {%endfor%}</div>
diff --git a/mysite/plots/views.py b/mysite/plots/views.py
index ffd208ea..54d16633 100644
--- a/mysite/plots/views.py
+++ b/mysite/plots/views.py
@@ -197,7 +197,7 @@ class PolygonEditView(View):
             PolygonEditView.plot_min_y = min(polygon_min_y_list)
             PolygonEditView.plot_max_x = max(polygon_max_x_list)
             PolygonEditView.plot_min_x = min(polygon_min_x_list)
-        plot_drawn = poly.polygons_to_tab_plot(drawn_polygons, ncols=4, tab_poly_count=8)
+        plot_drawn = poly.polygons_to_tab_plot(drawn_polygons, columns=4, tab_poly_count=8)
         polygons_drawn_plot_html = file_html(plot_drawn, CDN, "my plot")
         PolygonEditView.context["drawn_polygons"] = polygons_drawn_plot_html
         polygons_single_plot = poly.plot_polygons_in_single_plot(drawn_polygons, plot_height=850, plot_width=2000,
-- 
GitLab