-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdemo.py
More file actions
355 lines (293 loc) · 13.5 KB
/
demo.py
File metadata and controls
355 lines (293 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#!/usr/bin/env python3
"""
Demo script for the Advanced Sorting Algorithm Visualizer
Shows off the key features and capabilities with recording support
"""
import pygame
import time
import sys
import os
from datetime import datetime
# Add the current directory to the Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from main import *
class DemoRecorder:
"""Class to handle recording of the demo"""
def __init__(self, output_dir="recordings"):
self.output_dir = output_dir
self.recording = False
self.frame_count = 0
self.current_algorithm = ""
# Create recordings directory if it doesn't exist
if not os.path.exists(self.output_dir):
os.makedirs(self.output_dir)
def start_recording(self, algorithm_name):
"""Start recording for a specific algorithm"""
self.recording = True
self.frame_count = 0
self.current_algorithm = algorithm_name
# Create algorithm-specific directory
self.algorithm_dir = os.path.join(self.output_dir,
f"{algorithm_name.replace(' ', '_').lower()}")
if not os.path.exists(self.algorithm_dir):
os.makedirs(self.algorithm_dir)
print(f"🎬 Started recording {algorithm_name}")
def capture_frame(self, surface):
"""Capture a frame during recording"""
if self.recording:
filename = os.path.join(self.algorithm_dir, f"frame_{self.frame_count:06d}.png")
pygame.image.save(surface, filename)
self.frame_count += 1
def stop_recording(self):
"""Stop recording and provide conversion instructions"""
if self.recording:
self.recording = False
print(f"🎬 Stopped recording {self.current_algorithm}")
print(f"📁 Frames saved to: {self.algorithm_dir}")
print(f"📊 Total frames: {self.frame_count}")
# Provide ffmpeg command for video conversion
video_name = f"{self.current_algorithm.replace(' ', '_').lower()}.mp4"
ffmpeg_cmd = f"ffmpeg -framerate 30 -i {self.algorithm_dir}/frame_%06d.png -c:v libx264 -pix_fmt yuv420p {video_name}"
print(f"🎥 To create video: {ffmpeg_cmd}")
print()
def create_gif(self, algorithm_name, duration=0.1):
"""Create a GIF from captured frames (requires PIL)"""
try:
from PIL import Image
algorithm_dir = os.path.join(self.output_dir,
f"{algorithm_name.replace(' ', '_').lower()}")
if not os.path.exists(algorithm_dir):
print(f"❌ No frames found for {algorithm_name}")
return
# Get all frame files
frame_files = sorted([f for f in os.listdir(algorithm_dir) if f.endswith('.png')])
if not frame_files:
print(f"❌ No PNG frames found for {algorithm_name}")
return
if len(frame_files) < 2:
print(f"❌ Not enough frames for {algorithm_name} (need at least 2)")
return
print(f"📸 Processing {len(frame_files)} frames for {algorithm_name}...")
# Load and process images
frames = []
max_frames = min(200, len(frame_files)) # Limit to 200 frames for reasonable file size
step = max(1, len(frame_files) // max_frames) # Calculate step to get ~200 frames
for i in range(0, len(frame_files), step):
frame_path = os.path.join(algorithm_dir, frame_files[i])
try:
img = Image.open(frame_path)
# Convert to RGB if necessary (removes alpha channel)
if img.mode != 'RGB':
img = img.convert('RGB')
# Resize to reduce file size while maintaining aspect ratio
width, height = img.size
new_width = min(800, width)
new_height = int(height * new_width / width)
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
frames.append(img)
except Exception as e:
print(f"⚠️ Skipping frame {frame_files[i]}: {e}")
continue
if len(frames) < 2:
print(f"❌ Not enough valid frames processed for {algorithm_name}")
return
# Create GIF
gif_name = f"{algorithm_name.replace(' ', '_').lower()}.gif"
gif_path = os.path.join(self.output_dir, gif_name)
# Optimize GIF creation
frames[0].save(
gif_path,
save_all=True,
append_images=frames[1:],
duration=int(duration * 1000), # Convert to milliseconds
loop=0,
optimize=True, # Optimize for smaller file size
quality=85 # Good quality balance
)
# Check if file was created successfully
if os.path.exists(gif_path):
file_size = os.path.getsize(gif_path)
print(f"✅ GIF created: {gif_path}")
print(f"📊 File size: {file_size / (1024*1024):.1f} MB")
print(f"🎞️ Total frames: {len(frames)}")
else:
print(f"❌ Failed to create GIF file: {gif_path}")
except ImportError:
print("❌ PIL not installed. Install with: pip install Pillow")
except Exception as e:
print(f"❌ Error creating GIF: {e}")
import traceback
traceback.print_exc()
def demo_mode(record=False, create_gifs=False, express_mode=False):
"""Run a demo showcasing different algorithms and features"""
pygame.init()
# Configuration
WINDOW_WIDTH = 1200
WINDOW_HEIGHT = 800
# Demo settings - optimized for speed
if express_mode:
n = 15 # Even smaller array for express mode
min_val = 1
max_val = 50
frame_skip = 5 # Skip more frames
speed_multiplier = 10
pause_time = 0.2
else:
n = 25 # Reduced array size for faster sorting
min_val = 10
max_val = 100 # Reduced range for faster processing
frame_skip = 2
speed_multiplier = 8
pause_time = 0.5
# Initialize recorder
recorder = DemoRecorder() if record else None
# Initialize
lst = generate_list(n, min_val, max_val)
draw_info = DrawInformation(WINDOW_WIDTH, WINDOW_HEIGHT, lst)
# Demo algorithms in order
demo_algorithms = [
(bubble_sort, "Bubble Sort"),
(insertion_sort, "Insertion Sort"),
(selection_sort, "Selection Sort"),
(merge_sort, "Merge Sort"),
(quick_sort, "Quick Sort"),
(heap_sort, "Heap Sort")
]
clock = pygame.time.Clock()
print("🎯 Advanced Sorting Algorithm Visualizer Demo")
print("=" * 50)
if express_mode:
print("⚡ EXPRESS MODE - Ultra-fast demo with smaller arrays")
else:
print("🚀 FAST MODE - Optimized for speed")
print("This demo will showcase all sorting algorithms with different themes")
if record:
print("🎬 Recording enabled - frames will be saved to 'recordings' folder")
print("Press ESC to exit the demo at any time")
print()
for i, (algorithm, name) in enumerate(demo_algorithms):
print(f"📊 Demonstrating {name}...")
# Start recording for this algorithm
if recorder:
recorder.start_recording(name)
# Cycle through themes
theme_index = i % len(Theme)
draw_info.theme = list(Theme)[theme_index]
draw_info.colors = ColorScheme.get_theme(draw_info.theme)
# Reset array
lst = generate_list(n, min_val, max_val)
draw_info.set_list(lst)
draw_info.stats.start_sorting()
# Run algorithm
sorting_generator = algorithm(draw_info, True)
sorting = True
while sorting:
clock.tick(120 if express_mode else 60) # Even faster for express mode
# Handle quit events
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
if recorder:
recorder.stop_recording()
pygame.quit()
return
try:
# Skip frames to make it faster
for _ in range(frame_skip):
next(sorting_generator)
draw_complete(draw_info, name, True, speed_multiplier)
# Capture frame if recording (reduced frequency)
if recorder and draw_info.stats.swaps % (frame_skip + 1) == 0:
recorder.capture_frame(draw_info.window)
except StopIteration:
sorting = False
draw_info.stats.end_sorting()
# Show completion stats
stats = draw_info.stats
print(f"✅ {name} completed:")
print(f" Comparisons: {stats.comparisons}")
print(f" Swaps: {stats.swaps}")
print(f" Time: {stats.get_elapsed_time():.3f}s")
print()
# Stop recording for this algorithm
if recorder:
recorder.stop_recording()
# Create GIF if requested
if create_gifs:
recorder.create_gif(name)
# Pause between algorithms (reduced)
time.sleep(pause_time) # Variable pause time based on mode
print("🎉 Demo completed! All algorithms have been demonstrated.")
if record:
print(f"📁 All recordings saved to: {recorder.output_dir}")
print("🎥 To create videos from frames, use the provided ffmpeg commands")
if create_gifs:
print("🖼️ GIF files created in recordings folder")
print("Run 'python main.py' for interactive mode.")
# Keep window open for a moment (reduced)
time.sleep(1) # Reduced from 3 seconds to 1 second
pygame.quit()
def main():
"""Main function with command line argument support"""
import argparse
parser = argparse.ArgumentParser(description='Advanced Sorting Algorithm Visualizer Demo')
parser.add_argument('--record', '-r', action='store_true',
help='Record frames during demonstration')
parser.add_argument('--gif', '-g', action='store_true',
help='Create GIF files from recorded frames (requires Pillow)')
parser.add_argument('--express', '-e', action='store_true',
help='Express mode - ultra-fast demo with smaller arrays')
parser.add_argument('--help-recording', action='store_true',
help='Show recording help and requirements')
args = parser.parse_args()
if args.help_recording:
show_recording_help()
return
demo_mode(record=args.record, create_gifs=args.gif, express_mode=args.express)
def show_recording_help():
"""Show help information for recording features"""
print("🎬 Recording Help - Advanced Sorting Algorithm Visualizer")
print("=" * 60)
print()
print("📋 Basic Usage:")
print(" python demo.py # Run demo without recording")
print(" python demo.py --record # Run demo with frame recording")
print(" python demo.py --record --gif # Record and create GIFs")
print(" python demo.py --express # Ultra-fast demo mode")
print(" python demo.py --express --record # Fast demo with recording")
print()
print("📁 Output Structure:")
print(" recordings/")
print(" ├── bubble_sort/")
print(" │ ├── frame_000001.png")
print(" │ ├── frame_000002.png")
print(" │ └── ...")
print(" ├── insertion_sort/")
print(" └── ...")
print()
print("🎥 Creating Videos (requires ffmpeg):")
print(" # Install ffmpeg first:")
print(" # macOS: brew install ffmpeg")
print(" # Ubuntu: sudo apt install ffmpeg")
print(" # Windows: download from https://ffmpeg.org/")
print()
print(" # Create MP4 video:")
print(" ffmpeg -framerate 30 -i recordings/bubble_sort/frame_%06d.png \\")
print(" -c:v libx264 -pix_fmt yuv420p bubble_sort.mp4")
print()
print("🖼️ Creating GIFs (requires Pillow):")
print(" pip install Pillow")
print(" python demo.py --record --gif")
print()
print("⚙️ Advanced Options:")
print(" • Frames are saved as PNG files")
print(" • Default frame rate: 30 FPS")
print(" • GIFs use every 2nd frame to reduce size")
print(" • Each algorithm gets its own folder")
print()
print("💡 Tips:")
print(" • Large arrays create more frames")
print(" • Each algorithm demo creates ~500-2000 frames")
print(" • Total recording size: ~50-200MB per algorithm")
print(" • Use --gif for quick sharing, ffmpeg for high quality")
if __name__ == "__main__":
main()