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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -144
app.py CHANGED
@@ -1,157 +1,209 @@
1
- # app.py
 
2
  import streamlit as st
3
  import numpy as np
4
- import plotly.graph_objs as go
5
- from dataclasses import dataclass
6
  import time
7
 
8
- # カスタムコンポーネントのインポート
9
- from my_mouse_tracker import my_mouse_tracker
10
-
11
- @dataclass
12
  class Boid:
13
- position: np.ndarray
14
- velocity: np.ndarray
 
 
15
 
16
- def initialize_boids(num_boids, width, height, speed):
17
- boids = []
18
- for _ in range(num_boids):
19
- position = np.array([np.random.uniform(0, width), np.random.uniform(0, height)])
20
- angle = np.random.uniform(0, 2*np.pi)
21
- velocity = np.array([np.cos(angle), np.sin(angle)]) * speed
22
- boids.append(Boid(position, velocity))
23
- return boids
24
 
25
- def update_boids(boids, width, height, alignment_factor, cohesion_factor, separation_factor,
26
- perception_radius, max_speed, mouse_pos=None, mouse_influence=0.05):
27
- new_boids = []
28
- for boid in boids:
29
- alignment = np.zeros(2)
30
- cohesion = np.zeros(2)
31
- separation = np.zeros(2)
 
 
 
 
 
 
 
 
32
  total = 0
33
  for other in boids:
34
- distance = np.linalg.norm(other.position - boid.position)
35
- if other != boid and distance < perception_radius:
36
- alignment += other.velocity
37
- cohesion += other.position
38
- separation += (boid.position - other.position) / distance
39
  total += 1
40
  if total > 0:
41
- alignment = (alignment / total) * alignment_factor
42
- cohesion = ((cohesion / total) - boid.position) * cohesion_factor
43
- separation = (separation / total) * separation_factor
44
- boid.velocity += alignment + cohesion + separation
45
- # マウスの影響
46
- if mouse_pos is not None:
47
- direction_to_mouse = mouse_pos - boid.position
48
- distance_to_mouse = np.linalg.norm(direction_to_mouse)
49
- if distance_to_mouse < perception_radius:
50
- # 避ける動作
51
- boid.velocity -= (direction_to_mouse / distance_to_mouse) * mouse_influence
52
- # 速度の制限
53
- speed = np.linalg.norm(boid.velocity)
54
- if speed > max_speed:
55
- boid.velocity = (boid.velocity / speed) * max_speed
56
- # 位置の更新
57
- boid.position += boid.velocity
58
- # 境界条件(トーラス型)
59
- boid.position[0] %= width
60
- boid.position[1] %= height
61
- new_boids.append(boid)
62
- return new_boids
63
-
64
- def plot_boids(boids, width, height, mouse_pos=None):
65
- x = [boid.position[0] for boid in boids]
66
- y = [boid.position[1] for boid in boids]
67
- trace = go.Scatter(
68
- x=x,
69
- y=y,
70
- mode='markers',
71
- marker=dict(size=8, color='blue'),
72
- )
73
- layout = go.Layout(
74
- xaxis=dict(range=[0, width], autorange=False, zeroline=False, showgrid=False),
75
- yaxis=dict(range=[0, height], autorange=False, zeroline=False, showgrid=False),
76
- margin=dict(l=0, r=0, t=0, b=0),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  height=600,
78
- width=600,
79
- shapes=[],
80
  )
81
- # マウス位置の表示
82
- if mouse_pos is not None:
83
- layout['shapes'] = [
84
- dict(
85
- type="circle",
86
- xref="x",
87
- yref="y",
88
- x0=mouse_pos[0]-10,
89
- y0=mouse_pos[1]-10,
90
- x1=mouse_pos[0]+10,
91
- y1=mouse_pos[1]+10,
92
- line=dict(color="red"),
93
- )
94
- ]
95
- fig = go.Figure(data=[trace], layout=layout)
96
- return fig
97
-
98
- def main():
99
- st.set_page_config(page_title="Boidsシミュレーション", layout="wide")
100
- st.title("インタラクティブBoidsシミュレーション")
101
- st.markdown("マウスの位置に反応する魚の群れをシミュレートします。")
102
-
103
- # サイドバーのパラメータ設定
104
- st.sidebar.header("パラメータ設定")
105
- num_boids = st.sidebar.slider("ボイドの数", 10, 500, 100)
106
- width = st.sidebar.slider("キャンバスの幅", 300, 1200, 800)
107
- height = st.sidebar.slider("キャンバスの高さ", 300, 1200, 800)
108
- speed = st.sidebar.slider("初期速度", 0.5, 5.0, 2.0)
109
- alignment_factor = st.sidebar.slider("整列係数", 0.0, 2.0, 1.0)
110
- cohesion_factor = st.sidebar.slider("結合係数", 0.0, 2.0, 1.0)
111
- separation_factor = st.sidebar.slider("分離係数", 0.0, 2.0, 1.5)
112
- perception_radius = st.sidebar.slider("認識半径", 10, 200, 50)
113
- max_speed = st.sidebar.slider("最大速度", 1.0, 10.0, 4.0)
114
- simulation_speed = st.sidebar.slider("シミュレーション速度(秒)", 0.01, 1.0, 0.1)
115
- mouse_influence = st.sidebar.slider("マウスの影響力", 0.0, 1.0, 0.05)
116
-
117
- # マウス位置の取得
118
- mouse_event = my_mouse_tracker()
119
- if mouse_event:
120
- mouse_x, mouse_y = mouse_event['x'], mouse_event['y']
121
- # キャンバスの座標にマッピング(必要に応じて調整)
122
- mouse_pos = np.array([mouse_x, mouse_y])
123
- else:
124
- mouse_pos = None
125
-
126
- # セッションステートの初期化
127
- if 'boids' not in st.session_state:
128
- st.session_state.boids = initialize_boids(num_boids, width, height, speed)
129
- st.session_state.last_update = time.time()
130
-
131
- # パラメータの変更時にボイドを再初期化
132
- current_params = (num_boids, width, height, speed)
133
- if st.session_state.get('params', None) != current_params:
134
- st.session_state.boids = initialize_boids(num_boids, width, height, speed)
135
- st.session_state.last_update = time.time()
136
- st.session_state.params = current_params
137
-
138
- # シミュレーションの更新
139
- current_time = time.time()
140
- if current_time - st.session_state.last_update > simulation_speed:
141
- st.session_state.boids = update_boids(
142
- st.session_state.boids, width, height,
143
- alignment_factor, cohesion_factor,
144
- separation_factor, perception_radius,
145
- max_speed, mouse_pos, mouse_influence
146
- )
147
- st.session_state.last_update = current_time
148
-
149
- # ビジュアライゼーションの表示
150
- fig = plot_boids(st.session_state.boids, width, height, mouse_pos)
151
- st.plotly_chart(fig, use_container_width=True)
152
-
153
- # 自動リロードの代替
154
- # st.experimental_rerun() を削除し、Streamlitの再実行機能に任せる
155
-
156
- if __name__ == "__main__":
157
- main()
 
1
+ # boids_streamlit_advanced.py
2
+
3
  import streamlit as st
4
  import numpy as np
5
+ import plotly.graph_objects as go
 
6
  import time
7
 
8
+ # Boidクラスの定義
 
 
 
9
  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)
17
+ self.velocity += self.acceleration
18
+ speed = np.linalg.norm(self.velocity)
19
+ if speed > params['max_speed']:
20
+ self.velocity = (self.velocity / speed) * params['max_speed']
21
+ self.position += self.velocity
22
+ self.acceleration = np.zeros(2, dtype='float64')
23
 
24
+ # 画面端でのラッピング
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
123
+
124
+ # Boidsの初期化
125
+ boids = []
126
+ for _ in range(params['num_boids']):
127
+ position = [np.random.uniform(0, width), np.random.uniform(0, height)]
128
+ angle = np.random.uniform(0, 2 * np.pi)
129
+ velocity = [np.cos(angle), np.sin(angle)]
130
+ boids.append(Boid(position, velocity))
131
+
132
+ # Plotlyのグラフ設定
133
+ 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
+ # リセットボタンの追加
162
+ if st.sidebar.button("Reset Simulation"):
163
+ boids = []
164
+ for _ in range(params['num_boids']):
165
+ position = [np.random.uniform(0, width), np.random.uniform(0, height)]
166
+ angle = np.random.uniform(0, 2 * np.pi)
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)
207
+
208
+ # フレームレート調整
209
+ time.sleep(sleep_time)