Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
yangliu28
GitHub Repository: yangliu28/swarm_formation_sim
Path: blob/master/trigridnet_generator.py
104 views
1
# random 2D network generator for robot swarm on 2D equilateral triangle grid
2
# the networks are connected in one piece, there is a path between any pair of nodes
3
4
# Constraint of triangle grid guaranteed the robots are uniformed spaced, and 6 connections
5
# at most for each robot. Robots will reside in the joint of edges, the edges represents the
6
# connections between robots.
7
8
# Equilateral triangle grid coordinates:
9
# Position of a node on a 2D plane has two degrees of freedom. Following this rule, even
10
# though three intersecting axes can be drew on the triangle grid, only two are chosen as
11
# the coordinates. The chosen two axes are angled at pi/3, with x pointing right, and y
12
# pointing pi/3 CCW relative to x, like a warped Cartesian coordinate system. The nodes can
13
# be located by drawing parallel lines to the two aces. The position of a node is similarly
14
# described as (x,y) in integers.
15
16
# No topology duplication check:
17
# It's possible that two generated networks having different grid layouts may share the same
18
# topology. It's preferable to combine these two layouts, because the result of the algorithm
19
# test is only related to network topology. But since telling whether one grid layout has the
20
# same topology with another is with too much effort, I just use the grid layout as test
21
# subject of topology. Besides, it's not that often that two layouts are of same topology
22
# when network size is very large.
23
24
25
import math, random, sys, os, time
26
import matplotlib.pyplot as plt
27
import getopt
28
29
def main():
30
size = 0 # network size to be read from input
31
32
savefile = True
33
# read command line options
34
try:
35
opts, args = getopt.getopt(sys.argv[1:], 'n:', ['nosave'])
36
except getopt.GetoptError as err:
37
print str(err)
38
sys.exit()
39
for opt,arg in opts:
40
if opt == '-n':
41
size = int(arg)
42
elif opt == '--nosave':
43
savefile = False
44
45
node_size = 10 # default 10
46
47
# use list to store the node information
48
nodes_t = [] # target nodes pool, for decided nodes in target network
49
nodes_a = [] # available nodes pool, for nodes available to be added to network
50
# place the first node at the origin for the network
51
nodes_t.append((0,0))
52
for pos in get_neighbors(nodes_t[0]):
53
nodes_a.append(pos) # append all six neighbors to available pool
54
55
# loop for randomly placing new nodes from available pool to generate the network
56
for i in range(size-1): # first node is decided and excluded
57
# randomly choose one from the available pool
58
pos_new = random.choice(nodes_a)
59
nodes_a.remove(pos_new) # remove selected node from available pool
60
nodes_t.append(pos_new) # add new node to the target pool
61
# check and update every neighbor of newly selected node
62
for pos in get_neighbors(pos_new):
63
if pos in nodes_t: continue
64
if pos in nodes_a: continue
65
# if none of the above, add to the available pool
66
nodes_a.append(pos)
67
68
# generate the connection variable, 0 for not connected, 1 for connected
69
connections = [[0 for j in range(size)] for i in range(size)] # populated with zeros
70
for i in range(size):
71
for j in range(i+1, size):
72
# find if nodes[i] and nodes[j] are neighbors
73
diff_x = nodes_t[i][0] - nodes_t[j][0]
74
diff_y = nodes_t[i][1] - nodes_t[j][1]
75
if abs(diff_x) + abs(diff_y) == 1 or diff_x * diff_y == -1:
76
# condition 1: one of the axis value difference is 1, the other is 0
77
# condition 2: one of the axis value difference is 1, the other is -1
78
connections[i][j] = 1
79
connections[j][i] = 1
80
81
# the network has been generated, save it to file
82
if savefile:
83
save_folder = 'trigrid-networks' # folder for triangle grid network files
84
save_path = os.path.join(os.getcwd(), save_folder)
85
filename_count = 1 # always start to try filename with suffix of 1
86
new_filename = str(size) + '-' + str(filename_count)
87
all_files = os.listdir(save_path)
88
# check to avoid filename duplication
89
while new_filename in all_files:
90
filename_count = filename_count + 1
91
new_filename = str(size) + '-' + str(filename_count)
92
new_filepath = os.path.join(save_path, new_filename)
93
f = open(new_filepath, 'w')
94
for pos in nodes_t:
95
f.write(str(pos[0]) + ' ' + str(pos[1]) + '\n')
96
f.close()
97
98
# plot the network as dots and lines
99
fig_side_size = int(math.sqrt(size)*0.7) # calculate fig side size from network size
100
fig = plt.figure(figsize=(fig_side_size, fig_side_size)) # square fig
101
fig.canvas.set_window_title('2D Triangle Grid Network Generator')
102
splt = fig.add_subplot(1,1,1)
103
splt.axis('equal') # equal scale for x and y axis
104
splt.tick_params(axis='both',
105
which='both',
106
bottom='off',
107
top='off',
108
left='off',
109
right='off',
110
labelbottom='off',
111
labelleft='off') # turn off ticks and labels
112
# convert node position from triangle grid to Cartesian, for plotting
113
nodes_t_plt = [trigrid_to_cartesian(pos) for pos in nodes_t]
114
# set x and y axes limits
115
xmin = min([pos[0] for pos in nodes_t_plt])
116
xmax = max([pos[0] for pos in nodes_t_plt])
117
ymin = min([pos[1] for pos in nodes_t_plt])
118
ymax = max([pos[1] for pos in nodes_t_plt])
119
splt.set_xlim([xmin-0.5, xmax+0.5]) # leave space on both sides
120
splt.set_ylim([ymin-0.5, ymax+0.5])
121
# draw the connections as lines
122
for i in range(size):
123
for j in range(i+1, size):
124
if connections[i][j] == 1:
125
splt.plot([nodes_t_plt[i][0], nodes_t_plt[j][0]],
126
[nodes_t_plt[i][1], nodes_t_plt[j][1]], '-k')
127
for i in range(size):
128
splt.plot(nodes_t_plt[i][0], nodes_t_plt[i][1], 'o',
129
markersize=node_size, markerfacecolor='black')
130
131
# show the figure, press to exit
132
fig.show()
133
# choose one of the following two lines
134
raw_input("Press <ENTER> to continue")
135
plt.close(fig)
136
137
138
# return the positions of the six neighbors of the input node on triangle grid
139
# The first four neighbors are just like the situation in the Cartesian coordinates, the last
140
# two neighbors are the two on the diagonal line along the y=-x axis, because the triangle
141
# grid is like askewing the y axis toward x, allowing more space in second and fourth quadrants.
142
def get_neighbors(pos):
143
x = pos[0]
144
y = pos[1]
145
return [(x+1, y), (x-1, y),
146
(x, y+1), (x, y-1),
147
(x+1, y-1), (x-1, y+1)]
148
149
# return Cartesian coordinates of triangle grid nodes for plotting
150
def trigrid_to_cartesian(pos):
151
# the resulting coordinates should be in floating point numbers
152
x = float(pos[0])
153
y = float(pos[1])
154
# askewing y axis to the right for pi/6
155
return [x+y*math.sin(math.pi/6), y*math.cos(math.pi/6)]
156
157
if __name__ == '__main__':
158
main()
159
160
161
162