NavyDevilDoc commited on
Commit
cd7c7c8
·
verified ·
1 Parent(s): e8ad508

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +540 -37
src/streamlit_app.py CHANGED
@@ -1,40 +1,543 @@
1
- import altair as alt
2
  import numpy as np
 
3
  import pandas as pd
4
- import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
1
+ import streamlit as st
2
  import numpy as np
3
+ import matplotlib.pyplot as plt
4
  import pandas as pd
5
+ from math import pi, radians, degrees
6
+ import time
7
+
8
+ # Set page configuration
9
+ st.set_page_config(
10
+ page_title="Trigonometry Basics Explorer",
11
+ page_icon="📐",
12
+ layout="wide"
13
+ )
14
+
15
+ def safe_division(numerator, denominator, undefined_value=float('inf')):
16
+ """Safely handle division by zero for trigonometric functions."""
17
+ return numerator / denominator if abs(denominator) > 1e-10 else undefined_value
18
+
19
+ def calculate_trig_functions(angle_rad):
20
+ """Calculate all six trigonometric functions for a given angle in radians."""
21
+ sin_val = np.sin(angle_rad)
22
+ cos_val = np.cos(angle_rad)
23
+
24
+ # Primary functions
25
+ sine = sin_val
26
+ cosine = cos_val
27
+ tangent = safe_division(sin_val, cos_val)
28
+
29
+ # Reciprocal functions
30
+ cosecant = safe_division(1, sin_val)
31
+ secant = safe_division(1, cos_val)
32
+ cotangent = safe_division(cos_val, sin_val)
33
+
34
+ return {
35
+ 'sin': sine,
36
+ 'cos': cosine,
37
+ 'tan': tangent,
38
+ 'csc': cosecant,
39
+ 'sec': secant,
40
+ 'cot': cotangent
41
+ }
42
+
43
+
44
+ def create_animated_unit_circle(angle_degrees, show_animation=False, show_special_angles=True):
45
+
46
+ """Create unit circle with animation trail and enhanced features."""
47
+ fig, ax = plt.subplots(figsize=(9, 9))
48
+
49
+ # Draw unit circle
50
+ theta = np.linspace(0, 2*pi, 100)
51
+ x_circle = np.cos(theta)
52
+ y_circle = np.sin(theta)
53
+ ax.plot(x_circle, y_circle, 'b-', linewidth=3, label='Unit Circle', alpha=0.8)
54
+
55
+ # Convert angle to radians
56
+ angle_rad = radians(angle_degrees)
57
+
58
+ # Calculate point on circle
59
+ x_point = np.cos(angle_rad)
60
+ y_point = np.sin(angle_rad)
61
+
62
+ # Add quadrant shading
63
+ quadrant_colors = ['lightblue', 'lightgreen', 'lightyellow', 'lightcoral']
64
+ angles_quad = [np.linspace(i*pi/2, (i+1)*pi/2, 50) for i in range(4)]
65
+
66
+ for i, angles in enumerate(angles_quad):
67
+ x_quad = np.cos(angles)
68
+ y_quad = np.sin(angles)
69
+ ax.fill_between([0]*len(x_quad), [0]*len(y_quad),
70
+ x_quad, alpha=0.1, color=quadrant_colors[i])
71
+
72
+ # Add special angle markers if enabled
73
+ if show_special_angles:
74
+ special_angles = [0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 240, 270, 300, 315, 330]
75
+ special_colors = ['red', 'orange', 'gold', 'green', 'blue', 'purple', 'brown', 'pink',
76
+ 'gray', 'olive', 'cyan', 'magenta', 'lime', 'indigo', 'coral', 'navy']
77
+
78
+ for i, special_angle in enumerate(special_angles):
79
+ special_rad = radians(special_angle)
80
+ x_special = np.cos(special_rad)
81
+ y_special = np.sin(special_rad)
82
+
83
+ # Plot special angle point
84
+ ax.plot(x_special, y_special, 'o', color=special_colors[i % len(special_colors)],
85
+ markersize=6, markeredgecolor='black', markeredgewidth=1, alpha=0.8)
86
+
87
+ # Add angle labels (only for major angles to avoid clutter)
88
+ if special_angle % 30 == 0 or special_angle in [45, 135, 225, 315]:
89
+ # Position label slightly outside the circle
90
+ label_x = 1.15 * x_special
91
+ label_y = 1.15 * y_special
92
+ ax.text(label_x, label_y, f'{special_angle}°',
93
+ fontsize=9, fontweight='bold', ha='center', va='center',
94
+ bbox=dict(boxstyle="round,pad=0.2", facecolor='white', alpha=0.7, edgecolor='gray'))
95
+
96
+
97
+ # Draw radius line with arrow
98
+ ax.annotate('', xy=(x_point, y_point), xytext=(0, 0),
99
+ arrowprops=dict(arrowstyle='->', color='red', lw=3))
100
+
101
+ # Mark the point with enhanced styling
102
+ ax.plot(x_point, y_point, 'ro', markersize=12,
103
+ markeredgecolor='darkred', markeredgewidth=2)
104
+
105
+ # Draw coordinate lines with labels
106
+ ax.plot([x_point, x_point], [0, y_point], 'g--', linewidth=3,
107
+ alpha=0.8, label=f'sin({angle_degrees}°) = {y_point:.3f}')
108
+ ax.plot([0, x_point], [0, 0], 'm--', linewidth=3,
109
+ alpha=0.8, label=f'cos({angle_degrees}°) = {x_point:.3f}')
110
+
111
+ # Add angle arc
112
+ if angle_degrees > 0:
113
+ arc_angles = np.linspace(0, angle_rad, max(int(angle_degrees/10), 5))
114
+ arc_x = 0.3 * np.cos(arc_angles)
115
+ arc_y = 0.3 * np.sin(arc_angles)
116
+ ax.plot(arc_x, arc_y, 'orange', linewidth=3, alpha=0.7)
117
+
118
+ # Add angle label
119
+ mid_angle = angle_rad / 2
120
+ ax.text(0.4 * np.cos(mid_angle), 0.4 * np.sin(mid_angle),
121
+ f'{angle_degrees}°', fontsize=12, fontweight='bold',
122
+ ha='center', va='center',
123
+ bbox=dict(boxstyle="round,pad=0.3", facecolor='white', alpha=0.8))
124
+
125
+ # Add coordinate labels
126
+ ax.text(x_point + 0.05, y_point + 0.05,
127
+ f'({x_point:.3f}, {y_point:.3f})',
128
+ fontsize=11, fontweight='bold',
129
+ bbox=dict(boxstyle="round,pad=0.3", facecolor='yellow', alpha=0.7))
130
+
131
+ # Draw axes with labels
132
+ ax.axhline(y=0, color='k', linewidth=1)
133
+ ax.axvline(x=0, color='k', linewidth=1)
134
+
135
+ # Add quadrant labels
136
+ ax.text(0.7, 0.7, 'Q1', fontsize=14, fontweight='bold', ha='center', va='center')
137
+ ax.text(-0.7, 0.7, 'Q2', fontsize=14, fontweight='bold', ha='center', va='center')
138
+ ax.text(-0.7, -0.7, 'Q3', fontsize=14, fontweight='bold', ha='center', va='center')
139
+ ax.text(0.7, -0.7, 'Q4', fontsize=14, fontweight='bold', ha='center', va='center')
140
+
141
+ # Set equal aspect ratio and limits
142
+ ax.set_xlim(-1.3, 1.3)
143
+ ax.set_ylim(-1.3, 1.3)
144
+ ax.set_aspect('equal')
145
+ ax.grid(True, alpha=0.3)
146
+ ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
147
+ ax.set_title(f'Unit Circle at {angle_degrees}°', fontsize=16, fontweight='bold')
148
+
149
+ # Add axis labels
150
+ ax.set_xlabel('Cosine values', fontsize=12, fontweight='bold')
151
+ ax.set_ylabel('Sine values', fontsize=12, fontweight='bold')
152
+
153
+ plt.tight_layout()
154
+ return fig
155
+
156
+ def create_enhanced_function_plots(angle_range, selected_functions, current_angle=None, show_special_angles=True):
157
+ """Enhanced function plots with current angle highlighted."""
158
+ num_functions = len(selected_functions)
159
+ if num_functions == 0:
160
+ return None
161
+
162
+ # Dynamic subplot arrangement
163
+ if num_functions <= 2:
164
+ fig, axes = plt.subplots(1, num_functions, figsize=(7*num_functions, 6))
165
+ elif num_functions <= 4:
166
+ fig, axes = plt.subplots(2, 2, figsize=(14, 10))
167
+ else:
168
+ fig, axes = plt.subplots(2, 3, figsize=(15, 10))
169
+
170
+ if num_functions == 1:
171
+ axes = [axes]
172
+ else:
173
+ axes = axes.flatten()
174
+
175
+ angle_rad = np.radians(angle_range)
176
+
177
+ functions = {
178
+ 'sin': {'func': np.sin, 'color': 'blue', 'title': 'Sine Function', 'ylim': (-1.1, 1.1)},
179
+ 'cos': {'func': np.cos, 'color': 'red', 'title': 'Cosine Function', 'ylim': (-1.1, 1.1)},
180
+ 'tan': {'func': lambda x: np.where(np.abs(np.cos(x)) > 1e-10, np.tan(x), np.nan), 'color': 'green', 'title': 'Tangent Function', 'ylim': (-5, 5)},
181
+ 'csc': {'func': lambda x: np.where(np.abs(np.sin(x)) > 1e-10, 1/np.sin(x), np.nan), 'color': 'purple', 'title': 'Cosecant Function', 'ylim': (-5, 5)},
182
+ 'sec': {'func': lambda x: np.where(np.abs(np.cos(x)) > 1e-10, 1/np.cos(x), np.nan), 'color': 'orange', 'title': 'Secant Function', 'ylim': (-5, 5)},
183
+ 'cot': {'func': lambda x: np.where(np.abs(np.sin(x)) > 1e-10, np.cos(x)/np.sin(x), np.nan), 'color': 'brown', 'title': 'Cotangent Function', 'ylim': (-5, 5)}
184
+ }
185
+ plot_idx = 0
186
+ for func_name in selected_functions:
187
+ if func_name in functions:
188
+ ax = axes[plot_idx]
189
+ func_info = functions[func_name]
190
+
191
+ try:
192
+ y_values = func_info['func'](angle_rad)
193
+ y_values = np.clip(y_values, func_info['ylim'][0], func_info['ylim'][1])
194
+ ax.plot(angle_range, y_values, color=func_info['color'], linewidth=2.5, alpha=0.8)
195
+
196
+ # Highlight current angle
197
+ if current_angle is not None:
198
+ current_y = func_info['func'](radians(current_angle))
199
+ if abs(current_y) <= abs(func_info['ylim'][1]):
200
+ ax.plot(current_angle, current_y, 'o', color='red', markersize=10,
201
+ markeredgecolor='darkred', markeredgewidth=2)
202
+ ax.axvline(x=current_angle, color='red', linestyle='--', alpha=0.5)
203
+
204
+ ax.set_title(func_info['title'], fontsize=14, fontweight='bold')
205
+ ax.set_xlabel('Angle (degrees)', fontsize=12)
206
+ ax.set_ylabel(f'{func_name}(θ)', fontsize=12)
207
+ ax.grid(True, alpha=0.3)
208
+ ax.axhline(y=0, color='k', linewidth=0.5)
209
+ ax.axvline(x=0, color='k', linewidth=0.5)
210
+ ax.set_ylim(func_info['ylim'])
211
+
212
+ # Add special angle markers only if enabled
213
+ if show_special_angles:
214
+ special_angles = [0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 240, 270, 300, 315, 330, 360]
215
+ for special_angle in special_angles:
216
+ if angle_range[0] <= special_angle <= angle_range[-1]:
217
+ ax.axvline(x=special_angle, color='gray', linestyle=':', alpha=0.3)
218
+ # Add small text labels for key angles
219
+ if special_angle % 90 == 0 or special_angle in [30, 45, 60, 135, 225, 315]:
220
+ ax.text(special_angle, func_info['ylim'][1] * 0.9, f'{special_angle}°',
221
+ rotation=90, fontsize=8, ha='center', va='top', alpha=0.7)
222
+
223
+ except:
224
+ ax.text(0.5, 0.5, 'Function has\ndiscontinuities',
225
+ transform=ax.transAxes, ha='center', va='center',
226
+ fontsize=12, bbox=dict(boxstyle="round,pad=0.3", facecolor='lightgray'))
227
+ ax.set_title(func_info['title'])
228
+
229
+ plot_idx += 1
230
+
231
+ # Hide unused subplots
232
+ for i in range(plot_idx, len(axes)):
233
+ axes[i].set_visible(False)
234
+
235
+ plt.tight_layout()
236
+ return fig
237
+
238
+ def create_comparison_table(angle_degrees):
239
+ """Create an enhanced comparison table with more information."""
240
+ trig_values = calculate_trig_functions(radians(angle_degrees))
241
+
242
+ # Determine quadrant
243
+ quadrant = ""
244
+ if 0 <= angle_degrees < 90:
245
+ quadrant = "I"
246
+ elif 90 <= angle_degrees < 180:
247
+ quadrant = "II"
248
+ elif 180 <= angle_degrees < 270:
249
+ quadrant = "III"
250
+ else:
251
+ quadrant = "IV"
252
+
253
+ # Create enhanced dataframe
254
+ values_df = pd.DataFrame({
255
+ 'Function': ['sin(θ)', 'cos(θ)', 'tan(θ)', 'csc(θ)', 'sec(θ)', 'cot(θ)'],
256
+ 'Value': [
257
+ f"{trig_values['sin']:.4f}",
258
+ f"{trig_values['cos']:.4f}",
259
+ f"{trig_values['tan']:.4f}" if abs(trig_values['tan']) < 1000 else "undefined",
260
+ f"{trig_values['csc']:.4f}" if abs(trig_values['csc']) < 1000 else "undefined",
261
+ f"{trig_values['sec']:.4f}" if abs(trig_values['sec']) < 1000 else "undefined",
262
+ f"{trig_values['cot']:.4f}" if abs(trig_values['cot']) < 1000 else "undefined"
263
+ ],
264
+ 'Sign': [
265
+ "+" if trig_values['sin'] >= 0 else "-",
266
+ "+" if trig_values['cos'] >= 0 else "-",
267
+ "+" if abs(trig_values['tan']) < 1000 and trig_values['tan'] >= 0 else "-" if abs(trig_values['tan']) < 1000 else "N/A",
268
+ "+" if abs(trig_values['csc']) < 1000 and trig_values['csc'] >= 0 else "-" if abs(trig_values['csc']) < 1000 else "N/A",
269
+ "+" if abs(trig_values['sec']) < 1000 and trig_values['sec'] >= 0 else "-" if abs(trig_values['sec']) < 1000 else "N/A",
270
+ "+" if abs(trig_values['cot']) < 1000 and trig_values['cot'] >= 0 else "-" if abs(trig_values['cot']) < 1000 else "N/A"
271
+ ],
272
+ 'Definition': [
273
+ 'y-coordinate / opposite',
274
+ 'x-coordinate / adjacent',
275
+ 'sin(θ)/cos(θ) = opp/adj',
276
+ '1/sin(θ) = hyp/opp',
277
+ '1/cos(θ) = hyp/adj',
278
+ 'cos(θ)/sin(θ) = adj/opp'
279
+ ]
280
+ })
281
+
282
+ return values_df, quadrant
283
+
284
+ def main():
285
+ st.title("📐 Trigonometry Basics Explorer")
286
+ st.markdown("### Learn the Six Basic Trigonometric Functions Interactively!")
287
+
288
+ # Add a fun fact or tip of the day
289
+ tips = [
290
+ "💡 **Tip**: Remember SOHCAHTOA - Sine=Opposite/Hypotenuse, Cosine=Adjacent/Hypotenuse, Tangent=Opposite/Adjacent",
291
+ "🎯 **Did you know?**: The word 'sine' comes from the Latin word 'sinus' meaning 'bay' or 'fold'",
292
+ "🔄 **Pattern**: Notice how sine and cosine are just shifted versions of each other!",
293
+ "📊 **Memory trick**: In Quadrant I, all functions are positive. Use 'All Students Take Calculus' for Q1,Q2,Q3,Q4",
294
+ "🌊 **Cool fact**: Trigonometric functions model waves, from sound waves to ocean tides!"
295
+ ]
296
+
297
+ st.info(np.random.choice(tips))
298
+
299
+ # Sidebar controls
300
+ st.sidebar.header("🎛️ Controls")
301
+
302
+ # Enhanced function selection - MOVED TO TOP TO FIX SCOPE ISSUE
303
+ st.sidebar.subheader("📊 Function Plots")
304
+ col1, col2 = st.sidebar.columns(2)
305
+ with col1:
306
+ show_primary = st.checkbox("Primary Functions", value=True)
307
+ with col2:
308
+ show_reciprocal = st.checkbox("Reciprocal Functions", value=False)
309
+
310
+ selected_functions = []
311
+ if show_primary:
312
+ selected_functions.extend(['sin', 'cos', 'tan'])
313
+ if show_reciprocal:
314
+ selected_functions.extend(['csc', 'sec', 'cot'])
315
+
316
+ # Plot options
317
+ plot_range = st.sidebar.slider("Plot range (degrees)", 180, 720, 360, 90)
318
+ show_special_angles = st.sidebar.checkbox("Show special angle markers", value=True)
319
+
320
+ # Enhanced angle input - MOVED AFTER FUNCTION SELECTION
321
+ angle_input_method = st.sidebar.radio(
322
+ "Choose angle input method:",
323
+ ["Slider", "Text Input", "Common Angles", "Auto Animation"]
324
+ )
325
+
326
+ # Enhanced angle input method
327
+ st.sidebar.subheader("🔧 Settings")
328
+ angle_unit = st.sidebar.radio("Angle Units:", ["Degrees", "Radians"])
329
+
330
+ # Modify angle input methods to support both units
331
+ if angle_input_method == "Slider":
332
+ if angle_unit == "Degrees":
333
+ angle = st.sidebar.slider("Angle (degrees)", 0, 360, 45, 5)
334
+ else:
335
+ angle_rad = st.sidebar.slider("Angle (radians)", 0.0, 2*pi, pi/4, 0.1)
336
+ angle = degrees(angle_rad)
337
+
338
+ elif angle_input_method == "Text Input":
339
+ if angle_unit == "Degrees":
340
+ angle = st.sidebar.number_input("Angle (degrees)", value=45.0, step=1.0, min_value=0.0, max_value=360.0, key="degrees_input")
341
+ else:
342
+ angle_rad = st.sidebar.number_input("Angle (radians)", value=pi/4, step=0.1, min_value=0.0, max_value=2*pi, key="radians_input")
343
+ angle = degrees(angle_rad)
344
+
345
+ elif angle_input_method == "Auto Animation":
346
+ speed = st.sidebar.slider("Animation Speed", 1, 10, 5, key="animation_speed")
347
+ angle_step = st.sidebar.slider("Angle Step", 5, 30, 15, key="animation_step")
348
+
349
+
350
+ # Add animation controls
351
+ col_anim1, col_anim2 = st.sidebar.columns(2)
352
+ with col_anim1:
353
+ start_animation = st.sidebar.button("▶️ Start", key="start_anim") # Fixed: moved to sidebar
354
+ with col_anim2:
355
+ stop_animation = st.sidebar.button("⏹️ Stop", key="stop_anim") # Fixed: moved to sidebar
356
+
357
+ # Add session state for animation control
358
+ if 'animation_running' not in st.session_state:
359
+ st.session_state.animation_running = False
360
+
361
+ if start_animation:
362
+ st.session_state.animation_running = True
363
+ if stop_animation:
364
+ st.session_state.animation_running = False
365
+
366
+ # Fixed: Simplified animation logic
367
+ if st.session_state.animation_running:
368
+ # Create placeholders for the animated content
369
+ col1_placeholder = st.empty()
370
+
371
+ for i in range(0, 361, angle_step):
372
+ # Check if animation should stop
373
+ if not st.session_state.animation_running:
374
+ break
375
+
376
+ angle = i
377
+
378
+ # Create two columns for layout
379
+ with col1_placeholder.container():
380
+ col1, col2 = st.columns([1, 1])
381
+
382
+ with col1:
383
+ st.subheader("🔄 Enhanced Unit Circle")
384
+ st.write(f"**Current angle: {angle}°**")
385
+
386
+ # Generate and display unit circle
387
+ fig_circle = create_animated_unit_circle(angle, show_special_angles=show_special_angles)
388
+ st.pyplot(fig_circle)
389
+ plt.close() # Important: close figure to prevent memory issues
390
+
391
+ # Show current values
392
+ trig_values = calculate_trig_functions(radians(angle))
393
+ st.write(f"sin({angle}°) = {trig_values['sin']:.3f}")
394
+ st.write(f"cos({angle}°) = {trig_values['cos']:.3f}")
395
+
396
+ with col2:
397
+ if selected_functions:
398
+ st.subheader("📈 Enhanced Function Graphs")
399
+ # Generate and display function plots
400
+ angle_range = np.linspace(-plot_range//2, plot_range//2, 1000)
401
+ fig_functions = create_enhanced_function_plots(angle_range, selected_functions, angle, show_special_angles)
402
+ if fig_functions:
403
+ st.pyplot(fig_functions)
404
+ plt.close() # Important: close figure to prevent memory issues
405
+ else:
406
+ st.info("Select function types from the sidebar to see animated graphs.")
407
+
408
+ # Control animation speed
409
+ time.sleep(0.5 / speed) # Adjust timing based on speed slider
410
+
411
+ # Reset animation state when complete
412
+ st.session_state.animation_running = False
413
+ st.success("Animation complete!")
414
+ angle = 360 # Set final angle
415
+ else:
416
+ angle = 0 # Default angle when not animating
417
+ else: # Common Angles
418
+ common_angles = {
419
+ "0°": 0, "30°": 30, "45°": 45, "60°": 60, "90°": 90,
420
+ "120°": 120, "135°": 135, "150°": 150, "180°": 180,
421
+ "210°": 210, "225°": 225, "240°": 240, "270°": 270,
422
+ "300°": 300, "315°": 315, "330°": 330, "360°": 360
423
+ }
424
+ selected_angle = st.sidebar.selectbox("Select common angle:", list(common_angles.keys()))
425
+ angle = common_angles[selected_angle]
426
+
427
+ # Main content area
428
+ col1, col2 = st.columns([1, 1])
429
+
430
+ with col1:
431
+ st.subheader("🔄 Enhanced Unit Circle")
432
+ fig_circle = create_animated_unit_circle(angle)
433
+ st.pyplot(fig_circle)
434
+
435
+ # Enhanced values table
436
+ values_df, quadrant = create_comparison_table(angle)
437
+
438
+ st.subheader(f"📊 Function Values (Quadrant {quadrant})")
439
+ st.dataframe(values_df, use_container_width=True)
440
+
441
+ # Add quadrant information
442
+ st.info(f"**Angle {angle}° is in Quadrant {quadrant}**")
443
+
444
+ with col2:
445
+ if selected_functions:
446
+ st.subheader("📈 Enhanced Function Graphs")
447
+ angle_range = np.linspace(-plot_range//2, plot_range//2, 1000)
448
+ fig_functions = create_enhanced_function_plots(angle_range, selected_functions, angle, show_special_angles)
449
+ st.pyplot(fig_functions)
450
+ else:
451
+ st.info("Select function types from the sidebar to see their graphs.")
452
+
453
+ # Add a mini calculator
454
+ st.subheader("🧮 Quick Calculator")
455
+ calc_angle = st.number_input("Calculate for angle:", value=float(angle), step=1.0)
456
+ calc_values = calculate_trig_functions(radians(calc_angle))
457
+
458
+ col_calc1, col_calc2 = st.columns(2)
459
+ with col_calc1:
460
+ st.metric("sin", f"{calc_values['sin']:.4f}")
461
+ st.metric("cos", f"{calc_values['cos']:.4f}")
462
+ st.metric("tan", f"{calc_values['tan']:.4f}" if abs(calc_values['tan']) < 1000 else "undefined")
463
+ with col_calc2:
464
+ st.metric("csc", f"{calc_values['csc']:.4f}" if abs(calc_values['csc']) < 1000 else "undefined")
465
+ st.metric("sec", f"{calc_values['sec']:.4f}" if abs(calc_values['sec']) < 1000 else "undefined")
466
+ st.metric("cot", f"{calc_values['cot']:.4f}" if abs(calc_values['cot']) < 1000 else "undefined")
467
+
468
+ # Educational content
469
+ st.markdown("---")
470
+ st.subheader("📚 Understanding Trigonometric Functions")
471
+
472
+ tab1, tab2, tab3, tab4, tab5 = st.tabs(["Definitions", "Relationships", "Key Angles", "Patterns", "Applications"])
473
+
474
+ with tab1:
475
+ st.markdown("""
476
+ **Primary Functions:**
477
+ - **Sine (sin)**: The y-coordinate of a point on the unit circle
478
+ - **Cosine (cos)**: The x-coordinate of a point on the unit circle
479
+ - **Tangent (tan)**: The ratio sin/cos, representing the slope of the radius line
480
+
481
+ **Reciprocal Functions:**
482
+ - **Cosecant (csc)**: 1/sin, reciprocal of sine
483
+ - **Secant (sec)**: 1/cos, reciprocal of cosine
484
+ - **Cotangent (cot)**: 1/tan or cos/sin, reciprocal of tangent
485
+ """)
486
+
487
+ with tab2:
488
+ st.markdown("""
489
+ **Fundamental Identity:**
490
+ - sin²(θ) + cos²(θ) = 1
491
+
492
+ **Quotient Identities:**
493
+ - tan(θ) = sin(θ)/cos(θ)
494
+ - cot(θ) = cos(θ)/sin(θ)
495
+
496
+ **Reciprocal Identities:**
497
+ - csc(θ) = 1/sin(θ)
498
+ - sec(θ) = 1/cos(θ)
499
+ - cot(θ) = 1/tan(θ)
500
+ """)
501
+
502
+ with tab3:
503
+ key_angles_df = pd.DataFrame({
504
+ 'Angle': ['0°', '30°', '45°', '60°', '90°', '120°', '135°', '150°', '180°', '270°', '360°'],
505
+ 'sin': ['0', '1/2', '√2/2', '√3/2', '1', '√3/2', '√2/2', '1/2', '0', '-1', '0'],
506
+ 'cos': ['1', '√3/2', '√2/2', '1/2', '0', '-1/2', '-√2/2', '-√3/2', '-1', '0', '1'],
507
+ 'tan': ['0', '√3/3', '1', '√3', 'undefined', '-√3', '-1', '-√3/3', '0', 'undefined', '0']
508
+ })
509
+ st.dataframe(key_angles_df, use_container_width=True)
510
+
511
+ # Add radians conversion table
512
+ st.subheader("Radian Equivalents")
513
+ radian_df = pd.DataFrame({
514
+ 'Degrees': ['0°', '30°', '45°', '60°', '90°', '180°', '270°', '360°'],
515
+ 'Radians': ['0', 'π/6', 'π/4', 'π/3', 'π/2', 'π', '3π/2', '2π'],
516
+ 'Decimal': ['0', '0.524', '0.785', '1.047', '1.571', '3.142', '4.712', '6.283']
517
+ })
518
+ st.dataframe(radian_df, use_container_width=True)
519
+
520
+ with tab4:
521
+ st.markdown("""
522
+ **Sign Patterns by Quadrant:**
523
+ - **Quadrant I (0° to 90°)**: All functions positive
524
+ - **Quadrant II (90° to 180°)**: Only sine positive
525
+ - **Quadrant III (180° to 270°)**: Only tangent positive
526
+ - **Quadrant IV (270° to 360°)**: Only cosine positive
527
+
528
+ **Remember**: "All Students Take Calculus" (All, Sin, Tan, Cos)
529
+ """)
530
+
531
+ with tab5:
532
+ st.markdown("""
533
+ **Real-world Applications:**
534
+ - **Physics**: Wave motion, oscillations, circular motion
535
+ - **Engineering**: Signal processing, electrical circuits
536
+ - **Navigation**: GPS systems, celestial navigation
537
+ - **Computer Graphics**: Rotations, animations
538
+ - **Music**: Sound waves, harmonics
539
+ - **Architecture**: Designing arches and domes
540
+ """)
541
 
542
+ if __name__ == "__main__":
543
+ main()