naohiro701 commited on
Commit
5d1845f
·
verified ·
1 Parent(s): def8be6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -113
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # boids_streamlit_advanced.py
2
 
3
  import streamlit as st
4
  import numpy as np
@@ -10,7 +10,7 @@ class Boid:
10
  def __init__(self, position, velocity):
11
  self.position = np.array(position, dtype='float64')
12
  self.velocity = np.array(velocity, dtype='float64')
13
- self.acceleration = np.zeros(2, dtype='float64')
14
 
15
  def update(self, boids, width, height, params):
16
  self.flock(boids, params)
@@ -25,98 +25,97 @@ class Boid:
25
  self.position[0] = self.position[0] % width
26
  self.position[1] = self.position[1] % height
27
 
 
 
 
 
 
 
28
  def flock(self, boids, params):
29
- alignment = self.align(boids, params)
30
- cohesion = self.cohere(boids, params)
31
- separation = self.separate(boids, params)
 
 
32
 
33
- self.acceleration += alignment * params['align_weight']
34
- self.acceleration += cohesion * params['cohesion_weight']
35
- self.acceleration += separation * params['separation_weight']
 
 
36
 
37
- def align(self, boids, params):
38
- steering = np.zeros(2, dtype='float64')
39
- total = 0
40
  for other in boids:
41
- distance = np.linalg.norm(other.position - self.position)
42
- if other != self and distance < params['neighbor_radius']:
43
- steering += other.velocity
44
- total += 1
45
- if total > 0:
46
- steering /= total
47
- steering = self.set_magnitude(steering, params['max_speed'])
48
- steering -= self.velocity
49
- steering = self.limit(steering, params['max_force'])
50
- return steering
51
-
52
- def cohere(self, boids, params):
53
- steering = np.zeros(2, dtype='float64')
54
- total = 0
 
 
55
  for other in boids:
56
- distance = np.linalg.norm(other.position - self.position)
57
- if other != self and distance < params['neighbor_radius']:
58
- steering += other.position
59
- total += 1
60
- if total > 0:
61
- steering /= total
62
- steering -= self.position
63
- steering = self.set_magnitude(steering, params['max_speed'])
64
- steering -= self.velocity
65
- steering = self.limit(steering, params['max_force'])
66
- return steering
67
-
68
- def separate(self, boids, params):
69
- steering = np.zeros(2, dtype='float64')
70
- total = 0
71
  for other in boids:
72
- distance = np.linalg.norm(other.position - self.position)
73
- if other != self and distance < params['desired_separation']:
74
- diff = self.position - other.position
75
- if distance > 0:
76
- diff /= distance
77
- steering += diff
78
- total += 1
79
- if total > 0:
80
- steering /= total
81
- steering = self.set_magnitude(steering, params['max_speed'])
82
- steering -= self.velocity
83
- steering = self.limit(steering, params['max_force'])
84
- return steering
85
-
86
- def set_magnitude(self, vector, magnitude):
87
- norm = np.linalg.norm(vector)
88
- if norm == 0:
89
- return vector
90
- return (vector / norm) * magnitude
91
-
92
- def limit(self, vector, max_value):
93
- norm = np.linalg.norm(vector)
94
- if norm > max_value:
95
- return (vector / norm) * max_value
96
- return vector
97
 
98
  # シミュレーションパラメータ
99
  params = {
100
- 'num_boids': 50,
101
- 'max_speed': 4.0,
102
- 'max_force': 0.1,
103
- 'neighbor_radius': 50.0,
104
- 'desired_separation': 25.0,
105
- 'align_weight': 1.0,
106
- 'cohesion_weight': 1.0,
107
- 'separation_weight': 1.5
 
108
  }
109
 
110
  # Streamlitのサイドバーでパラメータを調整
111
  st.sidebar.title("Boids Simulation Parameters")
112
- params['num_boids'] = st.sidebar.slider("Number of Boids", 10, 200, 50)
113
- params['max_speed'] = st.sidebar.slider("Max Speed", 1.0, 10.0, 4.0)
114
- params['max_force'] = st.sidebar.slider("Max Force", 0.01, 1.0, 0.1)
115
- params['neighbor_radius'] = st.sidebar.slider("Neighbor Radius", 10.0, 100.0, 50.0)
116
- params['desired_separation'] = st.sidebar.slider("Desired Separation", 5.0, 50.0, 25.0)
117
- params['align_weight'] = st.sidebar.slider("Alignment Weight", 0.0, 5.0, 1.0)
118
- params['cohesion_weight'] = st.sidebar.slider("Cohesion Weight", 0.0, 5.0, 1.0)
119
- params['separation_weight'] = st.sidebar.slider("Separation Weight", 0.0, 5.0, 1.5)
 
 
120
 
121
  # シミュレーション画面サイズ
122
  width, height = 800, 600
@@ -134,28 +133,41 @@ fig = go.Figure(
134
  layout=go.Layout(
135
  xaxis=dict(range=[0, width], autorange=False, showgrid=False, zeroline=False),
136
  yaxis=dict(range=[0, height], autorange=False, showgrid=False, zeroline=False),
137
- width=800,
138
- height=600,
139
  margin=dict(l=0, r=0, t=0, b=0)
140
  )
141
  )
142
 
 
143
  scatter = go.Scatter(
144
  x=[boid.position[0] for boid in boids],
145
  y=[boid.position[1] for boid in boids],
146
  mode='markers',
147
- marker=dict(size=10, color='blue')
148
  )
149
 
150
  fig.add_trace(scatter)
151
 
152
- # Streamlitのメインループ
153
- st.title("Boids Simulation with Streamlit and Plotly")
 
 
 
 
 
 
 
 
 
 
 
154
 
 
155
  animation_placeholder = st.empty()
156
 
157
  # アニメーションの実行
158
- frame_rate = st.sidebar.slider("Frame Rate (FPS)", 1, 60, 20)
159
  sleep_time = 1.0 / frame_rate
160
 
161
  # リセットボタンの追加
@@ -167,40 +179,31 @@ if st.sidebar.button("Reset Simulation"):
167
  velocity = [np.cos(angle), np.sin(angle)]
168
  boids.append(Boid(position, velocity))
169
 
170
- # アニメーションの実行
171
  while True:
172
  # Boidsの更新
173
  for boid in boids:
174
  boid.update(boids, width, height, params)
175
 
176
- # 各Boidの向きを計算
177
- angles = [np.arctan2(boid.velocity[1], boid.velocity[0]) for boid in boids]
 
178
 
179
- # 矢印用の線データを作成
180
- arrow_x = []
181
- arrow_y = []
182
- arrow_u = []
183
- arrow_v = []
184
- for boid, angle in zip(boids, angles):
185
- arrow_x.append(boid.position[0])
186
- arrow_y.append(boid.position[1])
187
- arrow_u.append(np.cos(angle) * 10) # 矢印の長さ
188
- arrow_v.append(np.sin(angle) * 10)
189
 
190
  # グラフの更新
191
- fig.data[0].x = [boid.position[0] for boid in boids]
192
- fig.data[0].y = [boid.position[1] for boid in boids]
193
-
194
- # 矢印の追加
195
- fig.data = fig.data[:1] # 既存のデータをクリア
196
- fig.add_trace(go.Scatter(
197
- x=[x for x, u in zip(arrow_x, arrow_u)],
198
- y=[y for y, v in zip(arrow_y, arrow_v)],
199
- mode='markers+lines',
200
- marker=dict(size=5, color='red'),
201
- line=dict(width=1, color='red'),
202
- showlegend=False
203
- ))
204
 
205
  # アニメーション表示
206
  animation_placeholder.plotly_chart(fig, use_container_width=True)
 
1
+ # boids_streamlit.py
2
 
3
  import streamlit as st
4
  import numpy as np
 
10
  def __init__(self, position, velocity):
11
  self.position = np.array(position, dtype='float64')
12
  self.velocity = np.array(velocity, dtype='float64')
13
+ self.history = []
14
 
15
  def update(self, boids, width, height, params):
16
  self.flock(boids, params)
 
25
  self.position[0] = self.position[0] % width
26
  self.position[1] = self.position[1] % height
27
 
28
+ # 履歴に現在の位置を追加
29
+ if params['draw_trail']:
30
+ self.history.append(self.position.copy())
31
+ if len(self.history) > params['max_history']:
32
+ self.history.pop(0)
33
+
34
  def flock(self, boids, params):
35
+ self.acceleration = np.zeros(2, dtype='float64')
36
+ self.fly_towards_center(boids, params)
37
+ self.avoid_others(boids, params)
38
+ self.match_velocity(boids, params)
39
+ self.limit_speed(params)
40
 
41
+ def fly_towards_center(self, boids, params):
42
+ centering_factor = params['centering_factor']
43
+ center_x = 0.0
44
+ center_y = 0.0
45
+ num_neighbors = 0
46
 
 
 
 
47
  for other in boids:
48
+ if other is not self and self.distance(other) < params['visual_range']:
49
+ center_x += other.position[0]
50
+ center_y += other.position[1]
51
+ num_neighbors += 1
52
+
53
+ if num_neighbors > 0:
54
+ center_x /= num_neighbors
55
+ center_y /= num_neighbors
56
+ direction = np.array([center_x, center_y]) - self.position
57
+ self.acceleration += centering_factor * direction
58
+
59
+ def avoid_others(self, boids, params):
60
+ min_distance = params['min_distance']
61
+ avoid_factor = params['avoid_factor']
62
+ move = np.zeros(2, dtype='float64')
63
+
64
  for other in boids:
65
+ if other is not self and self.distance(other) < min_distance:
66
+ move += self.position - other.position
67
+
68
+ if np.linalg.norm(move) > 0:
69
+ move = move / np.linalg.norm(move) * avoid_factor
70
+ self.acceleration += move
71
+
72
+ def match_velocity(self, boids, params):
73
+ matching_factor = params['matching_factor']
74
+ avg_velocity = np.zeros(2, dtype='float64')
75
+ num_neighbors = 0
76
+
 
 
 
77
  for other in boids:
78
+ if other is not self and self.distance(other) < params['visual_range']:
79
+ avg_velocity += other.velocity
80
+ num_neighbors += 1
81
+
82
+ if num_neighbors > 0:
83
+ avg_velocity /= num_neighbors
84
+ self.acceleration += matching_factor * (avg_velocity - self.velocity)
85
+
86
+ def limit_speed(self, params):
87
+ speed = np.linalg.norm(self.velocity)
88
+ if speed > params['max_speed']:
89
+ self.velocity = (self.velocity / speed) * params['max_speed']
90
+
91
+ def distance(self, other):
92
+ return np.linalg.norm(self.position - other.position)
 
 
 
 
 
 
 
 
 
 
93
 
94
  # シミュレーションパラメータ
95
  params = {
96
+ 'num_boids': 100,
97
+ 'visual_range': 75.0,
98
+ 'min_distance': 20.0,
99
+ 'centering_factor': 0.005,
100
+ 'avoid_factor': 0.05,
101
+ 'matching_factor': 0.05,
102
+ 'max_speed': 15.0,
103
+ 'draw_trail': False,
104
+ 'max_history': 50
105
  }
106
 
107
  # Streamlitのサイドバーでパラメータを調整
108
  st.sidebar.title("Boids Simulation Parameters")
109
+ params['num_boids'] = st.sidebar.slider("Number of Boids", 10, 300, 100)
110
+ params['visual_range'] = st.sidebar.slider("Visual Range", 10.0, 200.0, 75.0)
111
+ params['min_distance'] = st.sidebar.slider("Minimum Separation Distance", 5.0, 100.0, 20.0)
112
+ params['centering_factor'] = st.sidebar.slider("Centering Factor", 0.001, 0.02, 0.005)
113
+ params['avoid_factor'] = st.sidebar.slider("Avoidance Factor", 0.01, 0.1, 0.05)
114
+ params['matching_factor'] = st.sidebar.slider("Matching Factor", 0.01, 0.1, 0.05)
115
+ params['max_speed'] = st.sidebar.slider("Maximum Speed", 5.0, 30.0, 15.0)
116
+ params['draw_trail'] = st.sidebar.checkbox("Draw Trails")
117
+ if params['draw_trail']:
118
+ params['max_history'] = st.sidebar.slider("Trail Length", 10, 100, 50)
119
 
120
  # シミュレーション画面サイズ
121
  width, height = 800, 600
 
133
  layout=go.Layout(
134
  xaxis=dict(range=[0, width], autorange=False, showgrid=False, zeroline=False),
135
  yaxis=dict(range=[0, height], autorange=False, showgrid=False, zeroline=False),
136
+ width=width,
137
+ height=height,
138
  margin=dict(l=0, r=0, t=0, b=0)
139
  )
140
  )
141
 
142
+ # Boidsの初期位置をプロット
143
  scatter = go.Scatter(
144
  x=[boid.position[0] for boid in boids],
145
  y=[boid.position[1] for boid in boids],
146
  mode='markers',
147
+ marker=dict(size=8, color='blue')
148
  )
149
 
150
  fig.add_trace(scatter)
151
 
152
+ # トレイル用のトレース
153
+ if params['draw_trail']:
154
+ trail_scatter = go.Scatter(
155
+ x=[],
156
+ y=[],
157
+ mode='lines',
158
+ line=dict(color='rgba(0,0,255,0.2)', width=1),
159
+ showlegend=False
160
+ )
161
+ fig.add_trace(trail_scatter)
162
+
163
+ # Streamlitのタイトル
164
+ st.title("Boids Simulation")
165
 
166
+ # アニメーションの表示領域
167
  animation_placeholder = st.empty()
168
 
169
  # アニメーションの実行
170
+ frame_rate = 30 # フレームレート (FPS)
171
  sleep_time = 1.0 / frame_rate
172
 
173
  # リセットボタンの追加
 
179
  velocity = [np.cos(angle), np.sin(angle)]
180
  boids.append(Boid(position, velocity))
181
 
 
182
  while True:
183
  # Boidsの更新
184
  for boid in boids:
185
  boid.update(boids, width, height, params)
186
 
187
+ # Boidsの位置データを更新
188
+ scatter.x = [boid.position[0] for boid in boids]
189
+ scatter.y = [boid.position[1] for boid in boids]
190
 
191
+ # トレイルの更新
192
+ if params['draw_trail']:
193
+ trail_x = []
194
+ trail_y = []
195
+ for boid in boids:
196
+ trail_x.extend([pos[0] for pos in boid.history])
197
+ trail_y.extend([pos[1] for pos in boid.history])
198
+ trail_scatter.x = trail_x
199
+ trail_scatter.y = trail_y
 
200
 
201
  # グラフの更新
202
+ fig.data[0].x = scatter.x
203
+ fig.data[0].y = scatter.y
204
+ if params['draw_trail']:
205
+ fig.data[1].x = trail_scatter.x
206
+ fig.data[1].y = trail_scatter.y
 
 
 
 
 
 
 
 
207
 
208
  # アニメーション表示
209
  animation_placeholder.plotly_chart(fig, use_container_width=True)