naohiro701 commited on
Commit
d0a8e75
·
verified ·
1 Parent(s): cbd8607

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -323
app.py CHANGED
@@ -1,327 +1,165 @@
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
- # Define the Boid class
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
- self.history = []
15
-
16
- def update(self, boids, width, height, params):
17
- self.flock(boids, params)
18
- self.velocity += self.acceleration
19
- speed = np.linalg.norm(self.velocity)
20
- if speed > params['max_speed']:
21
- self.velocity = (self.velocity / speed) * params['max_speed']
22
- self.position += self.velocity
23
- self.acceleration = np.zeros(2, dtype='float64')
24
-
25
- # Screen wrapping
26
- self.position[0] = self.position[0] % width
27
- self.position[1] = self.position[1] % height
28
-
29
- # Add current position to history
30
- if params['draw_trail']:
31
- self.history.append(self.position.copy())
32
- if len(self.history) > params['max_history']:
33
- self.history.pop(0)
34
-
35
- def flock(self, boids, params):
36
- self.acceleration = np.zeros(2, dtype='float64')
37
- self.fly_towards_center(boids, params)
38
- self.avoid_others(boids, params)
39
- self.match_velocity(boids, params)
40
- self.limit_speed(params)
41
- self.avoid_boundaries(width=params['width'], height=params['height'], params=params)
42
-
43
- def fly_towards_center(self, boids, params):
44
- centering_factor = params['centering_factor']
45
- center_x = 0.0
46
- center_y = 0.0
47
- num_neighbors = 0
48
-
49
- for other in boids:
50
- if other is not self and self.distance(other) < params['visual_range']:
51
- center_x += other.position[0]
52
- center_y += other.position[1]
53
- num_neighbors += 1
54
-
55
- if num_neighbors > 0:
56
- center_x /= num_neighbors
57
- center_y /= num_neighbors
58
- direction = np.array([center_x, center_y]) - self.position
59
- self.acceleration += centering_factor * direction
60
-
61
- def avoid_others(self, boids, params):
62
- min_distance = params['min_distance']
63
- avoid_factor = params['avoid_factor']
64
- move = np.zeros(2, dtype='float64')
65
-
66
- for other in boids:
67
- if other is not self and self.distance(other) < min_distance:
68
- move += self.position - other.position
69
-
70
- if np.linalg.norm(move) > 0:
71
- move = move / np.linalg.norm(move) * avoid_factor
72
- self.acceleration += move
73
-
74
- def match_velocity(self, boids, params):
75
- matching_factor = params['matching_factor']
76
- avg_velocity = np.zeros(2, dtype='float64')
77
- num_neighbors = 0
78
-
79
- for other in boids:
80
- if other is not self and self.distance(other) < params['visual_range']:
81
- avg_velocity += other.velocity
82
- num_neighbors += 1
83
-
84
- if num_neighbors > 0:
85
- avg_velocity /= num_neighbors
86
- self.acceleration += matching_factor * (avg_velocity - self.velocity)
87
-
88
- def limit_speed(self, params):
89
- speed = np.linalg.norm(self.velocity)
90
- if speed > params['max_speed']:
91
- self.velocity = (self.velocity / speed) * params['max_speed']
92
-
93
- def avoid_boundaries(self, width, height, params):
94
- margin = params['boundary_margin']
95
- turn_factor = params['boundary_turn_factor']
96
-
97
- if self.position[0] < margin:
98
- self.acceleration[0] += turn_factor
99
- elif self.position[0] > width - margin:
100
- self.acceleration[0] -= turn_factor
101
-
102
- if self.position[1] < margin:
103
- self.acceleration[1] += turn_factor
104
- elif self.position[1] > height - margin:
105
- self.acceleration[1] -= turn_factor
106
-
107
- def distance(self, other):
108
- return np.linalg.norm(self.position - other.position)
109
-
110
- # Simulation parameters
111
- params = {
112
- 'num_boids': 100,
113
- 'visual_range': 75.0,
114
- 'min_distance': 20.0,
115
- 'centering_factor': 0.005,
116
- 'avoid_factor': 0.05,
117
- 'matching_factor': 0.05,
118
- 'max_speed': 15.0,
119
- 'draw_trail': False,
120
- 'max_history': 50,
121
- 'width': 800,
122
- 'height': 600,
123
- 'boundary_margin': 100.0,
124
- 'boundary_turn_factor': 0.05
125
- }
126
-
127
- # Streamlit sidebar for parameter adjustments
128
- st.sidebar.title("Boids Simulation Parameters")
129
- params['num_boids'] = st.sidebar.slider("Number of Boids", 10, 300, 100)
130
- params['visual_range'] = st.sidebar.slider("Visual Range", 10.0, 200.0, 75.0)
131
- params['min_distance'] = st.sidebar.slider("Minimum Separation Distance", 5.0, 100.0, 20.0)
132
- params['centering_factor'] = st.sidebar.slider("Centering Factor", 0.001, 0.02, 0.005)
133
- params['avoid_factor'] = st.sidebar.slider("Avoidance Factor", 0.01, 0.1, 0.05)
134
- params['matching_factor'] = st.sidebar.slider("Matching Factor", 0.01, 0.1, 0.05)
135
- params['max_speed'] = st.sidebar.slider("Maximum Speed", 5.0, 30.0, 15.0)
136
- params['draw_trail'] = st.sidebar.checkbox("Draw Trails")
137
- if params['draw_trail']:
138
- params['max_history'] = st.sidebar.slider("Trail Length", 10, 100, 50)
139
- params['boundary_margin'] = st.sidebar.slider("Boundary Margin", 50.0, 300.0, 100.0)
140
- params['boundary_turn_factor'] = st.sidebar.slider("Boundary Turn Factor", 0.01, 0.2, 0.05)
141
-
142
- # Simulation screen size
143
- width, height = 800, 600
144
- params['width'] = width
145
- params['height'] = height
146
-
147
- # Initialize Boids
148
- boids = []
149
- for _ in range(params['num_boids']):
150
- position = [np.random.uniform(0, width), np.random.uniform(0, height)]
151
- angle = np.random.uniform(0, 2 * np.pi)
152
- velocity = [np.cos(angle), np.sin(angle)]
153
- boids.append(Boid(position, velocity))
154
-
155
- # Plotly graph setup
156
- fig = go.Figure(
157
- layout=go.Layout(
158
- xaxis=dict(range=[0, width], autorange=False, showgrid=False, zeroline=False),
159
- yaxis=dict(range=[0, height], autorange=False, showgrid=False, zeroline=False),
160
- width=width,
161
- height=height,
162
- margin=dict(l=0, r=0, t=0, b=0)
163
- )
164
- )
165
-
166
- # Plot initial positions of Boids
167
- scatter = go.Scatter(
168
- x=[boid.position[0] for boid in boids],
169
- y=[boid.position[1] for boid in boids],
170
- mode='markers',
171
- marker=dict(size=8, color='blue')
172
- )
173
-
174
- fig.add_trace(scatter)
175
-
176
- # Trail trace
177
- if params['draw_trail']:
178
- trail_scatter = go.Scatter(
179
- x=[],
180
- y=[],
181
- mode='lines',
182
- line=dict(color='rgba(0,0,255,0.2)', width=1),
183
- showlegend=False
184
  )
185
- fig.add_trace(trail_scatter)
186
-
187
- # Simplified Title
188
- st.title("Boids Simulation")
189
-
190
- # Animation display area
191
- animation_placeholder = st.empty()
192
-
193
- # Explanation Section Title
194
- st.header("Mathematical Background of the Boids Algorithm")
195
-
196
- # Explanation Section
197
- st.markdown("### **Overview of the Boids Algorithm**")
198
- st.markdown("""
199
- The Boids algorithm, proposed by Craig Reynolds in 1986, is a method for simulating flocking behavior in groups of agents called Boids. Each agent follows simple rules to recreate complex group dynamics. The three fundamental rules are:
200
-
201
- 1. **Separation**: Maintain a suitable distance from nearby Boids to avoid collisions.
202
- 2. **Alignment**: Align velocity with the average velocity of neighboring Boids.
203
- 3. **Cohesion**: Move towards the average position of neighboring Boids.
204
- """)
205
-
206
- st.markdown("### **Mathematical Model**")
207
- st.markdown("""
208
- The movement of each Boid is represented by its position vector \(\mathbf{p}_i(t)\) and velocity vector \(\mathbf{v}_i(t)\). The position and velocity of Boid \(i\) at time \(t\) are described by the following differential equations:
209
- """)
210
-
211
- st.latex(r"""
212
- \frac{d\mathbf{p}_i(t)}{dt} = \mathbf{v}_i(t)
213
- """)
214
- st.latex(r"""
215
- \frac{d\mathbf{v}_i(t)}{dt} = \mathbf{a}_i(t)
216
- """)
217
-
218
- st.markdown("""
219
- Here, the acceleration \(\mathbf{a}_i(t)\) is the sum of three forces:
220
- """)
221
- st.latex(r"""
222
- \mathbf{a}_i(t) = \mathbf{a}_{\text{separation}} + \mathbf{a}_{\text{alignment}} + \mathbf{a}_{\text{cohesion}}
223
- """)
224
-
225
- st.markdown("#### **1. Separation**")
226
- st.markdown("""
227
- To prevent collisions, the separation force is calculated based on the distance \(d_{ij}\) between Boid \(i\) and its neighboring Boids \(j\):
228
- """)
229
- st.latex(r"""
230
- \mathbf{a}_{\text{separation}} = \sum_{j \in N(i)} \frac{\mathbf{p}_i - \mathbf{p}_j}{d_{ij}^2}
231
- """)
232
- st.markdown("where \(N(i)\) is the set of neighboring Boids around Boid \(i\).")
233
-
234
- st.markdown("#### **2. Alignment**")
235
- st.markdown("""
236
- The alignment force encourages Boid \(i\) to match the average velocity \(\mathbf{v}_{\text{avg}}\) of its neighbors:
237
- """)
238
- st.latex(r"""
239
- \mathbf{a}_{\text{alignment}} = \frac{\mathbf{v}_{\text{avg}} - \mathbf{v}_i}{\tau}
240
- """)
241
- st.markdown("where \(\tau\) is a scaling parameter.")
242
-
243
- st.markdown("#### **3. Cohesion**")
244
- st.markdown("""
245
- The cohesion force steers Boid \(i\) towards the average position \(\mathbf{C}_{\text{avg}}\) of its neighbors:
246
- """)
247
- st.latex(r"""
248
- \mathbf{a}_{\text{cohesion}} = \frac{\mathbf{C}_{\text{avg}} - \mathbf{p}_i}{\sigma}
249
- """)
250
- st.markdown("where \(\sigma\) is a scaling parameter.")
251
 
252
- st.markdown("### **Update Rules**")
253
- st.markdown("""
254
- Each Boid's position and velocity are updated based on discrete time steps \(\Delta t\) as follows:
255
- """)
256
- st.latex(r"""
257
- \mathbf{v}_i(t + \Delta t) = \mathbf{v}_i(t) + \mathbf{a}_i(t) \Delta t
258
- """)
259
- st.latex(r"""
260
- \mathbf{p}_i(t + \Delta t) = \mathbf{p}_i(t) + \mathbf{v}_i(t + \Delta t) \Delta t
261
- """)
262
- st.markdown("""
263
- These equations ensure that each Boid updates its velocity and position based on the combined separation, alignment, and cohesion forces.
264
- """)
265
-
266
- st.markdown("### **Additional Features**")
267
- st.markdown("""
268
- This simulation includes the following additional features:
269
-
270
- 1. **Boundary Avoidance**: When a Boid approaches the edge of the simulation area, it receives a steering force to remain within bounds, preventing it from moving off-screen.
271
- 2. **Trail Drawing**: The past positions of each Boid are displayed as trails, allowing visualization of their movement patterns.
272
- """)
273
-
274
- st.markdown("### **Parameters and Their Roles**")
275
- st.markdown("""
276
- - **Boundary Margin (\(M\))**: The distance from the edge of the simulation area at which Boids begin to steer away.
277
- - **Boundary Turn Factor (\(\gamma\))**: The strength of the steering force applied when avoiding boundaries.
278
-
279
- These parameters allow fine-tuning of Boid behavior near the edges of the simulation area.
280
- """)
281
-
282
-
283
- # Animation settings
284
- frame_rate = 30 # Frames per second
285
- sleep_time = 1.0 / frame_rate
286
-
287
- # Reset button
288
- if st.sidebar.button("Reset Simulation"):
289
- boids = []
290
- for _ in range(params['num_boids']):
291
- position = [np.random.uniform(0, width), np.random.uniform(0, height)]
292
- angle = np.random.uniform(0, 2 * np.pi)
293
- velocity = [np.cos(angle), np.sin(angle)]
294
- boids.append(Boid(position, velocity))
295
-
296
- # Animation loop
297
- while True:
298
- # Update Boids
299
- for boid in boids:
300
- boid.update(boids, width, height, params)
301
-
302
- # Update Boids' positions
303
- scatter.x = [boid.position[0] for boid in boids]
304
- scatter.y = [boid.position[1] for boid in boids]
305
-
306
- # Update trails
307
- if params['draw_trail']:
308
- trail_x = []
309
- trail_y = []
310
- for boid in boids:
311
- trail_x.extend([pos[0] for pos in boid.history])
312
- trail_y.extend([pos[1] for pos in boid.history])
313
- trail_scatter.x = trail_x
314
- trail_scatter.y = trail_y
315
-
316
- # Update the Plotly figure
317
- fig.data[0].x = scatter.x
318
- fig.data[0].y = scatter.y
319
- if params['draw_trail']:
320
- fig.data[1].x = trail_scatter.x
321
- fig.data[1].y = trail_scatter.y
322
-
323
- # Display the animation
324
- animation_placeholder.plotly_chart(fig, use_container_width=True)
325
 
326
- # Control the frame rate
327
- time.sleep(sleep_time)
 
 
 
 
1
  import streamlit as st
2
+ import subprocess
3
+ import fitz
4
+ from pathlib import Path
5
+ from io import BytesIO
6
+
7
+ def compile_latex(latex_code: str, tex_file_path: Path) -> Path:
8
+ """
9
+ Compile LaTeX source into a PDF using upLaTeX and dvipdfmx.
10
+ Args:
11
+ latex_code: LaTeX source as a string.
12
+ tex_file_path: Path where the .tex file will be written.
13
+ Returns:
14
+ Path to the generated PDF file.
15
+ """
16
+ # write .tex file
17
+ tex_file_path.write_text(latex_code, encoding="utf-8")
18
+ # run upLaTeX to produce .dvi
19
+ subprocess.run(["uplatex", str(tex_file_path)], check=True)
20
+ # convert .dvi to .pdf
21
+ dvi_path = tex_file_path.with_suffix(".dvi")
22
+ subprocess.run(["dvipdfmx", str(dvi_path)], check=True)
23
+ return tex_file_path.with_suffix(".pdf")
24
+
25
+ def show_pdf_page(pdf_file_path: Path, page_index: int = 0) -> bytes:
26
+ """
27
+ Render one page of a PDF as PNG and display in Streamlit.
28
+ Args:
29
+ pdf_file_path: Path to the PDF to render.
30
+ page_index: Zero-based index of the page to render.
31
+ Returns:
32
+ PNG bytes of the rendered page.
33
+ """
34
+ document = fitz.open(str(pdf_file_path))
35
+ if page_index < 0 or page_index >= len(document):
36
+ st.error("Page index out of range") # (事実)
37
+ return b""
38
+ page = document[page_index]
39
+ pix = page.get_pixmap(dpi=150)
40
+ img_bytes = pix.tobytes("png")
41
+ st.image(BytesIO(img_bytes), caption=f"Page {page_index+1}/{len(document)}")
42
+ return img_bytes
43
+
44
+ # Streamlit configuration
45
+ st.set_page_config(layout="wide")
46
+
47
+ # Session state initialization
48
+ if "file_name" not in st.session_state:
49
+ st.session_state.file_name = "untitled.tex"
50
+ if "current_page" not in st.session_state:
51
+ st.session_state.current_page = 0
52
+ if "editor_content" not in st.session_state:
53
+ st.session_state.editor_content = ""
54
+
55
+ # File name input (ensure .tex)
56
+ name_input = st.text_input("File name", st.session_state.file_name)
57
+ if not name_input.endswith(".tex"):
58
+ name_input += ".tex"
59
+ st.session_state.file_name = name_input
60
+
61
+ st.title(f"LaTeX Editor – {st.session_state.file_name}")
62
+
63
+ col_edit, col_preview = st.columns([1, 1])
64
+
65
+ with col_edit:
66
+ # LaTeX ソース入力欄
67
+ latex_source = st.text_area("LaTeX Source", value=st.session_state.editor_content, height=600, key="editor_area")
68
+ st.session_state.editor_content = latex_source
69
+
70
+ # コンパイルボタン
71
+ if st.button("✅ Compile"):
72
+ try:
73
+ tex_path = Path(st.session_state.file_name)
74
+ pdf_path = compile_latex(latex_source, tex_path)
75
+ st.session_state.compiled_pdf = pdf_path
76
+ st.session_state.current_page = 0
77
+ st.success("Compiled successfully.") # (事実)
78
+ except subprocess.CalledProcessError as e:
79
+ st.error(f"Compilation failed: {e}") # (事実)
80
+
81
+ # .tex ダウンロード
82
+ st.download_button(
83
+ "📂 Download .tex",
84
+ data=latex_source,
85
+ file_name=st.session_state.file_name,
86
+ mime="text/plain"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ with col_preview:
90
+ if "compiled_pdf" in st.session_state:
91
+ pdf_path = st.session_state.compiled_pdf
92
+ png_bytes = show_pdf_page(pdf_path, st.session_state.current_page)
93
+
94
+ btn_prev, btn_next, btn_pdf_dl, btn_png_dl = st.columns([1, 1, 1, 1])
95
+ with btn_prev:
96
+ if st.button("Previous Page"):
97
+ st.session_state.current_page = max(0, st.session_state.current_page - 1)
98
+ with btn_next:
99
+ if st.button("Next Page"):
100
+ doc = fitz.open(str(pdf_path))
101
+ last_idx = len(doc) - 1
102
+ st.session_state.current_page = min(last_idx, st.session_state.current_page + 1)
103
+ with btn_pdf_dl:
104
+ pdf_bytes = pdf_path.read_bytes()
105
+ st.download_button(
106
+ "⬇️ Download PDF",
107
+ data=pdf_bytes,
108
+ file_name=pdf_path.name,
109
+ mime="application/pdf"
110
+ )
111
+ with btn_png_dl:
112
+ if png_bytes:
113
+ st.download_button(
114
+ "⬇️ Download PNG",
115
+ data=png_bytes,
116
+ file_name=f"page_{st.session_state.current_page+1}.png",
117
+ mime="image/png"
118
+ )
119
+
120
+ # テンプレート定義(TITLE, AUTHOR は適宜書き換えてください)
121
+ templates = {
122
+ "Report": r"""\documentclass[uplatex]{jsarticle}
123
+ \usepackage{graphicx}
124
+ \usepackage{here}
125
+ \usepackage{amsmath}
126
+ \begin{document}
127
+ \begin{titlepage}
128
+ \centering
129
+ \vspace*{30mm}
130
+ {\scalebox{2}{\textbf{TITLE}}}
131
+ \vspace{15mm}
132
+
133
+ {\scalebox{1.2}{AUTHOR}}
134
+ \vfill
135
+
136
+ {\today}
137
+ \end{titlepage}
138
+ \newpage
139
+
140
+ \end{document}""",
141
+ "Figure": r"""\begin{figure}[H]
142
+ \centering
143
+ \includegraphics[width=0.8\linewidth]{example.jpg}
144
+ \caption{Sample}
145
+ \label{fig:sample}
146
+ \end{figure}""",
147
+ "Table": r"""\begin{table}[H]
148
+ \centering
149
+ \begin{tabular}{|c|c|}
150
+ \hline
151
+ A & B \\ \hline
152
+ 1 & 2 \\ \hline
153
+ \end{tabular}
154
+ \caption{Sample Table}
155
+ \label{tab:sample}
156
+ \end{table}""",
157
+ "Align": r"""\begin{align}
158
+ a &= b + c \\
159
+ d &= e - f
160
+ \end{align}"""
161
+ }
162
 
163
+ selection = st.selectbox("Insert Template", ["None"] + list(templates.keys()))
164
+ if selection != "None" and st.button("Insert Template"):
165
+ st.session_state.editor_content += "\n" + templates[selection]