├── DOP.ipynb ├── Final report latex files.zip ├── Final report.pdf ├── Max_partition.py ├── Min_partitition.py ├── Presentation.pdf ├── README.md ├── Research Paper by Sahni Sartaj.pdf └── final_partition.py /Final report latex files.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mittalgovind/Polygon-Partition/678b1249eef4a587835a8a70fb8b150fe375466a/Final report latex files.zip -------------------------------------------------------------------------------- /Final report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mittalgovind/Polygon-Partition/678b1249eef4a587835a8a70fb8b150fe375466a/Final report.pdf -------------------------------------------------------------------------------- /Max_partition.py: -------------------------------------------------------------------------------- 1 | 'Please make sure that the INPUT is A CLOSED RECTILINEAR POLYGON,' 2 | 'CONSTRUCTED WHILE GOING IN ANTI-CLOCKWISE ONLY' 3 | # importing libraries 4 | import networkx as nx 5 | import turtle 6 | import matplotlib.pyplot as plt 7 | import warnings 8 | from networkx.algorithms import bipartite 9 | import math 10 | 11 | warnings.filterwarnings("ignore") 12 | # declaration list 13 | wn = turtle.Screen() 14 | # a turtle called gopu 15 | gopu = turtle.Turtle() 16 | # co-ordinate points 17 | x = [] 18 | y = [] 19 | # vertex-type := rectilinear = -1; convex = 0; concave = 1 20 | vertex_type = [] 21 | # store the bipartite graph of chords 22 | G = nx.Graph() 23 | # the line below can be commented if one needs animation 24 | gopu.speed(0) 25 | # starts recording keys being pressed 26 | gopu.begin_poly() 27 | 28 | # Event handlers 29 | stride = 25 30 | # Up = up() - vertex to be deleted = -1 31 | def up(): 32 | vertex_type.append(-1) 33 | gopu.forward(stride) 34 | 35 | # left = left() ; make vertex - convex = 0 36 | def left(): 37 | vertex_type.append(0) 38 | gopu.setheading(gopu.heading()+90) 39 | gopu.forward(stride) 40 | 41 | # right = right() ; make vertex - concave = 1 42 | def right(): 43 | vertex_type.append(1) 44 | gopu.setheading(gopu.heading()-90) 45 | gopu.forward(stride) 46 | 47 | # back = back() -- doing undo is allowed only once. 48 | def back(): 49 | vertex_type.pop() 50 | gopu.undo() 51 | 52 | # quits the screen and outputs the plots the partitioned polygon 53 | def partition_polygon(): 54 | # closing the screen 55 | wn.bye() 56 | # stopped recording the polygon 57 | gopu.end_poly() 58 | p = gopu.get_poly() 59 | p = list(p) 60 | compute_partition(p) 61 | 62 | ''' 63 | The movements of the turtle are recorded and the rectilinear graph thus 64 | obtained is converted into bipartite graph of chords 65 | ''' 66 | def compute_partition(p): 67 | # p now contains the list of coordinates of vertices 68 | # last one is same as the origin 69 | # and the origin is always going to be a convex vertex 70 | p.pop() 71 | vertex_type[0] = 0 72 | 73 | # x and y contain list of x and y coordinates respectively 74 | for i,j in p: 75 | x.append(i) 76 | y.append(j) 77 | 78 | # this is done as there are some very small errors in recording the 79 | # position by the turtle library 80 | for i in range(len(x)): 81 | x[i] = int(x[i]) 82 | y[i] = int(y[i]) 83 | 84 | for i in range(len(x)): 85 | if x[i] % stride != 0: 86 | x[i] += (stride - x[i] % stride) 87 | if y[i] % stride != 0: 88 | y[i] += (stride - y[i] % stride) 89 | 90 | collinear_vertices = [i for i,val in enumerate(vertex_type) if val == -1] 91 | # finding the chords inside the polygon 92 | horizontal_chords = [] 93 | vertical_chords = [] 94 | concave_vertices = [i for i,val in enumerate(vertex_type) if val == 1] 95 | 96 | # middles is used because, there are cases when there is a chord between vertices 97 | # and they intersect with external chords, hence if there is any vertex in between 98 | # two vertices then skip that chord. 99 | for i in range(len(concave_vertices)): 100 | for j in range(i+1,len(concave_vertices)): 101 | if concave_vertices[j] != concave_vertices[i] + 1: 102 | middles = [] 103 | if y[concave_vertices[i]] == y[concave_vertices[j]]: 104 | for k in range(len(x)): 105 | if y[concave_vertices[i]] == y[k] and (x[concave_vertices[i]] < x[k] and x[concave_vertices[j]] > x[k] \ 106 | or x[concave_vertices[i]] > x[k] and x[concave_vertices[j]] < x[k]): 107 | middles.append(k) 108 | if len(middles) == 0: 109 | horizontal_chords.append((concave_vertices[i],concave_vertices[j])) 110 | middles = [] 111 | if x[concave_vertices[i]] == x[concave_vertices[j]]: 112 | for k in range(len(x)): 113 | if x[concave_vertices[i]] == x[k] and (y[concave_vertices[i]] < y[k] and y[concave_vertices[j]] > y[k] \ 114 | or y[concave_vertices[i]] > y[k] and y[concave_vertices[j]] < y[k]): 115 | middles.append(k) 116 | if len(middles) == 0: 117 | vertical_chords.append((concave_vertices[i],concave_vertices[j])) 118 | 119 | 120 | 121 | fig, ax = plt.subplots() 122 | ax.plot(x+[0], y+[0], color='black') 123 | ax.scatter(x+[0], y+[0], color='black') 124 | for i in range(len(x)): 125 | ax.annotate(i, (x[i],y[i])) 126 | plt.show() 127 | plt.clf() 128 | print("Horizontal chords before collinear vertices = ", horizontal_chords) 129 | print("vertical_chords = ", vertical_chords) 130 | 131 | fig, ax = plt.subplots() 132 | ax.plot(x+[0], y+[0], color='black') 133 | ax.scatter(x+[0], y+[0], color='black') 134 | for i in range(len(x)): 135 | ax.annotate(i, (x[i],y[i])) 136 | for i,j in horizontal_chords: 137 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 138 | for i,j in vertical_chords: 139 | ax.plot([x[i],x[j]],[y[i],y[j]], color='black') 140 | plt.show() 141 | plt.clf() 142 | for i in range(len(collinear_vertices)): 143 | for j in range(len(concave_vertices)): 144 | middles = [] 145 | if y[collinear_vertices[i]] == y[concave_vertices[j]]: 146 | if collinear_vertices[i] < concave_vertices[j]: 147 | for k in range(len(x)): 148 | if y[k] == y[collinear_vertices[i]] and (x[k] < x[concave_vertices[j]] \ 149 | and x[k] > x[collinear_vertices[i]] or x[k] > x[concave_vertices[j]] \ 150 | and x[k] < x[collinear_vertices[i]]): 151 | middles.append(k) 152 | if collinear_vertices[i]+1 == concave_vertices[j]: 153 | middles.append(0) 154 | else: 155 | for k in range(len(x)): 156 | if y[k] == y[collinear_vertices[i]] and (x[k] > x[concave_vertices[j]] \ 157 | and x[k] < x[collinear_vertices[i]] or x[k] < x[concave_vertices[j]] \ 158 | and x[k] > x[collinear_vertices[i]]): 159 | middles.append(k) 160 | if collinear_vertices[i] == concave_vertices[j]+1: 161 | middles.append(0) 162 | if len(middles) == 0: 163 | horizontal_chords.append((collinear_vertices[i],concave_vertices[j])) 164 | middles = [] 165 | if x[collinear_vertices[i]] == x[concave_vertices[j]]: 166 | if collinear_vertices[i] < concave_vertices[j]: 167 | for k in range(len(x)): 168 | if x[k] == x[collinear_vertices[i]] and (y[k] < y[concave_vertices[j]] \ 169 | and y[k] > y[collinear_vertices[i]] or y[k] > y[concave_vertices[j]] \ 170 | and y[k] < y[collinear_vertices[i]]): 171 | middles.append(k) 172 | if collinear_vertices[i]+1 == concave_vertices[j]: 173 | middles.append(0) 174 | else: 175 | for k in range(len(x)): 176 | if x[k] == x[collinear_vertices[i]] and (y[k] > y[concave_vertices[j]] \ 177 | and y[k] < y[collinear_vertices[i]] or y[k] < y[concave_vertices[j]] \ 178 | and y[k] > y[collinear_vertices[i]]): 179 | middles.append(k) 180 | if collinear_vertices[i] == concave_vertices[j]+1: 181 | middles.append(0) 182 | if len(middles) == 0: 183 | vertical_chords.append((collinear_vertices[i],concave_vertices[j])) 184 | 185 | # displaying all attributes and important parameters involved 186 | # print("p = ",p) 187 | # print("vertex_Type = ",vertex_type) 188 | # print ("x = ", x) 189 | # print ("y = ", y) 190 | # print("collinear_vertices = ", collinear_vertices) 191 | # print("concave_vertices =", concave_vertices) 192 | print("horizontal_chords = " ,horizontal_chords) 193 | print("vertical_chords = ",vertical_chords) 194 | # drawing the partitioned polygon 195 | fig, ax = plt.subplots() 196 | ax.plot(x+[0], y+[0], color='black') 197 | ax.scatter(x+[0], y+[0], color='black') 198 | for i in range(len(x)): 199 | ax.annotate(i, (x[i],y[i])) 200 | for i,j in horizontal_chords: 201 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 202 | for i,j in vertical_chords: 203 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 204 | plt.show() 205 | 206 | # Defining the keys function 207 | wn.onkey(up, "Up") 208 | wn.onkey(left, "Left") 209 | wn.onkey(right, "Right") 210 | wn.onkey(back, "Down") 211 | wn.onkey(partition_polygon, "Escape") 212 | wn.listen() 213 | wn.mainloop() 214 | 215 | -------------------------------------------------------------------------------- /Min_partitition.py: -------------------------------------------------------------------------------- 1 | 'Please make sure that the INPUT is A CLOSED RECTILINEAR POLYGON,' 2 | 'CONSTRUCTED WHILE GOING IN ANTI-CLOCKWISE ONLY' 3 | # importing libraries 4 | import networkx as nx 5 | import turtle 6 | import matplotlib.pyplot as plt 7 | import warnings 8 | from networkx.algorithms import bipartite 9 | import math 10 | 11 | warnings.filterwarnings("ignore") 12 | # declaration list 13 | wn = turtle.Screen() 14 | # a turtle called gopu 15 | gopu = turtle.Turtle() 16 | # co-ordinate points 17 | x = [] 18 | y = [] 19 | # vertex-type := rectilinear = -1; convex = 0; concave = 1 20 | vertex_type = [] 21 | # store the bipartite graph of chords 22 | G = nx.Graph() 23 | # the line below can be commented if one needs animation 24 | gopu.speed(0) 25 | # starts recording keys being pressed 26 | gopu.begin_poly() 27 | 28 | # Event handlers 29 | stride = 25 30 | # Up = up() - vertex to be deleted = -1 31 | def up(): 32 | vertex_type.append(-1) 33 | gopu.forward(stride) 34 | 35 | # left = left() ; make vertex - convex = 0 36 | def left(): 37 | vertex_type.append(0) 38 | gopu.setheading(gopu.heading()+90) 39 | gopu.forward(stride) 40 | 41 | # right = right() ; make vertex - concave = 1 42 | def right(): 43 | vertex_type.append(1) 44 | gopu.setheading(gopu.heading()-90) 45 | gopu.forward(stride) 46 | 47 | # back = back() -- doing undo is allowed only once. 48 | def back(): 49 | vertex_type.pop() 50 | gopu.undo() 51 | 52 | # quits the screen and outputs the plots the partitioned polygon 53 | def partition_polygon(): 54 | # closing the screen 55 | wn.bye() 56 | # stopped recording the polygon 57 | gopu.end_poly() 58 | p = gopu.get_poly() 59 | p = list(p) 60 | compute_partition(p) 61 | 62 | ''' 63 | The movements of the turtle are recorded and the rectilinear graph thus 64 | obtained is converted into bipartite graph of chords 65 | ''' 66 | def compute_partition(p): 67 | # p now contains the list of coordinates of vertices 68 | # last one is same as the origin 69 | # and the origin is always going to be a convex vertex 70 | p.pop() 71 | vertex_type[0] = 0 72 | 73 | # x and y contain list of x and y coordinates respectively 74 | for i,j in p: 75 | x.append(i) 76 | y.append(j) 77 | 78 | # this is done as there are very small errors in recording the 79 | # position accurately by the turtle library 80 | for i in range(len(x)): 81 | x[i] = int(x[i]) 82 | y[i] = int(y[i]) 83 | 84 | for i in range(len(x)): 85 | if x[i] % stride != 0: 86 | x[i] += (stride - x[i] % stride) 87 | if y[i] % stride != 0: 88 | y[i] += (stride - y[i] % stride) 89 | 90 | # deleting vertices that are on a straight line 91 | collinear_vertices = [i for i,val in enumerate(vertex_type) if val == -1] 92 | # Reverse order deletion is done so that the previous index does not 93 | # overthrow the current one 94 | 95 | fig, ax = plt.subplots() 96 | ax.plot(x+[0], y+[0], color='black') 97 | ax.scatter(x+[0], y+[0], color='black') 98 | for i in range(len(x)): 99 | ax.annotate(i, (x[i],y[i])) 100 | plt.show() 101 | plt.clf() 102 | # finding the chords inside the polygon 103 | horizontal_chords = [] 104 | vertical_chords = [] 105 | concave_vertices = [i for i,val in enumerate(vertex_type) if val == 1] 106 | 107 | # middles is used because, there are cases when there is a chord between vertices 108 | # and they intersect with external chords, hence if there is any vertex in between 109 | # two vertices then skip that chord. 110 | for i in range(len(concave_vertices)): 111 | for j in range(i+1,len(concave_vertices)): 112 | if concave_vertices[j] != concave_vertices[i] + 1: 113 | middles = [] 114 | if y[concave_vertices[i]] == y[concave_vertices[j]]: 115 | for k in range(len(x)): 116 | if y[concave_vertices[i]] == y[k] and (x[concave_vertices[i]] < x[k] and x[concave_vertices[j]] > x[k] \ 117 | or x[concave_vertices[i]] > x[k] and x[concave_vertices[j]] < x[k]): 118 | middles.append(k) 119 | if len(middles) == 0: 120 | horizontal_chords.append((concave_vertices[i],concave_vertices[j])) 121 | middles = [] 122 | if x[concave_vertices[i]] == x[concave_vertices[j]]: 123 | for k in range(len(x)): 124 | if x[concave_vertices[i]] == x[k] and (y[concave_vertices[i]] < y[k] and y[concave_vertices[j]] > y[k] \ 125 | or y[concave_vertices[i]] > y[k] and y[concave_vertices[j]] < y[k]): 126 | middles.append(k) 127 | if len(middles) == 0: 128 | vertical_chords.append((concave_vertices[i],concave_vertices[j])) 129 | 130 | fig, ax = plt.subplots() 131 | ax.plot(x+[0], y+[0], color='black') 132 | ax.scatter(x+[0], y+[0], color='black') 133 | for i in range(len(x)): 134 | ax.annotate(i, (x[i],y[i])) 135 | for i,j in horizontal_chords: 136 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 137 | for i,j in vertical_chords: 138 | ax.plot([x[i],x[j]],[y[i],y[j]], color='black') 139 | plt.show() 140 | plt.clf() 141 | # Creating a bipartite graph from the set of chords 142 | for i,h in enumerate(horizontal_chords): 143 | y1 = y[h[0]] 144 | x1 = min(x[h[0]] ,x[h[1]] ) 145 | x2 = max(x[h[0]] ,x[h[1]]) 146 | G.add_node(i, bipartite=1) 147 | for j,v in enumerate(vertical_chords): 148 | x3 = x[v[0]] 149 | y3 = min(y[v[0]],y[v[1]]) 150 | y4 = max(y[v[0]],y[v[1]]) 151 | G.add_node(j + len(horizontal_chords),bipartite=0) 152 | if x1 <= x3 and x3 <=x2 and y3 <= y1 and y1 <= y4: 153 | G.add_edge(i, j + len(horizontal_chords)) 154 | 155 | if len(horizontal_chords) == 0: 156 | for j,v in enumerate(vertical_chords): 157 | x3 = x[v[0]] 158 | y3 = min(y[v[0]],y[v[1]]) 159 | y4 = max(y[v[0]],y[v[1]]) 160 | G.add_node(j,bipartite=0) 161 | 162 | # finding the maximum matching of the bipartite graph, G. 163 | M = nx.Graph() 164 | maximum_matching = nx.bipartite.maximum_matching(G) 165 | maximum_matching_list = [] 166 | for i,j in maximum_matching.items(): 167 | maximum_matching_list += [(i,j)] 168 | M.add_edges_from(maximum_matching_list) 169 | maximum_matching = M.edges() 170 | nx.draw(G) 171 | plt.show() 172 | plt.gcf().clear() 173 | # breaking up into two sets 174 | H, V = bipartite.sets(G) 175 | free_vertices = [] 176 | for u in H: 177 | temp = [] 178 | for v in V: 179 | if (u,v) in maximum_matching or (v,u) in maximum_matching: 180 | temp += [v] 181 | if len(temp) == 0: 182 | free_vertices += [u] 183 | for u in V: 184 | temp = [] 185 | for v in H: 186 | if (u,v) in maximum_matching or (v,u) in maximum_matching: 187 | temp += [v] 188 | if len(temp) == 0: 189 | free_vertices += [u] 190 | 191 | print("free_vertices = ", free_vertices) 192 | print("maximum matching = ", maximum_matching) 193 | print("H = ", H) 194 | print("V = ", V) 195 | # finding the maximum independent set 196 | max_independent = [] 197 | while len(free_vertices) != 0 or len(maximum_matching) != 0: 198 | if len(free_vertices) != 0 : 199 | u = free_vertices.pop() 200 | max_independent += [u] 201 | else: 202 | u, v = maximum_matching.pop() 203 | G.remove_edge(u,v) 204 | max_independent += [u] 205 | 206 | for v in G.neighbors(u): 207 | G.remove_edge(u, v) 208 | for h in G.nodes(): 209 | if (v,h) in maximum_matching: 210 | maximum_matching.remove((v,h)) 211 | free_vertices += [h] 212 | if (h,v) in maximum_matching: 213 | maximum_matching.remove((h,v)) 214 | free_vertices += [h] 215 | 216 | 217 | # displaying all attributes and important parameters involved 218 | print("p = ",p) 219 | print("vertex_Type = ",vertex_type) 220 | print ("x = ", x) 221 | print ("y = ", y) 222 | print("collinear_vertices = ", collinear_vertices) 223 | print("concave_vertices =", concave_vertices) 224 | print("horizontal_chords = " ,horizontal_chords) 225 | print("vertical_chords = ",vertical_chords) 226 | nx.draw(G) 227 | plt.show() 228 | plt.gcf().clear() 229 | nx.draw(M) 230 | plt.show() 231 | plt.gcf().clear() 232 | print("maximum independent = ", max_independent) 233 | 234 | # drawing the partitioned polygon 235 | ind_chords = [] 236 | for i in max_independent: 237 | if (i >= len(horizontal_chords)): 238 | ind_chords += [vertical_chords[i-len(horizontal_chords)]] 239 | else: 240 | ind_chords += [horizontal_chords[i]] 241 | unmatched_concave_vertices = [i for i in concave_vertices] 242 | for i,j in ind_chords: 243 | if i in unmatched_concave_vertices: 244 | unmatched_concave_vertices.remove(i) 245 | if j in unmatched_concave_vertices: 246 | unmatched_concave_vertices.remove(j) 247 | 248 | print("Unmatched concave vertices", unmatched_concave_vertices) 249 | nearest_chord = [] 250 | for i in unmatched_concave_vertices: 251 | dist = 0 252 | nearest_distance = math.inf 253 | for j in max_independent: 254 | if j < len(horizontal_chords): 255 | temp1, temp2 = horizontal_chords[j] 256 | if abs(y[i] - y[temp1]) < nearest_distance and \ 257 | (x[i] <= x[temp1] and x[i] >= x[temp2] or x[i] >= x[temp1] and x[i] <= x[temp2]) \ 258 | and abs(temp1 - i) != 1 and abs(temp2 - i) != 1: 259 | middles = [] 260 | for u in range(len(x)): 261 | if x[i] == x[u] and (y[i] < y[u] and y[u] < y[temp1] or y[temp1] < y[u] and y[u] < y[i]): 262 | middles.append(u) 263 | if len(middles) == 0: 264 | nearest_distance = abs(y[i] - y[temp1]) 265 | dist = y[temp1] - y[i] 266 | 267 | if nearest_distance != math.inf: 268 | nearest_chord.append((i,dist)) 269 | else: 270 | for k in collinear_vertices: 271 | if x[k] == x[i] and abs(y[k] - y[i]) < nearest_distance and abs(k-i) != 1: 272 | middles = [] 273 | for u in range(len(x)): 274 | if x[i] == x[u] and (y[i] < y[u] and y[u] < y[k] or y[k] < y[u] and y[u] < y[i]): 275 | middles.append(u) 276 | if len(middles) == 0: 277 | nearest_distance = abs(y[i] - y[k]) 278 | dist = y[k] - y[i] 279 | nearest_chord.append((i,dist)) 280 | 281 | print("nearest_chord", nearest_chord) 282 | fig, ax = plt.subplots() 283 | ax.plot(x+[0], y+[0], color='black') 284 | ax.scatter(x+[0], y+[0], color='black') 285 | for i in range(len(x)): 286 | ax.annotate(i, (x[i],y[i])) 287 | for i,j in ind_chords: 288 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 289 | for i,dist in nearest_chord: 290 | ax.plot([x[i],x[i]],[y[i], y[i]+dist],color='black') 291 | plt.show() 292 | 293 | # Defining the keys function 294 | wn.onkey(up, "Up") 295 | 296 | wn.onkey(left, "Left") 297 | wn.onkey(right, "Right") 298 | wn.onkey(back, "Down") 299 | wn.onkey(partition_polygon, "Escape") 300 | 301 | wn.listen() 302 | 303 | wn.mainloop() -------------------------------------------------------------------------------- /Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mittalgovind/Polygon-Partition/678b1249eef4a587835a8a70fb8b150fe375466a/Presentation.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polygon-Partition 2 | Python code for partitioning rectilinear polygon in O(n) time complexity 3 | 4 | **Rectillinear Polygon :** A simple connected single-cyclic graph in R2, such that each of its edge is perpendicular or in-line with another one of its edge(s). 5 | 6 | *__Method of Labelling the graph__* \ 7 | We take input as a rectillinear polygon from cursor keys, i.e., up(↑), left(←), and right (→). As input is read, the pointer proceeds forward and draws a rectillinear polygon with its trail. The labelling of the vertices starts from v0 to vn-1, and v0 = vn, where _n_ is the _number of vertices in the polygon_. 8 |
9 | pic0 10 | 11 | A Rectillinear polygon consisting of 20 vertices. 12 | 13 | Pressing a key once means going forward, left, or right. A distance of only one unit can be traversed at a time. 14 | 15 | #### INPUTS 16 | **_G_** = Rectilinear Graph \ 17 | **_X_** = Set of Abscissa of vertices \ 18 | **_Y_** = Set of Ordinates of vertices \ 19 | **_Collinear\_Vertices_** = Set of Collinear Vertices \ 20 | **_Concave\_Vertices_** = Set of Concave Vertices \ 21 | **_Horizontal\_Chords_** = Set of Horizontal Chords \ 22 | **_Vertical\_Chords_** = Set of Vertical Chords 23 | 24 | #### Important points to note 25 | 26 | 1. Left and Right operations changes the direction the pointer faces. 27 | 2. Vertices that are induced after going forward consecutively. Although in the example, they are not explicitly shown, but they do exist and at a distance of one unit from its previous vertex. 28 | 3. If the interior angle made by the two edges incident at this vertex is 270 degree. 29 | 4. Chords are lines joining two vertices which are not already part of the polygon. 30 | 5. As, the way of labelling is defined, there is unique labelling of each rectillinear polygon. 31 | 32 | **EXAMPLE** \ 33 | In the above figure, the pointer is shown by an arrow. \ 34 | Total number of vertices = 20 \ 35 | Collinear\_Vertices = [v1, v2, v3, v9, v13, v17, v18, v19] 36 | Concave\_Vertices = [v6, v7, v12, v14] 37 | 38 | ### _**Algorithm for Finding Maximum partitions**_ 39 | 40 | **Maximum Partition:** Partition of given rectillinear polygon into maximum number of non-overlapping rectangles. 41 | 42 | ##### STEP I 43 | 44 | ```python 45 | max_partition(G): 46 | for u in Concave_Vertices: 47 | for v in Concave_Vertices and v > u+1: 48 | if exists a chord joining v & u and ~exists another concave 49 | vertex on chord joining v & u: 50 | if chord is horizontal: 51 | add (v, u) to Horizontal_Chords 52 | else if chord is vertical: 53 | add (v, u) to Vertical_Chords 54 | else : 55 | loop_back 56 | ``` 57 | 58 | **_Task Achieved:_** All the edges that exist between *any two concave vertices* are being added to their *respectful categories*. \ 59 | 60 | **EXAMPLE** \ 61 | pic1 62 | 63 | *Horizontal\_Chords* = ∅ \ 64 | *Vertical\_Chords* = [(v7, v12)] 65 | 66 | *Explanation*: 67 | **u > v :** Comparison between two vertices is done on the basis of their respective vertex indices. \ 68 | Here **_v-u_** should be greater than unity, because this assures the vertex v is not consecutive to u and has a higher index than u. Thus, iteration through each pair of vertex is done only once, making it more efficient. \ 69 | 70 | In the above code, we iterate through all (concave vertex, concave vertex') pairs, and check for existence of vertical and horizontal chords, that are not intersected by any other vertex. \ 71 | We observe that, v7 and v12 are the only two concave vertices and between whom, there exists a vertical chord. Therefore, it is added to the set of *Vertical\_Chords*. Also, there does not exist any horizontal chord between any two concave vertices and therefore, set of *Horizontal\_Chords* is empty. \ 72 | 73 | ##### STEP II 74 | 75 | ```python 76 | for u in Collinear_Vertices: 77 | for v in Concave_Vertices: 78 | if exists a chord joining v & u and ~exists another concave 79 | or collinear vertex on chord joining v & u: 80 | if chord is horizontal: 81 | add (v, u) to Horizontal_Chords 82 | else if chord is vertical: 83 | add (v, u) to Vertical_Chords 84 | else : 85 | loop_back 86 | ``` 87 | 88 | **_Task Achieved:_** All the chords between *collinear vertices and concave vertices* are being added to their *respective categories*. 89 | 90 | **EXAMPLE** 91 | 92 | pic2 93 | 94 | *Horizontal\_Chords* = [(v9, v12), (v17, v14), (v18, v7), (v19, v6)] \ 95 | *Vertical_Chords* = [(v7, v12), (v1, v4), (v3, v6)] 96 | 97 | *Explanation*: 98 | In the above code, we iterate through all (collinear vertex, concave vertex) pairs, and check for existence of vertical and horizontal chords between them, that are not intersected by any other vertex. \ 99 | If any chord is found, it is added to set of *Vertical\_Chords* or *Horizontal\_Chords*, depending on its orientation. \ 100 | 101 | ##### STEP III 102 | 103 | Thus, we have found all the chords, and only need to plot them now. 104 | ```python 105 | plot(X,Y) 106 | plot(Horizontal_Chords) 107 | plot(Vertical_Chords) 108 | display(plot) 109 | ``` 110 | 111 | test 0 2 112 | 113 | ##### STEP IV 114 | 115 | Now we have found the maximum partition, but to find the minimum partition the following needs to be done 116 | 117 | 1. Find a maximum independent set of chords (i.e., a maximum cardinality set of independent chords). 118 | 2. Draw the chords in this maximum independent set. This partitions the polygon into smaller rectilinear polygons. 119 | 120 | ##### STEP V 121 | From each of the concave vertices from which a chord was not drawn in *Step IV* draw a maximum length vertical line that is wholly within the smaller rectilinear polygon created in *Step III* that contains this vertex. 122 | 123 | ##### STEP VI 124 | 125 | Thus, we have found all the chords, and only need to plot them now. 126 | ```python 127 | plot(X,Y) 128 | plot(Horizontal_Chords) 129 | plot(Vertical_Chords) 130 | plot(Nearest_Partial_Chords) 131 | display(plot) 132 | ``` 133 | *Test Case 1* 134 | test1 1 135 | test 1 2 136 | 137 | *Test Case 2* 138 | 2 1 139 | 2 2 140 | 141 | *Test Case 3* 142 | 3 1 143 | 3 2 144 | 145 | *Test Case 4* 146 | test 4 1 147 | test 4 2 148 | 149 | 150 | -------------------------------------------------------------------------------- /Research Paper by Sahni Sartaj.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mittalgovind/Polygon-Partition/678b1249eef4a587835a8a70fb8b150fe375466a/Research Paper by Sahni Sartaj.pdf -------------------------------------------------------------------------------- /final_partition.py: -------------------------------------------------------------------------------- 1 | 'Please make sure that the INPUT is A CLOSED RECTILINEAR POLYGON,' 2 | 'CONSTRUCTED WHILE GOING IN ANTI-CLOCKWISE ONLY' 3 | # importing libraries 4 | import networkx as nx 5 | import turtle 6 | import matplotlib.pyplot as plt 7 | import warnings 8 | from networkx.algorithms import bipartite 9 | import math 10 | 11 | warnings.filterwarnings("ignore") 12 | # declaration list 13 | wn = turtle.Screen() 14 | # a turtle called gopu 15 | gopu = turtle.Turtle() 16 | # co-ordinate points 17 | x = [] 18 | y = [] 19 | # vertex-type := rectilinear = -1; convex = 0; concave = 1 20 | vertex_type = [] 21 | # store the bipartite graph of chords 22 | G = nx.Graph() 23 | # the line below can be commented if one needs animation 24 | gopu.speed(0) 25 | # starts recording keys being pressed 26 | gopu.begin_poly() 27 | 28 | # Event handlers 29 | stride = 25 30 | # Up = up() - vertex to be deleted = -1 31 | def up(): 32 | vertex_type.append(-1) 33 | gopu.forward(stride) 34 | 35 | # left = left() ; make vertex - convex = 0 36 | def left(): 37 | vertex_type.append(0) 38 | gopu.setheading(gopu.heading()+90) 39 | gopu.forward(stride) 40 | 41 | # right = right() ; make vertex - concave = 1 42 | def right(): 43 | vertex_type.append(1) 44 | gopu.setheading(gopu.heading()-90) 45 | gopu.forward(stride) 46 | 47 | # back = back() -- doing undo is allowed only once. 48 | def back(): 49 | vertex_type.pop() 50 | gopu.undo() 51 | 52 | # quits the screen and outputs the plots the partitioned polygon 53 | def partition_polygon(): 54 | # closing the screen 55 | wn.bye() 56 | # stopped recording the polygon 57 | gopu.end_poly() 58 | p = gopu.get_poly() 59 | p = list(p) 60 | compute_partition(p) 61 | 62 | ''' 63 | The movements of the turtle are recorded and the rectilinear polygon thus 64 | obtained is converted into bipartite graph of chords 65 | ''' 66 | def compute_partition(p): 67 | # p now contains the list of coordinates of vertices 68 | # last one is same as the origin 69 | # and the origin is always going to be a convex vertex 70 | p.pop() 71 | vertex_type[0] = 0 72 | 73 | # x and y contain list of x and y coordinates respectively 74 | for i,j in p: 75 | x.append(i) 76 | y.append(j) 77 | 78 | # this is done as there are some very small errors in recording the 79 | # position by the turtle library 80 | for i in range(len(x)): 81 | x[i] = int(x[i]) 82 | y[i] = int(y[i]) 83 | 84 | for i in range(len(x)): 85 | if x[i] % stride != 0: 86 | x[i] += (stride - x[i] % stride) 87 | if y[i] % stride != 0: 88 | y[i] += (stride - y[i] % stride) 89 | 90 | # separating concave and collinear vertices 91 | collinear_vertices = [i for i,val in enumerate(vertex_type) if val == -1] 92 | concave_vertices = [i for i,val in enumerate(vertex_type) if val == 1] 93 | 94 | # finding the chords inside the polygon 95 | horizontal_chords = [] 96 | vertical_chords = [] 97 | 98 | # middles is used because, there are cases when there is a chord between vertices 99 | # and they intersect with external chords, hence if there is any vertex in between 100 | # two vertices then skip that chord. 101 | for i in range(len(concave_vertices)): 102 | for j in range(i+1,len(concave_vertices)): 103 | if concave_vertices[j] != concave_vertices[i] + 1: 104 | middles = [] 105 | if y[concave_vertices[i]] == y[concave_vertices[j]]: 106 | for k in range(len(x)): 107 | if y[concave_vertices[i]] == y[k] and (x[concave_vertices[i]] < x[k] and x[concave_vertices[j]] > x[k] \ 108 | or x[concave_vertices[i]] > x[k] and x[concave_vertices[j]] < x[k]): 109 | middles.append(k) 110 | if len(middles) == 0: 111 | horizontal_chords.append((concave_vertices[i],concave_vertices[j])) 112 | middles = [] 113 | if x[concave_vertices[i]] == x[concave_vertices[j]]: 114 | for k in range(len(x)): 115 | if x[concave_vertices[i]] == x[k] and (y[concave_vertices[i]] < y[k] and y[concave_vertices[j]] > y[k] \ 116 | or y[concave_vertices[i]] > y[k] and y[concave_vertices[j]] < y[k]): 117 | middles.append(k) 118 | if len(middles) == 0: 119 | vertical_chords.append((concave_vertices[i],concave_vertices[j])) 120 | 121 | 122 | temp_hori = horizontal_chords[:] 123 | temp_verti = vertical_chords[:] 124 | 125 | for i in range(len(collinear_vertices)): 126 | for j in range(len(concave_vertices)): 127 | middles = [] 128 | if y[collinear_vertices[i]] == y[concave_vertices[j]]: 129 | if collinear_vertices[i] < concave_vertices[j]: 130 | for k in range(len(x)): 131 | if y[k] == y[collinear_vertices[i]] and (x[k] < x[concave_vertices[j]] \ 132 | and x[k] > x[collinear_vertices[i]] or x[k] > x[concave_vertices[j]] \ 133 | and x[k] < x[collinear_vertices[i]]): 134 | middles.append(k) 135 | if collinear_vertices[i]+1 == concave_vertices[j]: 136 | middles.append(0) 137 | else: 138 | for k in range(len(x)): 139 | if y[k] == y[collinear_vertices[i]] and (x[k] > x[concave_vertices[j]] \ 140 | and x[k] < x[collinear_vertices[i]] or x[k] < x[concave_vertices[j]] \ 141 | and x[k] > x[collinear_vertices[i]]): 142 | middles.append(k) 143 | if collinear_vertices[i] == concave_vertices[j]+1: 144 | middles.append(0) 145 | if len(middles) == 0: 146 | horizontal_chords.append((collinear_vertices[i],concave_vertices[j])) 147 | middles = [] 148 | if x[collinear_vertices[i]] == x[concave_vertices[j]]: 149 | if collinear_vertices[i] < concave_vertices[j]: 150 | for k in range(len(x)): 151 | if x[k] == x[collinear_vertices[i]] and (y[k] < y[concave_vertices[j]] \ 152 | and y[k] > y[collinear_vertices[i]] or y[k] > y[concave_vertices[j]] \ 153 | and y[k] < y[collinear_vertices[i]]): 154 | middles.append(k) 155 | if collinear_vertices[i]+1 == concave_vertices[j]: 156 | middles.append(0) 157 | else: 158 | for k in range(len(x)): 159 | if x[k] == x[collinear_vertices[i]] and (y[k] > y[concave_vertices[j]] \ 160 | and y[k] < y[collinear_vertices[i]] or y[k] < y[concave_vertices[j]] \ 161 | and y[k] > y[collinear_vertices[i]]): 162 | middles.append(k) 163 | if collinear_vertices[i] == concave_vertices[j]+1: 164 | middles.append(0) 165 | if len(middles) == 0: 166 | vertical_chords.append((collinear_vertices[i],concave_vertices[j])) 167 | # displaying all attributes and important parameters involved 168 | # plotting the initial input given 169 | print ("Initial input rectillinear graph") 170 | fig, ax = plt.subplots() 171 | ax.plot(x+[0], y+[0], color='black') 172 | ax.scatter(x+[0], y+[0], color='black') 173 | for i in range(len(x)): 174 | ax.annotate(i, (x[i],y[i])) 175 | plt.show() 176 | plt.clf() 177 | 178 | print("collinear_vertices = ", collinear_vertices) 179 | print("concave_vertices =", concave_vertices) 180 | print("horizontal_chords = " ,horizontal_chords) 181 | print("vertical_chords = ",vertical_chords) 182 | 183 | # drawing the maximum partitioned polygon 184 | print("The maximum partitioned rectillinear polygon") 185 | fig, ax = plt.subplots() 186 | ax.plot(x+[0], y+[0], color='black') 187 | ax.scatter(x+[0], y+[0], color='black') 188 | for i in range(len(x)): 189 | ax.annotate(i, (x[i],y[i])) 190 | for i,j in horizontal_chords: 191 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 192 | for i,j in vertical_chords: 193 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 194 | plt.show() 195 | plt.clf() 196 | # MAXIMUM PARTITION CODE ENDS --------------------------------- 197 | 198 | # MINIMUM PARTITION CODE STARTS ------------------------------- 199 | horizontal_chords = temp_hori[:] 200 | vertical_chords = temp_verti[:] 201 | 202 | # Creating a bipartite graph from the set of chords 203 | for i,h in enumerate(horizontal_chords): 204 | y1 = y[h[0]] 205 | x1 = min(x[h[0]] ,x[h[1]] ) 206 | x2 = max(x[h[0]] ,x[h[1]]) 207 | G.add_node(i, bipartite=1) 208 | for j,v in enumerate(vertical_chords): 209 | x3 = x[v[0]] 210 | y3 = min(y[v[0]],y[v[1]]) 211 | y4 = max(y[v[0]],y[v[1]]) 212 | G.add_node(j + len(horizontal_chords),bipartite=0) 213 | if x1 <= x3 and x3 <=x2 and y3 <= y1 and y1 <= y4: 214 | G.add_edge(i, j + len(horizontal_chords)) 215 | 216 | if len(horizontal_chords) == 0: 217 | for j,v in enumerate(vertical_chords): 218 | x3 = x[v[0]] 219 | y3 = min(y[v[0]],y[v[1]]) 220 | y4 = max(y[v[0]],y[v[1]]) 221 | G.add_node(j,bipartite=0) 222 | 223 | # finding the maximum matching of the bipartite graph, G. 224 | M = nx.Graph() 225 | maximum_matching = nx.bipartite.maximum_matching(G) 226 | maximum_matching_list = [] 227 | for i,j in maximum_matching.items(): 228 | maximum_matching_list += [(i,j)] 229 | M.add_edges_from(maximum_matching_list) 230 | maximum_matching = M.edges() 231 | # breaking up into two sets 232 | H, V = bipartite.sets(G) 233 | free_vertices = [] 234 | for u in H: 235 | temp = [] 236 | for v in V: 237 | if (u,v) in maximum_matching or (v,u) in maximum_matching: 238 | temp += [v] 239 | if len(temp) == 0: 240 | free_vertices += [u] 241 | for u in V: 242 | temp = [] 243 | for v in H: 244 | if (u,v) in maximum_matching or (v,u) in maximum_matching: 245 | temp += [v] 246 | if len(temp) == 0: 247 | free_vertices += [u] 248 | 249 | # finding the maximum independent set 250 | max_independent = [] 251 | while len(free_vertices) != 0 or len(maximum_matching) != 0: 252 | if len(free_vertices) != 0 : 253 | u = free_vertices.pop() 254 | max_independent += [u] 255 | else: 256 | u, v = maximum_matching.pop() 257 | G.remove_edge(u,v) 258 | max_independent += [u] 259 | 260 | for v in G.neighbors(u): 261 | G.remove_edge(u, v) 262 | for h in G.nodes(): 263 | if (v,h) in maximum_matching: 264 | maximum_matching.remove((v,h)) 265 | free_vertices += [h] 266 | if (h,v) in maximum_matching: 267 | maximum_matching.remove((h,v)) 268 | free_vertices += [h] 269 | 270 | 271 | # drawing the partitioned polygon 272 | independent_chords = [] 273 | for i in max_independent: 274 | if (i >= len(horizontal_chords)): 275 | independent_chords += [vertical_chords[i-len(horizontal_chords)]] 276 | else: 277 | independent_chords += [horizontal_chords[i]] 278 | unmatched_concave_vertices = [i for i in concave_vertices] 279 | for i,j in independent_chords: 280 | if i in unmatched_concave_vertices: 281 | unmatched_concave_vertices.remove(i) 282 | if j in unmatched_concave_vertices: 283 | unmatched_concave_vertices.remove(j) 284 | 285 | nearest_chord = [] 286 | for i in unmatched_concave_vertices: 287 | dist = 0 288 | nearest_distance = math.inf 289 | for j in max_independent: 290 | if j < len(horizontal_chords): 291 | temp1, temp2 = horizontal_chords[j] 292 | if abs(y[i] - y[temp1]) < nearest_distance and \ 293 | (x[i] <= x[temp1] and x[i] >= x[temp2] or x[i] >= x[temp1] and x[i] <= x[temp2]) \ 294 | and abs(temp1 - i) != 1 and abs(temp2 - i) != 1: 295 | middles = [] 296 | for u in range(len(x)): 297 | if x[i] == x[u] and (y[i] < y[u] and y[u] < y[temp1] or y[temp1] < y[u] and y[u] < y[i]): 298 | middles.append(u) 299 | if len(middles) == 0: 300 | nearest_distance = abs(y[i] - y[temp1]) 301 | dist = y[temp1] - y[i] 302 | 303 | if nearest_distance != math.inf: 304 | nearest_chord.append((i,dist)) 305 | else: 306 | for k in collinear_vertices: 307 | if x[k] == x[i] and abs(y[k] - y[i]) < nearest_distance and abs(k-i) != 1: 308 | middles = [] 309 | for u in range(len(x)): 310 | if x[i] == x[u] and (y[i] < y[u] and y[u] < y[k] or y[k] < y[u] and y[u] < y[i]): 311 | middles.append(u) 312 | if len(middles) == 0: 313 | nearest_distance = abs(y[i] - y[k]) 314 | dist = y[k] - y[i] 315 | nearest_chord.append((i,dist)) 316 | 317 | print("The minimum partitioned rectillinear polygon") 318 | fig, ax = plt.subplots() 319 | ax.plot(x+[0], y+[0], color='black') 320 | ax.scatter(x+[0], y+[0], color='black') 321 | for i in range(len(x)): 322 | ax.annotate(i, (x[i],y[i])) 323 | for i,j in independent_chords: 324 | ax.plot([x[i],x[j]],[y[i],y[j]],color='black') 325 | for i,dist in nearest_chord: 326 | ax.plot([x[i],x[i]],[y[i], y[i]+dist],color='black') 327 | plt.show() 328 | # MAXIMUM PARTITION CODE ENDS 329 | # Defining the keyboard keys function 330 | wn.onkey(up, "Up") 331 | wn.onkey(left, "Left") 332 | wn.onkey(right, "Right") 333 | wn.onkey(back, "Down") 334 | wn.onkey(partition_polygon, "Escape") 335 | wn.listen() 336 | wn.mainloop() 337 | 338 | --------------------------------------------------------------------------------