#!/usr/bin/env python3 """ Bouncing Ball Animation Launcher This script provides a simple command-line interface to run different animated visualizations of bouncing ball data. """ import sys import argparse import importlib.util import os def check_dependencies(): """Check if all required dependencies are installed.""" required_packages = ['pandas', 'numpy', 'matplotlib', 'seaborn', 'scipy'] missing_packages = [] for package in required_packages: try: importlib.import_module(package) except ImportError: missing_packages.append(package) if missing_packages: print("Missing required packages:") for package in missing_packages: print(f" - {package}") print("\nPlease install them using:") print(f"pip install {' '.join(missing_packages)}") return False return True def check_files(): """Check if the animation script files exist.""" required_files = ['lab2_part1_animated.py', 'lab2_part2_animated.py'] missing_files = [] for file in required_files: if not os.path.exists(file): missing_files.append(file) if missing_files: print("Missing required script files:") for file in missing_files: print(f" - {file}") return False return True def check_data_files(ball_type=None): """Check if the required data files exist.""" if ball_type is None or ball_type.lower() == 'golf': golf_files = ["golf_11.csv", "golf_12.csv", "golf_13.csv", "golf_14.csv", "golf_15.csv"] for file in golf_files: if not os.path.exists(file): print(f"Warning: Golf ball data file '{file}' not found.") return False if ball_type is None or ball_type.lower() == 'lacrosse': lacrosse_files = ["l_18.csv", "l_19.csv", "l_20.csv", "l_21.csv", "l_22.csv"] for file in lacrosse_files: if not os.path.exists(file): print(f"Warning: Lacrosse ball data file '{file}' not found.") return False if ball_type is None or ball_type.lower() == 'metal': metal_files = ["metal_2.csv", "metal_4.csv", "metal_6.csv", "metal_8.csv", "metal_10.csv"] for file in metal_files: if not os.path.exists(file): print(f"Warning: Metal ball data file '{file}' not found.") return False return True def run_animation(animation_type, ball_type=None, save=False): """Run the selected animation.""" if animation_type == 'position': # Import the position vs. time animation script spec = importlib.util.spec_from_file_location("lab2_part1_animated", "lab2_part1_animated.py") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) # Run the animation if ball_type: if ball_type.lower() == 'golf': print("Running position vs. time animation for Golf balls...") module.animate_position_vs_time(module.golf_file_paths, "Golf Ball", save) elif ball_type.lower() == 'lacrosse': print("Running position vs. time animation for Lacrosse balls...") module.animate_position_vs_time(module.lacrosse_file_paths, "Lacrosse Ball", save) elif ball_type.lower() == 'metal': print("Running position vs. time animation for Metal balls...") module.animate_position_vs_time(module.metal_file_paths, "Metal Ball", save) else: print(f"Unknown ball type: {ball_type}") print("Available ball types: golf, lacrosse, metal") else: print("Running position vs. time animations for all ball types...") module.animate_all_ball_types(save) elif animation_type == 'bounce': # Import the bouncing ball animation script spec = importlib.util.spec_from_file_location("lab2_part2_animated", "lab2_part2_animated.py") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) # Set save_animations flag module.save_animations = save # Run the animation if ball_type: if ball_type.lower() == 'golf': print("Running bouncing ball animation for Golf balls...") module.golf_results = module.process_and_animate_all( module.golf_file_paths, ball_type='Golf', min_distance=module.min_distance, min_time_diff=module.min_time_diff, relative_prominence=module.golf_relative_prominence, low_height_threshold=module.golf_low_threshold, low_height_adjustment=module.golf_low_adjustment, high_height_threshold=module.golf_high_threshold, high_height_adjustment=module.golf_high_adjustment, max_bounces=7, fs=module.fs, save_animations=save ) module.plot_cor_table(module.golf_results, 'Golf') elif ball_type.lower() == 'lacrosse': print("Running bouncing ball animation for Lacrosse balls...") module.lacrosse_results = module.process_and_animate_all( module.lacrosse_file_paths, ball_type='Lacrosse', min_distance=module.min_distance, min_time_diff=module.min_time_diff, relative_prominence=module.lacrosse_relative_prominence, low_height_threshold=module.lacrosse_low_threshold, low_height_adjustment=module.lacrosse_low_adjustment, high_height_threshold=module.lacrosse_high_threshold, high_height_adjustment=module.lacrosse_high_adjustment, max_bounces=7, fs=module.fs, save_animations=save ) module.plot_cor_table(module.lacrosse_results, 'Lacrosse') elif ball_type.lower() == 'metal': print("Running bouncing ball animation for Metal balls...") module.metal_results = module.process_and_animate_all( module.metal_file_paths, ball_type='Metal', min_distance=module.min_distance, min_time_diff=module.min_time_diff, relative_prominence=module.metal_relative_prominence, low_height_threshold=module.metal_low_threshold, low_height_adjustment=module.metal_low_adjustment, high_height_threshold=module.metal_high_threshold, high_height_adjustment=module.metal_high_adjustment, max_bounces=7, fs=module.fs, save_animations=save ) module.plot_cor_table(module.metal_results, 'Metal') else: print(f"Unknown ball type: {ball_type}") print("Available ball types: golf, lacrosse, metal") else: # Run the animations for all ball types one by one print("Running bouncing ball animations for all ball types...") # Golf balls print("\nProcessing and animating Golf Ball trials...") module.golf_results = module.process_and_animate_all( module.golf_file_paths, ball_type='Golf', min_distance=module.min_distance, min_time_diff=module.min_time_diff, relative_prominence=module.golf_relative_prominence, low_height_threshold=module.golf_low_threshold, low_height_adjustment=module.golf_low_adjustment, high_height_threshold=module.golf_high_threshold, high_height_adjustment=module.golf_high_adjustment, max_bounces=7, fs=module.fs, save_animations=save ) # Lacrosse balls print("\nProcessing and animating Lacrosse Ball trials...") module.lacrosse_results = module.process_and_animate_all( module.lacrosse_file_paths, ball_type='Lacrosse', min_distance=module.min_distance, min_time_diff=module.min_time_diff, relative_prominence=module.lacrosse_relative_prominence, low_height_threshold=module.lacrosse_low_threshold, low_height_adjustment=module.lacrosse_low_adjustment, high_height_threshold=module.lacrosse_high_threshold, high_height_adjustment=module.lacrosse_high_adjustment, max_bounces=7, fs=module.fs, save_animations=save ) # Metal balls print("\nProcessing and animating Metal Ball trials...") module.metal_results = module.process_and_animate_all( module.metal_file_paths, ball_type='Metal', min_distance=module.min_distance, min_time_diff=module.min_time_diff, relative_prominence=module.metal_relative_prominence, low_height_threshold=module.metal_low_threshold, low_height_adjustment=module.metal_low_adjustment, high_height_threshold=module.metal_high_threshold, high_height_adjustment=module.metal_high_adjustment, max_bounces=7, fs=module.fs, save_animations=save ) # Print summary tables print("\nGolf Ball COR Table:") print(module.golf_results[['Trial', 'Initial Height', 'Average COR']]) print("\nLacrosse Ball COR Table:") print(module.lacrosse_results[['Trial', 'Initial Height', 'Average COR']]) print("\nMetal Ball COR Table:") print(module.metal_results[['Trial', 'Initial Height', 'Average COR']]) # Plot COR vs. initial height for each ball type module.plot_cor_table(module.golf_results, 'Golf') module.plot_cor_table(module.lacrosse_results, 'Lacrosse') module.plot_cor_table(module.metal_results, 'Metal') else: print(f"Unknown animation type: {animation_type}") print("Available animation types: position, bounce") def main(): """Main function to parse arguments and run the selected animation.""" parser = argparse.ArgumentParser(description='Run bouncing ball animations.') parser.add_argument('animation_type', choices=['position', 'bounce'], help='Type of animation to run: position (for position vs. time) or bounce (for bouncing ball physics)') parser.add_argument('--ball', choices=['golf', 'lacrosse', 'metal'], help='Type of ball to animate (default: all)') parser.add_argument('--save', action='store_true', help='Save animations as GIF files') args = parser.parse_args() # Check dependencies and files if not check_dependencies(): print("Please install the required dependencies and try again.") sys.exit(1) if not check_files(): print("Please ensure the animation script files are in the current directory and try again.") sys.exit(1) if not check_data_files(args.ball): print("Warning: Some data files are missing. The animations may not work correctly.") response = input("Do you want to continue anyway? (y/n): ") if response.lower() != 'y': sys.exit(1) # Run the selected animation run_animation(args.animation_type, args.ball, args.save) if __name__ == '__main__': main()