├── 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 |
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 |
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 |
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 |
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 |
135 |
136 |
137 | *Test Case 2*
138 |
139 |
140 |
141 | *Test Case 3*
142 |
143 |
144 |
145 | *Test Case 4*
146 |
147 |
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 |
--------------------------------------------------------------------------------