@@ -21,12 +21,19 @@ def __init__(self, r=None, a=None, bubble_distance=0):
21
21
"""
22
22
setup for bubble collapse
23
23
24
- Args:
25
- r (list, optional): radius of the bubbles. Defaults to None.
26
- a (list, optional): area of the bubbles. Defaults to None.
27
- Note: If r or a is sorted, the results might look weird
28
- bubble_distance (int, optional): minimal distance the bubbles
29
- should have after collapsing. Defaults to 0.
24
+ Parameters
25
+ ----------
26
+ r : list, optional
27
+ radius of the bubbles. Defaults to None.
28
+ a : list, optional
29
+ area of the bubbles. Defaults to None.
30
+ bubble_distance : int, optional
31
+ minimal distance the bubbles should
32
+ have after collapsing. Defaults to 0.
33
+
34
+ Notes
35
+ -----
36
+ If r or a is sorted, the results might look weird
30
37
"""
31
38
if r is None :
32
39
r = np .sqrt (a / np .pi )
@@ -59,8 +66,8 @@ def center_of_mass(self):
59
66
)
60
67
61
68
def center_distance (self , bubble , bubbles ):
62
- return np .sqrt ( np . power ( bubble [0 ] - bubbles [:, 0 ], 2 ) +
63
- np . power ( bubble [1 ] - bubbles [:, 1 ], 2 ) )
69
+ return np .hypot ( bubble [0 ] - bubbles [:, 0 ],
70
+ bubble [1 ] - bubbles [:, 1 ])
64
71
65
72
def outline_distance (self , bubble , bubbles ):
66
73
center_distance = self .center_distance (bubble , bubbles )
@@ -76,59 +83,73 @@ def collides_with(self, bubble, bubbles):
76
83
idx_min = np .argmin (distance )
77
84
return idx_min if type (idx_min ) == np .ndarray else [idx_min ]
78
85
79
- def collapse (self ):
80
- moves = 0
81
- for i in range (len (self )):
82
- rest_bub = np .delete (self .bubbles , i , 0 )
83
- # try to move directly towoards the center of mass
84
- # dir_vec from bubble to com
85
- dir_vec = self .com - self .bubbles [i , :2 ]
86
-
87
- # shorten dir_vec to have length of 1
88
- dir_vec = dir_vec / np .sqrt (dir_vec .dot (dir_vec ))
89
-
90
- # calculate new bubble position
91
- new_point = self .bubbles [i , :2 ] + dir_vec * self .step_dist
92
- new_bubble = np .append (new_point , self .bubbles [i , 2 :4 ])
93
-
94
- # check whether new bubble collides with oder bubbles
95
- if not self .check_collisions (new_bubble , rest_bub ):
96
- self .bubbles [i , :] = new_bubble
97
- self .com = self .center_of_mass ()
98
- moves += 1
99
- else :
100
- # try to move around a bubble that you collide with
101
- # find colliding bubble
102
- for colliding in self .collides_with (new_bubble , rest_bub ):
103
- # calculate dir vec
104
- dir_vec = rest_bub [colliding , :2 ] - self .bubbles [i , :2 ]
105
- dir_vec = dir_vec / np .sqrt (dir_vec .dot (dir_vec ))
106
- # calculate orthagonal vec
107
- orth = np .array ([dir_vec [1 ], - dir_vec [0 ]])
108
- # test which dir to go
109
- new_point1 = self .bubbles [i , :2 ] + orth * self .step_dist
110
- new_point2 = self .bubbles [i , :2 ] - orth * self .step_dist
111
- dist1 = self .center_distance (
112
- self .com , np .array ([new_point1 ]))
113
- dist2 = self .center_distance (
114
- self .com , np .array ([new_point2 ]))
115
- new_point = new_point1 if dist1 < dist2 else new_point2
116
- new_bubble = np .append (new_point , self .bubbles [i , 2 :4 ])
117
- if not self .check_collisions (new_bubble , rest_bub ):
118
- self .bubbles [i , :] = new_bubble
119
- self .com = self .center_of_mass ()
120
-
121
- if moves / len (self ) < 0.1 :
122
- self .step_dist = self .step_dist / 2
86
+ def collapse (self , n_iterations = 50 ):
87
+ """
88
+ Move bubbles to the center of mass
89
+
90
+ Parameters
91
+ ----------
92
+ n_iterations :int, optional
93
+ number of moves to perform. Defaults to 50.
94
+ """
95
+ for _i in range (n_iterations ):
96
+ moves = 0
97
+ for i in range (len (self )):
98
+ rest_bub = np .delete (self .bubbles , i , 0 )
99
+ # try to move directly towoards the center of mass
100
+ # dir_vec from bubble to com
101
+ dir_vec = self .com - self .bubbles [i , :2 ]
102
+
103
+ # shorten dir_vec to have length of 1
104
+ dir_vec = dir_vec / np .sqrt (dir_vec .dot (dir_vec ))
105
+
106
+ # calculate new bubble position
107
+ new_point = self .bubbles [i , :2 ] + dir_vec * self .step_dist
108
+ new_bubble = np .append (new_point , self .bubbles [i , 2 :4 ])
109
+
110
+ # check whether new bubble collides with other bubbles
111
+ if not self .check_collisions (new_bubble , rest_bub ):
112
+ self .bubbles [i , :] = new_bubble
113
+ self .com = self .center_of_mass ()
114
+ moves += 1
115
+ else :
116
+ # try to move around a bubble that you collide with
117
+ # find colliding bubble
118
+ for colliding in self .collides_with (new_bubble , rest_bub ):
119
+ # calculate dir vec
120
+ dir_vec = rest_bub [colliding , :2 ] - self .bubbles [i , :2 ]
121
+ dir_vec = dir_vec / np .sqrt (dir_vec .dot (dir_vec ))
122
+ # calculate orthagonal vec
123
+ orth = np .array ([dir_vec [1 ], - dir_vec [0 ]])
124
+ # test which dir to go
125
+ new_point1 = (self .bubbles [i , :2 ] + orth *
126
+ self .step_dist )
127
+ new_point2 = (self .bubbles [i , :2 ] - orth *
128
+ self .step_dist )
129
+ dist1 = self .center_distance (
130
+ self .com , np .array ([new_point1 ]))
131
+ dist2 = self .center_distance (
132
+ self .com , np .array ([new_point2 ]))
133
+ new_point = new_point1 if dist1 < dist2 else new_point2
134
+ new_bubble = np .append (new_point , self .bubbles [i , 2 :4 ])
135
+ if not self .check_collisions (new_bubble , rest_bub ):
136
+ self .bubbles [i , :] = new_bubble
137
+ self .com = self .center_of_mass ()
138
+
139
+ if moves / len (self ) < 0.1 :
140
+ self .step_dist = self .step_dist / 2
123
141
124
142
def plot (self , ax , labels , colors ):
125
143
"""
126
144
draw the bubble plot
127
145
128
- Args:
129
- ax (matplotlib.axes._subplots.AxesSubplot)
130
- labels (list): labels of the bubbles
131
- colors (list): colors of the bubbles
146
+ Parameters
147
+ ----------
148
+ ax : matplotlib.axes._subplots.AxesSubplot
149
+ labels : list
150
+ labels of the bubbles
151
+ colors : list
152
+ colors of the bubbles
132
153
"""
133
154
for i in range (len (self )):
134
155
circ = plt .Circle (
@@ -142,10 +163,7 @@ def plot(self, ax, labels, colors):
142
163
bubble_plot = BubbleChart (a = np .array (
143
164
browser_market_share ['market_share' ]), bubble_distance = 1 )
144
165
145
- # collapse plot 50 times. In some cases it might be useful
146
- # to do this more or less often
147
- for i in range (50 ):
148
- bubble_plot .collapse ()
166
+ bubble_plot .collapse ()
149
167
150
168
fig , ax = plt .subplots (subplot_kw = dict (aspect = "equal" ))
151
169
bubble_plot .plot (
0 commit comments