NavyDevilDoc commited on
Commit
2374c13
·
verified ·
1 Parent(s): 3059793

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +118 -68
src/streamlit_app.py CHANGED
@@ -3,6 +3,13 @@ import numpy as np
3
  import matplotlib.pyplot as plt
4
  from kinematics_visualizer import Motion1D, Motion2D, KinematicsVisualizer
5
 
 
 
 
 
 
 
 
6
  # Configure Streamlit page
7
  st.set_page_config(
8
  page_title="Physics Tutorial: Kinematics",
@@ -68,6 +75,11 @@ def main():
68
  ["1D Motion", "2D Projectile Motion", "Compare Motions"]
69
  )
70
 
 
 
 
 
 
71
  if tutorial_type == "1D Motion":
72
  show_1d_motion()
73
  elif tutorial_type == "2D Projectile Motion":
@@ -473,86 +485,124 @@ def show_motion_comparison():
473
 
474
  st.markdown("**Compare up to 3 different projectile motions side by side**")
475
 
 
 
 
 
476
  # Create tabs for different trajectories
477
  tab1, tab2, tab3 = st.tabs(["🚀 Trajectory 1", "🎯 Trajectory 2", "⚽ Trajectory 3"])
478
 
479
  trajectories = []
480
 
 
481
  with tab1:
482
- col1, col2 = st.columns(2)
483
- with col1:
484
- speed1 = st.slider("Speed 1 (m/s)", 5.0, 50.0, 20.0, key="speed1")
485
- angle1 = st.slider("Angle 1 (°)", 0.0, 90.0, 30.0, key="angle1")
486
- with col2:
487
- height1 = st.slider("Height 1 (m)", 0.0, 30.0, 0.0, key="height1")
488
-
489
- motion1 = Motion2D(launch_speed=speed1, launch_angle=angle1, launch_height=height1)
490
- trajectories.append(("Trajectory 1", motion1, 'blue'))
 
491
 
492
  with tab2:
493
- col1, col2 = st.columns(2)
494
- with col1:
495
- speed2 = st.slider("Speed 2 (m/s)", 5.0, 50.0, 25.0, key="speed2")
496
- angle2 = st.slider("Angle 2 (°)", 0.0, 90.0, 45.0, key="angle2")
497
- with col2:
498
- height2 = st.slider("Height 2 (m)", 0.0, 30.0, 0.0, key="height2")
499
-
500
- motion2 = Motion2D(launch_speed=speed2, launch_angle=angle2, launch_height=height2)
501
- trajectories.append(("Trajectory 2", motion2, 'red'))
 
502
 
503
  with tab3:
504
- col1, col2 = st.columns(2)
505
- with col1:
506
- speed3 = st.slider("Speed 3 (m/s)", 5.0, 50.0, 30.0, key="speed3")
507
- angle3 = st.slider("Angle 3 (°)", 0.0, 90.0, 60.0, key="angle3")
508
- with col2:
509
- height3 = st.slider("Height 3 (m)", 0.0, 30.0, 5.0, key="height3")
510
-
511
- motion3 = Motion2D(launch_speed=speed3, launch_angle=angle3, launch_height=height3)
512
- trajectories.append(("Trajectory 3", motion3, 'green'))
513
-
514
- # Create comparison plot
515
- fig, ax = plt.subplots(figsize=(12, 8))
516
-
517
- max_range = 0
518
- for name, motion, color in trajectories:
519
- data = motion.trajectory_data(motion.calculate_flight_time())
520
- ax.plot(data['x'], data['y'], linewidth=3, label=name, color=color)
521
-
522
- # Mark launch and landing points
523
- ax.plot(motion.launch_x, motion.launch_height, 'o', markersize=8, color=color, alpha=0.7)
524
- if len(data['x']) > 0:
525
- ax.plot(data['x'][-1], data['y'][-1], 's', markersize=8, color=color, alpha=0.7)
526
- max_range = max(max_range, data['x'][-1])
527
 
528
- ax.set_xlabel('Horizontal Position (m)', fontsize=12)
529
- ax.set_ylabel('Vertical Position (m)', fontsize=12)
530
- ax.set_title('Trajectory Comparison', fontsize=16, fontweight='bold')
531
- ax.grid(True, alpha=0.3)
532
- ax.legend(fontsize=12)
533
- ax.set_ylim(bottom=0)
534
- ax.set_xlim(0, max_range * 1.1)
535
-
536
- st.pyplot(fig)
537
- plt.close()
538
-
539
- # Comparison table
540
- st.markdown("### 📋 Trajectory Comparison Table")
541
-
542
- comparison_data = []
543
- for name, motion, _ in trajectories:
544
- info = motion.get_launch_info()
545
- comparison_data.append({
546
- "Trajectory": name,
547
- "Speed (m/s)": f"{info['launch_speed']:.1f}",
548
- "Angle (°)": f"{info['launch_angle']:.1f}",
549
- "Height (m)": f"{info['launch_height']:.1f}",
550
- "Flight Time (s)": f"{info['flight_time']:.2f}",
551
- "Range (m)": f"{info['range']:.1f}",
552
- "Max Height (m)": f"{info['max_height']:.1f}"
553
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
 
555
- st.table(comparison_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
 
557
  if __name__ == "__main__":
558
  main()
 
3
  import matplotlib.pyplot as plt
4
  from kinematics_visualizer import Motion1D, Motion2D, KinematicsVisualizer
5
 
6
+ def clear_comparison_state():
7
+ """Clear any lingering comparison state"""
8
+ keys_to_clear = [key for key in st.session_state.keys() if key.startswith('comp_')]
9
+ for key in keys_to_clear:
10
+ if key in st.session_state:
11
+ del st.session_state[key]
12
+
13
  # Configure Streamlit page
14
  st.set_page_config(
15
  page_title="Physics Tutorial: Kinematics",
 
75
  ["1D Motion", "2D Projectile Motion", "Compare Motions"]
76
  )
77
 
78
+ # Clear comparison state when not on comparison tab
79
+ if tutorial_type != "Compare Motions":
80
+ clear_comparison_state()
81
+ plt.close('all') # Close any lingering plots
82
+
83
  if tutorial_type == "1D Motion":
84
  show_1d_motion()
85
  elif tutorial_type == "2D Projectile Motion":
 
485
 
486
  st.markdown("**Compare up to 3 different projectile motions side by side**")
487
 
488
+ # Clear any previous matplotlib figures explicitly
489
+ plt.clf()
490
+ plt.close('all')
491
+
492
  # Create tabs for different trajectories
493
  tab1, tab2, tab3 = st.tabs(["🚀 Trajectory 1", "🎯 Trajectory 2", "⚽ Trajectory 3"])
494
 
495
  trajectories = []
496
 
497
+ # Use containers to isolate each tab's content
498
  with tab1:
499
+ with st.container():
500
+ col1, col2 = st.columns(2)
501
+ with col1:
502
+ speed1 = st.slider("Speed 1 (m/s)", 5.0, 50.0, 20.0, key="comp_speed1")
503
+ angle1 = st.slider("Angle 1 (°)", 0.0, 90.0, 30.0, key="comp_angle1")
504
+ with col2:
505
+ height1 = st.slider("Height 1 (m)", 0.0, 30.0, 0.0, key="comp_height1")
506
+
507
+ motion1 = Motion2D(launch_speed=speed1, launch_angle=angle1, launch_height=height1)
508
+ trajectories.append(("Trajectory 1", motion1, 'blue'))
509
 
510
  with tab2:
511
+ with st.container():
512
+ col1, col2 = st.columns(2)
513
+ with col1:
514
+ speed2 = st.slider("Speed 2 (m/s)", 5.0, 50.0, 25.0, key="comp_speed2")
515
+ angle2 = st.slider("Angle 2 (°)", 0.0, 90.0, 45.0, key="comp_angle2")
516
+ with col2:
517
+ height2 = st.slider("Height 2 (m)", 0.0, 30.0, 0.0, key="comp_height2")
518
+
519
+ motion2 = Motion2D(launch_speed=speed2, launch_angle=angle2, launch_height=height2)
520
+ trajectories.append(("Trajectory 2", motion2, 'red'))
521
 
522
  with tab3:
523
+ with st.container():
524
+ col1, col2 = st.columns(2)
525
+ with col1:
526
+ speed3 = st.slider("Speed 3 (m/s)", 5.0, 50.0, 30.0, key="comp_speed3")
527
+ angle3 = st.slider("Angle 3 (°)", 0.0, 90.0, 60.0, key="comp_angle3")
528
+ with col2:
529
+ height3 = st.slider("Height 3 (m)", 0.0, 30.0, 5.0, key="comp_height3")
530
+
531
+ motion3 = Motion2D(launch_speed=speed3, launch_angle=angle3, launch_height=height3)
532
+ trajectories.append(("Trajectory 3", motion3, 'green'))
 
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
+ # Create comparison plot in its own container
535
+ with st.container():
536
+ try:
537
+ # Ensure we have a clean figure
538
+ plt.close('all')
539
+ fig, ax = plt.subplots(figsize=(12, 8))
540
+
541
+ max_range = 0
542
+ for name, motion, color in trajectories:
543
+ try:
544
+ data = motion.trajectory_data(motion.calculate_flight_time())
545
+ if len(data['x']) > 0: # Check data exists
546
+ ax.plot(data['x'], data['y'], linewidth=3, label=name, color=color)
547
+
548
+ # Mark launch and landing points
549
+ ax.plot(motion.launch_x, motion.launch_height, 'o', markersize=8, color=color, alpha=0.7)
550
+ ax.plot(data['x'][-1], data['y'][-1], 's', markersize=8, color=color, alpha=0.7)
551
+ max_range = max(max_range, data['x'][-1] if data['x'][-1] > 0 else 0)
552
+ except Exception as e:
553
+ st.error(f"Error plotting {name}: {str(e)}")
554
+ continue
555
+
556
+ # Only proceed if we have valid data
557
+ if max_range > 0:
558
+ ax.set_xlabel('Horizontal Position (m)', fontsize=12)
559
+ ax.set_ylabel('Vertical Position (m)', fontsize=12)
560
+ ax.set_title('Trajectory Comparison', fontsize=16, fontweight='bold')
561
+ ax.grid(True, alpha=0.3)
562
+ ax.legend(fontsize=12)
563
+ ax.set_ylim(bottom=0)
564
+ ax.set_xlim(0, max_range * 1.1)
565
+
566
+ # Display plot
567
+ st.pyplot(fig, clear_figure=True) # Important: clear_figure=True
568
+ else:
569
+ st.warning("No valid trajectory data to display")
570
+
571
+ except Exception as e:
572
+ st.error(f"Error creating comparison plot: {str(e)}")
573
+ finally:
574
+ plt.close(fig) # Ensure figure is always closed
575
+ plt.close('all')
576
 
577
+ # Comparison table in its own container
578
+ with st.container():
579
+ st.markdown("### 📋 Trajectory Comparison Table")
580
+
581
+ try:
582
+ comparison_data = []
583
+ for name, motion, _ in trajectories:
584
+ try:
585
+ info = motion.get_launch_info()
586
+ comparison_data.append({
587
+ "Trajectory": name,
588
+ "Speed (m/s)": f"{info['launch_speed']:.1f}",
589
+ "Angle (°)": f"{info['launch_angle']:.1f}",
590
+ "Height (m)": f"{info['launch_height']:.1f}",
591
+ "Flight Time (s)": f"{info['flight_time']:.2f}",
592
+ "Range (m)": f"{info['range']:.1f}",
593
+ "Max Height (m)": f"{info['max_height']:.1f}"
594
+ })
595
+ except Exception as e:
596
+ st.error(f"Error getting info for {name}: {str(e)}")
597
+ continue
598
+
599
+ if comparison_data:
600
+ st.table(comparison_data)
601
+ else:
602
+ st.warning("No trajectory data available for comparison table")
603
+
604
+ except Exception as e:
605
+ st.error(f"Error creating comparison table: {str(e)}")
606
 
607
  if __name__ == "__main__":
608
  main()