NavyDevilDoc commited on
Commit
9baa874
·
verified ·
1 Parent(s): 304dc75

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +799 -37
src/streamlit_app.py CHANGED
@@ -1,40 +1,802 @@
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 plotly.graph_objects as go
4
+ import plotly.express as px
5
  import pandas as pd
6
+ from math import sin, cos, tan, asin, acos, atan2, sqrt, degrees, radians, pi
7
+ import time
8
+
9
+ def calculate_triangle_area(a, b, c):
10
+ """Calculate triangle area using Heron's formula"""
11
+ s = (a + b + c) / 2 # semi-perimeter
12
+ area = sqrt(s * (s - a) * (s - b) * (s - c))
13
+ return area
14
+
15
+ def draw_triangle(side_a, side_b, side_c, angle_A, angle_B, angle_C, title="Triangle"):
16
+ """Draw a triangle with labeled sides and angles using Plotly"""
17
+
18
+ # Place vertices (C at origin, B on x-axis)
19
+ C = np.array([0, 0])
20
+ B = np.array([side_a, 0])
21
+ A = np.array([side_b * cos(radians(angle_C)), side_b * sin(radians(angle_C))])
22
+
23
+ # Create triangle coordinates for plotting
24
+ triangle_x = [A[0], B[0], C[0], A[0]] # Close the triangle
25
+ triangle_y = [A[1], B[1], C[1], A[1]]
26
+
27
+ # Create the figure
28
+ fig = go.Figure()
29
+
30
+ # Add triangle fill
31
+ fig.add_trace(go.Scatter(
32
+ x=triangle_x,
33
+ y=triangle_y,
34
+ fill='toself',
35
+ fillcolor='rgba(173, 216, 230, 0.3)',
36
+ line=dict(color='blue', width=3),
37
+ mode='lines',
38
+ name='Triangle',
39
+ showlegend=False
40
+ ))
41
+
42
+ # Add vertices
43
+ fig.add_trace(go.Scatter(
44
+ x=[A[0]], y=[A[1]],
45
+ mode='markers+text',
46
+ marker=dict(color='red', size=12),
47
+ text=['A'],
48
+ textposition='top center',
49
+ textfont=dict(size=16, color='black'),
50
+ name='Vertex A',
51
+ showlegend=True
52
+ ))
53
+
54
+ fig.add_trace(go.Scatter(
55
+ x=[B[0]], y=[B[1]],
56
+ mode='markers+text',
57
+ marker=dict(color='green', size=12),
58
+ text=['B'],
59
+ textposition='bottom center',
60
+ textfont=dict(size=16, color='black'),
61
+ name='Vertex B',
62
+ showlegend=True
63
+ ))
64
+
65
+ fig.add_trace(go.Scatter(
66
+ x=[C[0]], y=[C[1]],
67
+ mode='markers+text',
68
+ marker=dict(color='blue', size=12),
69
+ text=['C'],
70
+ textposition='middle left',
71
+ textfont=dict(size=16, color='black'),
72
+ name='Vertex C',
73
+ showlegend=True
74
+ ))
75
+
76
+ # Calculate midpoints for side labels
77
+ mid_AB = (A + B) / 2
78
+ mid_BC = (B + C) / 2
79
+ mid_CA = (C + A) / 2
80
+
81
+ # Add side labels
82
+ fig.add_trace(go.Scatter(
83
+ x=[mid_AB[0]], y=[mid_AB[1]],
84
+ mode='text',
85
+ text=[f'c = {side_c:.2f}'],
86
+ textfont=dict(size=12, color='black'),
87
+ textposition='top center',
88
+ showlegend=False
89
+ ))
90
+
91
+ fig.add_trace(go.Scatter(
92
+ x=[mid_BC[0]], y=[mid_BC[1]],
93
+ mode='text',
94
+ text=[f'a = {side_a:.2f}'],
95
+ textfont=dict(size=12, color='black'),
96
+ textposition='bottom center',
97
+ showlegend=False
98
+ ))
99
+
100
+ fig.add_trace(go.Scatter(
101
+ x=[mid_CA[0]], y=[mid_CA[1]],
102
+ mode='text',
103
+ text=[f'b = {side_b:.2f}'],
104
+ textfont=dict(size=12, color='black'),
105
+ textposition='middle left',
106
+ showlegend=False
107
+ ))
108
+
109
+ # Add angle labels
110
+ fig.add_trace(go.Scatter(
111
+ x=[A[0] - 0.3], y=[A[1] - 0.3],
112
+ mode='text',
113
+ text=[f'∠A = {angle_A:.1f}°'],
114
+ textfont=dict(size=10, color='black'),
115
+ showlegend=False
116
+ ))
117
+
118
+ fig.add_trace(go.Scatter(
119
+ x=[B[0] + 0.2], y=[B[1] + 0.2],
120
+ mode='text',
121
+ text=[f'∠B = {angle_B:.1f}°'],
122
+ textfont=dict(size=10, color='black'),
123
+ showlegend=False
124
+ ))
125
+
126
+ fig.add_trace(go.Scatter(
127
+ x=[C[0] + 0.2], y=[C[1] + 0.2],
128
+ mode='text',
129
+ text=[f'∠C = {angle_C:.1f}°'],
130
+ textfont=dict(size=10, color='black'),
131
+ showlegend=False
132
+ ))
133
+
134
+ # Set layout
135
+ all_x = [A[0], B[0], C[0]]
136
+ all_y = [A[1], B[1], C[1]]
137
+ margin = 1.0
138
+
139
+ fig.update_layout(
140
+ title=dict(text=title, font=dict(size=18, color='black'), x=0.5),
141
+ xaxis=dict(
142
+ range=[min(all_x) - margin, max(all_x) + margin],
143
+ showgrid=True,
144
+ gridcolor='lightgray',
145
+ zeroline=True,
146
+ zerolinecolor='gray'
147
+ ),
148
+ yaxis=dict(
149
+ range=[min(all_y) - margin, max(all_y) + margin],
150
+ showgrid=True,
151
+ gridcolor='lightgray',
152
+ zeroline=True,
153
+ zerolinecolor='gray',
154
+ scaleanchor="x",
155
+ scaleratio=1
156
+ ),
157
+ plot_bgcolor='white',
158
+ paper_bgcolor='white',
159
+ width=700,
160
+ height=600,
161
+ margin=dict(l=50, r=50, t=80, b=50)
162
+ )
163
+
164
+ return fig
165
+
166
+ def draw_vectors(a_x, a_y, b_x, b_y, angle_between):
167
+ """Draw two vectors and show the angle between them using Plotly"""
168
+
169
+ fig = go.Figure()
170
+
171
+ # Draw vectors from origin
172
+ fig.add_trace(go.Scatter(
173
+ x=[0, a_x], y=[0, a_y],
174
+ mode='lines+markers',
175
+ line=dict(color='red', width=4),
176
+ marker=dict(symbol='arrow', angle=90, size=15, color='red'),
177
+ name=f'Vector A ({a_x:.2f}, {a_y:.2f})'
178
+ ))
179
+
180
+ fig.add_trace(go.Scatter(
181
+ x=[0, b_x], y=[0, b_y],
182
+ mode='lines+markers',
183
+ line=dict(color='blue', width=4),
184
+ marker=dict(symbol='arrow', angle=90, size=15, color='blue'),
185
+ name=f'Vector B ({b_x:.2f}, {b_y:.2f})'
186
+ ))
187
+
188
+ # Create angle arc
189
+ magnitude_a = sqrt(a_x**2 + a_y**2)
190
+ magnitude_b = sqrt(b_x**2 + b_y**2)
191
+
192
+ angle_a = atan2(a_y, a_x)
193
+ angle_b = atan2(b_y, b_x)
194
+
195
+ if angle_a < angle_b:
196
+ arc_angles = np.linspace(angle_a, angle_b, 50)
197
+ else:
198
+ arc_angles = np.linspace(angle_b, angle_a, 50)
199
+
200
+ arc_radius = min(magnitude_a, magnitude_b) * 0.3
201
+ arc_x = arc_radius * np.cos(arc_angles)
202
+ arc_y = arc_radius * np.sin(arc_angles)
203
+
204
+ # Add angle arc
205
+ fig.add_trace(go.Scatter(
206
+ x=arc_x, y=arc_y,
207
+ mode='lines',
208
+ line=dict(color='green', width=4),
209
+ showlegend=False
210
+ ))
211
+
212
+ # Add angle label
213
+ mid_angle = (angle_a + angle_b) / 2
214
+ label_x = (arc_radius + 0.5) * cos(mid_angle)
215
+ label_y = (arc_radius + 0.5) * sin(mid_angle)
216
+
217
+ fig.add_trace(go.Scatter(
218
+ x=[label_x], y=[label_y],
219
+ mode='text',
220
+ text=[f'{angle_between:.1f}°'],
221
+ textfont=dict(size=14, color='green'),
222
+ showlegend=False
223
+ ))
224
+
225
+ # Add vector endpoint labels
226
+ fig.add_trace(go.Scatter(
227
+ x=[a_x], y=[a_y],
228
+ mode='text',
229
+ text=['A'],
230
+ textfont=dict(size=16, color='red'),
231
+ textposition='top right',
232
+ showlegend=False
233
+ ))
234
+
235
+ fig.add_trace(go.Scatter(
236
+ x=[b_x], y=[b_y],
237
+ mode='text',
238
+ text=['B'],
239
+ textfont=dict(size=16, color='blue'),
240
+ textposition='top right',
241
+ showlegend=False
242
+ ))
243
+
244
+ # Set layout
245
+ max_range = max(magnitude_a, magnitude_b) * 1.2
246
+
247
+ fig.update_layout(
248
+ title=dict(text=f'Vectors with {angle_between:.1f}° angle between them',
249
+ font=dict(size=18, color='black'), x=0.5),
250
+ xaxis=dict(
251
+ range=[-max_range * 0.1, max_range],
252
+ showgrid=True,
253
+ gridcolor='lightgray',
254
+ zeroline=True,
255
+ zerolinecolor='black',
256
+ zerolinewidth=2
257
+ ),
258
+ yaxis=dict(
259
+ range=[-max_range * 0.1, max_range],
260
+ showgrid=True,
261
+ gridcolor='lightgray',
262
+ zeroline=True,
263
+ zerolinecolor='black',
264
+ zerolinewidth=2,
265
+ scaleanchor="x",
266
+ scaleratio=1
267
+ ),
268
+ plot_bgcolor='white',
269
+ paper_bgcolor='white',
270
+ width=700,
271
+ height=600,
272
+ margin=dict(l=50, r=50, t=80, b=50)
273
+ )
274
+
275
+ return fig
276
+
277
+ def law_of_sines_calculator():
278
+ st.header("📏 Law of Sines Calculator")
279
+ st.markdown("**Formula:** a/sin(A) = b/sin(B) = c/sin(C)")
280
+ st.markdown("**Equivalent:** sin(A)/a = sin(B)/b = sin(C)/c")
281
+
282
+ # Add formula explanation
283
+ with st.expander("📚 Understanding the Law of Sines"):
284
+ st.markdown("""
285
+ The Law of Sines can be written in two equivalent forms:
286
+
287
+ **Form 1:** a/sin(A) = b/sin(B) = c/sin(C)
288
+
289
+ **Form 2:** sin(A)/a = sin(B)/b = sin(C)/c
290
+
291
+ Both forms are mathematically identical and lead to the same calculations:
292
+ - To find a side: side = (other_side × sin(opposite_angle)) / sin(known_angle)
293
+ - To find an angle: sin(angle) = (opposite_side × sin(known_angle)) / known_side
294
+
295
+ **Use the Law of Sines when you have:**
296
+ - **AAS (Angle-Angle-Side)**: Two angles and one side
297
+ - **ASA (Angle-Side-Angle)**: Two angles and the included side
298
+ - **SSA (Side-Side-Angle)**: Two sides and one angle (ambiguous case)
299
+ """)
300
+
301
+ # Add a visual demonstration of the relationship
302
+ with st.expander("🔍 See the calculation steps"):
303
+ st.markdown("""
304
+ **Example calculation process:**
305
+
306
+ If we know: angle A, angle B, and side a
307
+
308
+ **Step 1:** Find angle C
309
+ C = 180° - A - B
310
+
311
+ **Step 2:** Use Law of Sines to find side b
312
+ From: a/sin(A) = b/sin(B)
313
+
314
+ Rearrange: b = (a × sin(B)) / sin(A)
315
+
316
+ **Step 3:** Find side c
317
+ From: a/sin(A) = c/sin(C)
318
+
319
+ Rearrange: c = (a × sin(C)) / sin(A)
320
+ """)
321
+
322
+ col1, col2 = st.columns([1, 1])
323
+
324
+ with col1:
325
+ st.subheader("📝 Input Triangle Data")
326
+
327
+ known_case = st.selectbox(
328
+ "What do you know about the triangle?",
329
+ ["Two angles and one side (AAS/ASA)", "Two sides and one angle (SSA)"]
330
+ )
331
+
332
+ if known_case == "Two angles and one side (AAS/ASA)":
333
+ st.write("**Enter two angles and one side:**")
334
+ angle_A = st.number_input("Angle A (degrees):", min_value=0.1, max_value=179.9, value=60.0, key="sines_angleA")
335
+ angle_B = st.number_input("Angle B (degrees):", min_value=0.1, max_value=179.9, value=50.0, key="sines_angleB")
336
+ side_a = st.number_input("Side a (opposite to angle A):", min_value=0.1, value=10.0, key="sines_sidea")
337
+
338
+ # Calculate third angle
339
+ angle_C = 180 - angle_A - angle_B
340
+
341
+ if angle_C <= 0:
342
+ st.error("❌ Invalid triangle! Sum of angles must be less than 180°")
343
+ return
344
+
345
+ # Calculate other sides using Law of Sines
346
+ side_b = side_a * sin(radians(angle_B)) / sin(radians(angle_A))
347
+ side_c = side_a * sin(radians(angle_C)) / sin(radians(angle_A))
348
+
349
+ # Validate triangle
350
+ if side_b <= 0 or side_c <= 0:
351
+ st.error("❌ Invalid triangle! Check your inputs.")
352
+ return
353
+
354
+ else: # SSA case
355
+ st.write("**Enter two sides and one angle (ambiguous case):**")
356
+ side_a = st.number_input("Side a:", min_value=0.1, value=10.0, key="sines_ssa_sidea")
357
+ side_b = st.number_input("Side b:", min_value=0.1, value=8.0, key="sines_ssa_sideb")
358
+ angle_A = st.number_input("Angle A (opposite to side a):", min_value=0.1, max_value=179.9, value=60.0, key="sines_ssa_angleA")
359
+
360
+ # Check for validity
361
+ sin_B = side_b * sin(radians(angle_A)) / side_a
362
+
363
+ if sin_B > 1:
364
+ st.error("❌ No triangle possible with these measurements!")
365
+ return
366
+ elif abs(sin_B - 1) < 1e-10: # sin_B == 1 (within floating point precision)
367
+ angle_B = 90.0
368
+ angle_C = 90 - angle_A
369
+ side_c = side_a * cos(radians(angle_A))
370
+ st.info("✅ Right triangle solution found!")
371
+ else:
372
+ angle_B = degrees(asin(sin_B))
373
+ angle_C = 180 - angle_A - angle_B
374
+ side_c = side_a * sin(radians(angle_C)) / sin(radians(angle_A))
375
+
376
+ # Check for ambiguous case
377
+ if side_b < side_a and angle_A < 90:
378
+ angle_B2 = 180 - angle_B
379
+ angle_C2 = 180 - angle_A - angle_B2
380
+ if angle_C2 > 0:
381
+ side_c2 = side_a * sin(radians(angle_C2)) / sin(radians(angle_A))
382
+ st.warning(f"⚠️ Ambiguous case! Two triangles possible:")
383
+ st.write(f"**Triangle 1:** B = {angle_B:.2f}°, C = {angle_C:.2f}°, c = {side_c:.3f}")
384
+ st.write(f"**Triangle 2:** B = {angle_B2:.2f}°, C = {angle_C2:.2f}°, c = {side_c2:.3f}")
385
+
386
+ # Let user choose which triangle to display
387
+ triangle_choice = st.radio("Choose triangle to visualize:", ["Triangle 1", "Triangle 2"])
388
+ if triangle_choice == "Triangle 2":
389
+ angle_B, angle_C, side_c = angle_B2, angle_C2, side_c2
390
+
391
+ with col2:
392
+ st.subheader("📊 Results")
393
+
394
+ # Display results in a nice table
395
+ results_df = pd.DataFrame({
396
+ 'Element': ['Side a', 'Side b', 'Side c', 'Angle A', 'Angle B', 'Angle C'],
397
+ 'Value': [f'{side_a:.3f}', f'{side_b:.3f}', f'{side_c:.3f}',
398
+ f'{angle_A:.2f}°', f'{angle_B:.2f}°', f'{angle_C:.2f}°'],
399
+ 'Type': ['Given' if known_case == "Two angles and one side (AAS/ASA)" else 'Given',
400
+ 'Calculated' if known_case == "Two angles and one side (AAS/ASA)" else 'Given',
401
+ 'Calculated',
402
+ 'Given' if known_case == "Two angles and one side (AAS/ASA)" else 'Given',
403
+ 'Given' if known_case == "Two angles and one side (AAS/ASA)" else 'Calculated',
404
+ 'Calculated']
405
+ })
406
+ st.dataframe(results_df, use_container_width=True)
407
+
408
+ # Calculate area using formula: Area = (1/2)ab*sin(C)
409
+ area = 0.5 * side_a * side_b * sin(radians(angle_C))
410
+ perimeter = side_a + side_b + side_c
411
+
412
+ col_metric1, col_metric2 = st.columns(2)
413
+ with col_metric1:
414
+ st.metric("Area", f"{area:.3f} sq units")
415
+ with col_metric2:
416
+ st.metric("Perimeter", f"{perimeter:.3f} units")
417
+
418
+ # Draw the triangle
419
+ fig = draw_triangle(side_a, side_b, side_c, angle_A, angle_B, angle_C, "Law of Sines Triangle")
420
+ st.plotly_chart(fig, use_container_width=True)
421
+
422
+ def law_of_cosines_calculator():
423
+ st.header("📐 Law of Cosines Calculator")
424
+ st.markdown("**Formula:** c² = a² + b² - 2ab⋅cos(C)")
425
+
426
+ # Add formula explanation
427
+ with st.expander("📚 When to use Law of Cosines"):
428
+ st.markdown("""
429
+ Use the Law of Cosines when you have:
430
+ - **SSS (Side-Side-Side)**: All three sides known
431
+ - **SAS (Side-Angle-Side)**: Two sides and the included angle
432
+
433
+ This is especially useful when the Law of Sines doesn't apply directly.
434
+ """)
435
+
436
+ col1, col2 = st.columns([1, 1])
437
+
438
+ with col1:
439
+ st.subheader("📝 Input Triangle Data")
440
+
441
+ known_case = st.selectbox(
442
+ "What do you know?",
443
+ ["Three sides (SSS)", "Two sides and included angle (SAS)"]
444
+ )
445
+
446
+ if known_case == "Three sides (SSS)":
447
+ st.write("**Enter all three sides:**")
448
+ side_a = st.number_input("Side a:", min_value=0.1, value=5.0, key="cosines_sss_sidea")
449
+ side_b = st.number_input("Side b:", min_value=0.1, value=7.0, key="cosines_sss_sideb")
450
+ side_c = st.number_input("Side c:", min_value=0.1, value=9.0, key="cosines_sss_sidec")
451
+
452
+ # Check triangle inequality
453
+ if not (side_a + side_b > side_c and side_b + side_c > side_a and side_a + side_c > side_b):
454
+ st.error("❌ Invalid triangle! Triangle inequality not satisfied.")
455
+ st.write("**Triangle Inequality Rules:**")
456
+ st.write(f"• a + b > c: {side_a:.2f} + {side_b:.2f} = {side_a + side_b:.2f} {'✓' if side_a + side_b > side_c else '✗'} {side_c:.2f}")
457
+ st.write(f"• b + c > a: {side_b:.2f} + {side_c:.2f} = {side_b + side_c:.2f} {'✓' if side_b + side_c > side_a else '✗'} {side_a:.2f}")
458
+ st.write(f"• a + c > b: {side_a:.2f} + {side_c:.2f} = {side_a + side_c:.2f} {'✓' if side_a + side_c > side_b else '✗'} {side_b:.2f}")
459
+ return
460
+
461
+ # Calculate angles using Law of Cosines
462
+ try:
463
+ angle_A = degrees(acos((side_b**2 + side_c**2 - side_a**2) / (2 * side_b * side_c)))
464
+ angle_B = degrees(acos((side_a**2 + side_c**2 - side_b**2) / (2 * side_a * side_c)))
465
+ angle_C = 180 - angle_A - angle_B
466
+ except ValueError:
467
+ st.error("❌ Error calculating angles. Check your side lengths.")
468
+ return
469
+
470
+ else: # SAS case
471
+ st.write("**Enter two sides and the included angle:**")
472
+ side_a = st.number_input("Side a:", min_value=0.1, value=5.0, key="cosines_sas_sidea")
473
+ side_b = st.number_input("Side b:", min_value=0.1, value=7.0, key="cosines_sas_sideb")
474
+ angle_C = st.number_input("Angle C (between sides a and b):", min_value=0.1, max_value=179.9, value=60.0, key="cosines_sas_angleC")
475
+
476
+ # Calculate third side using Law of Cosines
477
+ side_c = sqrt(side_a**2 + side_b**2 - 2 * side_a * side_b * cos(radians(angle_C)))
478
+
479
+ # Calculate other angles using Law of Cosines
480
+ try:
481
+ angle_A = degrees(acos((side_b**2 + side_c**2 - side_a**2) / (2 * side_b * side_c)))
482
+ angle_B = 180 - angle_A - angle_C
483
+ except ValueError:
484
+ st.error("❌ Error calculating angles. Check your inputs.")
485
+ return
486
+
487
+ with col2:
488
+ st.subheader("📊 Results")
489
+
490
+ # Display results
491
+ results_df = pd.DataFrame({
492
+ 'Element': ['Side a', 'Side b', 'Side c', 'Angle A', 'Angle B', 'Angle C'],
493
+ 'Value': [f'{side_a:.3f}', f'{side_b:.3f}', f'{side_c:.3f}',
494
+ f'{angle_A:.2f}°', f'{angle_B:.2f}°', f'{angle_C:.2f}°'],
495
+ 'Type': ['Given', 'Given',
496
+ 'Given' if known_case == "Three sides (SSS)" else 'Calculated',
497
+ 'Calculated', 'Calculated',
498
+ 'Calculated' if known_case == "Three sides (SSS)" else 'Given']
499
+ })
500
+ st.dataframe(results_df, use_container_width=True)
501
+
502
+ # Calculate area and perimeter
503
+ area = calculate_triangle_area(side_a, side_b, side_c)
504
+ perimeter = side_a + side_b + side_c
505
+
506
+ col_metric1, col_metric2 = st.columns(2)
507
+ with col_metric1:
508
+ st.metric("Area", f"{area:.3f} sq units")
509
+ with col_metric2:
510
+ st.metric("Perimeter", f"{perimeter:.3f} units")
511
+
512
+ # Determine triangle type
513
+ if abs(angle_A - 90) < 0.01 or abs(angle_B - 90) < 0.01 or abs(angle_C - 90) < 0.01:
514
+ triangle_type = "Right Triangle"
515
+ elif angle_A > 90 or angle_B > 90 or angle_C > 90:
516
+ triangle_type = "Obtuse Triangle"
517
+ else:
518
+ triangle_type = "Acute Triangle"
519
+
520
+ st.info(f"**Triangle Type:** {triangle_type}")
521
+
522
+ # Draw the triangle
523
+ fig = draw_triangle(side_a, side_b, side_c, angle_A, angle_B, angle_C, "Law of Cosines Triangle")
524
+ st.plotly_chart(fig, use_container_width=True)
525
+
526
+ def vector_angle_calculator():
527
+ st.header("🔄 Vector Angle Calculator")
528
+ st.markdown("**Formula:** cos(θ) = (A⋅B) / (|A|⋅|B|)")
529
+
530
+ # Add explanation
531
+ with st.expander("📚 Understanding Vector Angles"):
532
+ st.markdown("""
533
+ **Dot Product Formula:** A⋅B = |A||B|cos(θ)
534
+
535
+ **Applications:**
536
+ - Physics: Work = Force⋅Displacement⋅cos(θ)
537
+ - Computer Graphics: Lighting calculations
538
+ - Engineering: Force analysis
539
+ - Navigation: Direction calculations
540
+ """)
541
+
542
+ col1, col2 = st.columns([1, 1])
543
+
544
+ with col1:
545
+ st.subheader("📝 Enter Vectors")
546
+
547
+ # Vector input methods
548
+ input_method = st.radio("Input method:", ["Component form", "Magnitude & Direction"])
549
+
550
+ if input_method == "Component form":
551
+ st.write("**Vector A:**")
552
+ a_x = st.number_input("A_x component:", value=3.0, key="vector_ax")
553
+ a_y = st.number_input("A_y component:", value=4.0, key="vector_ay")
554
+
555
+ st.write("**Vector B:**")
556
+ b_x = st.number_input("B_x component:", value=1.0, key="vector_bx")
557
+ b_y = st.number_input("B_y component:", value=2.0, key="vector_by")
558
+
559
+ else:
560
+ st.write("**Vector A:**")
561
+ mag_a = st.number_input("Magnitude of A:", min_value=0.1, value=5.0, key="vector_mag_a")
562
+ dir_a = st.number_input("Direction of A (degrees):", value=53.0, key="vector_dir_a")
563
+
564
+ st.write("**Vector B:**")
565
+ mag_b = st.number_input("Magnitude of B:", min_value=0.1, value=2.24, key="vector_mag_b")
566
+ dir_b = st.number_input("Direction of B (degrees):", value=63.4, key="vector_dir_b")
567
+
568
+ # Convert to components
569
+ a_x = mag_a * cos(radians(dir_a))
570
+ a_y = mag_a * sin(radians(dir_a))
571
+ b_x = mag_b * cos(radians(dir_b))
572
+ b_y = mag_b * sin(radians(dir_b))
573
+
574
+ # Calculate vector properties
575
+ dot_product = a_x * b_x + a_y * b_y
576
+ magnitude_a = sqrt(a_x**2 + a_y**2)
577
+ magnitude_b = sqrt(b_x**2 + b_y**2)
578
+
579
+ if magnitude_a == 0 or magnitude_b == 0:
580
+ st.error("❌ Zero vector detected! Cannot calculate angle.")
581
+ return
582
+
583
+ cos_theta = dot_product / (magnitude_a * magnitude_b)
584
+ # Clamp to [-1, 1] to avoid floating point errors
585
+ cos_theta = max(-1, min(1, cos_theta))
586
+ angle_degrees = degrees(acos(cos_theta))
587
+
588
+ # Calculate cross product for 2D (gives scalar)
589
+ cross_product = a_x * b_y - a_y * b_x
590
+
591
+ with col2:
592
+ st.subheader("📊 Results")
593
+
594
+ # Vector information table
595
+ vector_info = pd.DataFrame({
596
+ 'Property': ['A_x', 'A_y', 'B_x', 'B_y', '|A|', '|B|'],
597
+ 'Value': [f'{a_x:.3f}', f'{a_y:.3f}', f'{b_x:.3f}', f'{b_y:.3f}',
598
+ f'{magnitude_a:.3f}', f'{magnitude_b:.3f}']
599
+ })
600
+ st.dataframe(vector_info, use_container_width=True)
601
+
602
+ # Key results
603
+ col_metric1, col_metric2 = st.columns(2)
604
+ with col_metric1:
605
+ st.metric("Dot Product (A⋅B)", f"{dot_product:.3f}")
606
+ st.metric("Angle", f"{angle_degrees:.2f}°")
607
+ with col_metric2:
608
+ st.metric("Cross Product (2D)", f"{cross_product:.3f}")
609
+ st.metric("cos(θ)", f"{cos_theta:.4f}")
610
+
611
+ # Vector relationship
612
+ if abs(dot_product) < 1e-10:
613
+ st.info("🔄 **Vectors are perpendicular (orthogonal)**")
614
+ elif cos_theta > 0:
615
+ st.info("📐 **Vectors point in similar directions (acute angle)**")
616
+ else:
617
+ st.info("📐 **Vectors point in opposite directions (obtuse angle)**")
618
+
619
+ # Draw vectors
620
+ fig = draw_vectors(a_x, a_y, b_x, b_y, angle_degrees)
621
+ st.plotly_chart(fig, use_container_width=True)
622
+
623
+ # Additional calculations
624
+ st.subheader("🧮 Additional Calculations")
625
+
626
+ # Unit vectors
627
+ unit_a_x = a_x / magnitude_a if magnitude_a != 0 else 0
628
+ unit_a_y = a_y / magnitude_a if magnitude_a != 0 else 0
629
+ unit_b_x = b_x / magnitude_b if magnitude_b != 0 else 0
630
+ unit_b_y = b_y / magnitude_b if magnitude_b != 0 else 0
631
+
632
+ st.write(f"**Unit vector A:** ({unit_a_x:.3f}, {unit_a_y:.3f})")
633
+ st.write(f"**Unit vector B:** ({unit_b_x:.3f}, {unit_b_y:.3f})")
634
+
635
+ # Vector sum and difference
636
+ sum_x, sum_y = a_x + b_x, a_y + b_y
637
+ diff_x, diff_y = a_x - b_x, a_y - b_y
638
+
639
+ st.write(f"**A + B:** ({sum_x:.3f}, {sum_y:.3f})")
640
+ st.write(f"**A - B:** ({diff_x:.3f}, {diff_y:.3f})")
641
+
642
+ def triangle_visualizer():
643
+ st.header("🎨 Interactive Triangle Visualizer")
644
+ st.markdown("Explore how changing triangle properties affects its shape and calculations!")
645
+
646
+ col1, col2 = st.columns([1, 1])
647
+
648
+ with col1:
649
+ st.subheader("🎛️ Triangle Controls")
650
+
651
+ # Interactive sliders for triangle properties
652
+ side_a = st.slider("Side a:", min_value=1.0, max_value=15.0, value=8.0, step=0.1)
653
+ side_b = st.slider("Side b:", min_value=1.0, max_value=15.0, value=6.0, step=0.1)
654
+ angle_C = st.slider("Angle C (degrees):", min_value=10.0, max_value=170.0, value=60.0, step=1.0)
655
+
656
+ # Calculate using Law of Cosines
657
+ side_c = sqrt(side_a**2 + side_b**2 - 2 * side_a * side_b * cos(radians(angle_C)))
658
+
659
+ # Calculate other angles
660
+ try:
661
+ angle_A = degrees(acos((side_b**2 + side_c**2 - side_a**2) / (2 * side_b * side_c)))
662
+ angle_B = 180 - angle_A - angle_C
663
+ except ValueError:
664
+ st.error("Invalid triangle configuration!")
665
+ return
666
+
667
+ # Real-time calculations
668
+ area = 0.5 * side_a * side_b * sin(radians(angle_C))
669
+ perimeter = side_a + side_b + side_c
670
+
671
+ # Display live results
672
+ st.subheader("📊 Live Results")
673
+ col_live1, col_live2 = st.columns(2)
674
+ with col_live1:
675
+ st.metric("Side c", f"{side_c:.2f}")
676
+ st.metric("Angle A", f"{angle_A:.1f}°")
677
+ st.metric("Area", f"{area:.2f}")
678
+ with col_live2:
679
+ st.metric("Angle B", f"{angle_B:.1f}°")
680
+ st.metric("Perimeter", f"{perimeter:.2f}")
681
+
682
+ # Triangle type
683
+ if abs(angle_A - 90) < 0.1 or abs(angle_B - 90) < 0.1 or abs(angle_C - 90) < 0.1:
684
+ triangle_type = "Right"
685
+ elif angle_A > 90 or angle_B > 90 or angle_C > 90:
686
+ triangle_type = "Obtuse"
687
+ else:
688
+ triangle_type = "Acute"
689
+
690
+ st.info(f"**Triangle Type:** {triangle_type}")
691
+
692
+ with col2:
693
+ st.subheader("📐 Interactive Triangle")
694
+
695
+ # Draw the interactive triangle
696
+ fig = draw_triangle(side_a, side_b, side_c, angle_A, angle_B, angle_C, "Interactive Triangle")
697
+ st.plotly_chart(fig, use_container_width=True)
698
+
699
+ # Show which laws apply
700
+ st.subheader("📚 Applicable Laws")
701
+ st.write("**Given:** Two sides (a, b) and included angle (C)")
702
+ st.write("**Use:** Law of Cosines to find side c")
703
+ st.write("**Then:** Law of Cosines or Sines to find remaining angles")
704
+
705
+ # Show the calculations
706
+ with st.expander("🔍 See the calculations"):
707
+ st.write("**Step 1: Find side c using Law of Cosines**")
708
+ st.latex(r"c^2 = a^2 + b^2 - 2ab \cos(C)")
709
+ st.write(f"c² = {side_a}² + {side_b}² - 2({side_a})({side_b})cos({angle_C}°)")
710
+ st.write(f"c² = {side_a**2:.2f} + {side_b**2:.2f} - {2*side_a*side_b:.2f} × {cos(radians(angle_C)):.4f}")
711
+ st.write(f"c = {side_c:.3f}")
712
+
713
+ st.write("**Step 2: Find angle A using Law of Cosines**")
714
+ st.latex(r"\cos(A) = \frac{b^2 + c^2 - a^2}{2bc}")
715
+ st.write(f"cos(A) = ({side_b}² + {side_c:.2f}² - {side_a}²) / (2 × {side_b} × {side_c:.2f})")
716
+ st.write(f"A = {angle_A:.2f}°")
717
+
718
+ st.write("**Step 3: Find angle B**")
719
+ st.write(f"B = 180° - A - C = 180° - {angle_A:.2f}° - {angle_C}° = {angle_B:.2f}°")
720
+
721
+ def main():
722
+ st.set_page_config(page_title="Triangle Solver & Vector Calculator", page_icon="📐", layout="wide")
723
+
724
+ st.title("📐 Triangle Solver & Vector Calculator")
725
+ st.markdown("### Master the Law of Sines, Law of Cosines, and Vector Applications!")
726
+
727
+ # Add educational tips
728
+ tips = [
729
+ "💡 **Law of Sines**: Use when you have angle-side-angle (ASA) or side-angle-angle (SAA)",
730
+ "🎯 **Law of Cosines**: Use when you have side-side-side (SSS) or side-angle-side (SAS)",
731
+ "📊 **Vector Tip**: The angle between vectors uses the dot product formula",
732
+ "🔄 **Remember**: Always check if your triangle is valid (triangle inequality)",
733
+ "⚡ **Practical**: These laws help in navigation, engineering, and physics!"
734
+ ]
735
+
736
+ st.info(np.random.choice(tips))
737
+
738
+ # Sidebar for mode selection
739
+ st.sidebar.header("🎛️ Calculator Mode")
740
+ mode = st.sidebar.radio(
741
+ "Choose what to calculate:",
742
+ ["Law of Sines", "Law of Cosines", "Vector Angle Calculator", "Triangle Visualizer"]
743
+ )
744
+
745
+ # Educational content sidebar
746
+ with st.sidebar.expander("📚 Quick Reference"):
747
+ st.markdown("""
748
+ **Law of Sines:**
749
+ a/sin(A) = b/sin(B) = c/sin(C)
750
+
751
+ **Law of Cosines:**
752
+ c² = a² + b² - 2ab⋅cos(C)
753
+
754
+ **Vector Angle:**
755
+ cos(θ) = (A⋅B)/(|A||B|)
756
+
757
+ **Triangle Inequality:**
758
+ a + b > c, b + c > a, a + c > b
759
+ """)
760
+
761
+ # Main content based on mode
762
+ if mode == "Law of Sines":
763
+ law_of_sines_calculator()
764
+ elif mode == "Law of Cosines":
765
+ law_of_cosines_calculator()
766
+ elif mode == "Vector Angle Calculator":
767
+ vector_angle_calculator()
768
+ else:
769
+ triangle_visualizer()
770
+
771
+ # Footer with applications
772
+ st.markdown("---")
773
+ st.subheader("🌟 Real-World Applications")
774
+
775
+ app_col1, app_col2, app_col3 = st.columns(3)
776
+
777
+ with app_col1:
778
+ st.markdown("""
779
+ **🏗️ Engineering**
780
+ - Structural analysis
781
+ - Force calculations
782
+ - Bridge design
783
+ """)
784
+
785
+ with app_col2:
786
+ st.markdown("""
787
+ **🧭 Navigation**
788
+ - GPS systems
789
+ - Ship navigation
790
+ - Flight paths
791
+ """)
792
+
793
+ with app_col3:
794
+ st.markdown("""
795
+ **🎮 Technology**
796
+ - Computer graphics
797
+ - Game physics
798
+ - Robotics
799
+ """)
800
 
801
+ if __name__ == "__main__":
802
+ main()