Files
bounce/src/visualization/animate_bouncing_balls.py
bennettldavid c6b08a089d Provided modular architecture for animated bouncing ball analysis
- Organized project into src directory with subpackages (analysis, data, visualization, utils)
- Added comprehensive README with project overview and structure
- Implemented data loading, bounce detection, and visualization modules
- Created example scripts and Jupyter notebook for project usage
- Added requirements.txt for dependency management
- Included output files for different ball types (golf, lacrosse, metal)
2025-03-01 16:55:29 -07:00

271 lines
12 KiB
Python

#!/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()