#!/usr/bin/env python3
# complete_fixed_demo.py - Fixed with PyGame init and animation
import os
import pygame
import numpy as np
import time
import threading
from flask import Flask, Response
# ===== CRITICAL FIXES =====
os.environ['SDL_VIDEODRIVER'] = 'dummy'
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
PORT = int(os.getenv('PORT', 7860))
WIDTH, HEIGHT = 800, 600
app = Flask(__name__)
# Shared state
frame_data = {
"pixels": None,
"frame_id": 0,
"lock": threading.Lock()
}
@app.route('/')
def index():
return '''
Complete Demo
⚡ Complete Demo
Mode: Loading...
'''
@app.route('/pygame_frame')
def pygame_frame():
"""PyGame animation endpoint"""
with frame_data["lock"]:
if frame_data["pixels"] is not None:
return Response(
frame_data["pixels"].tobytes(),
mimetype='application/octet-stream',
headers={'Cache-Control': 'no-cache'}
)
# Fallback
black = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8).tobytes()
return Response(black, mimetype='application/octet-stream')
def pygame_animation():
"""PyGame animation with proper initialization"""
print("🎬 Starting PyGame animation...")
try:
# ===== FIX 1: Proper PyGame initialization =====
pygame.init()
# Initialize display (CRITICAL for headless)
pygame.display.init()
pygame.display.set_mode((1, 1), pygame.HIDDEN) # Hidden window
print("✅ PyGame display initialized")
# Create surface
surface = pygame.Surface((WIDTH, HEIGHT))
print(f"✅ Surface created: {WIDTH}x{HEIGHT}")
except Exception as e:
print(f"❌ PyGame init failed: {e}")
import traceback
traceback.print_exc()
return
# ===== FIX 2: Animated objects =====
particles = []
for _ in range(100):
particles.append({
'x': np.random.randint(0, WIDTH),
'y': np.random.randint(0, HEIGHT),
'dx': np.random.uniform(-3, 3),
'dy': np.random.uniform(-3, 3),
'size': np.random.randint(2, 10),
'color': (
np.random.randint(50, 255),
np.random.randint(50, 255),
np.random.randint(50, 255)
),
'life': np.random.randint(100, 300),
'type': np.random.choice(['circle', 'square', 'triangle'])
})
# Rotating geometric shapes
shapes = []
for i in range(8):
shapes.append({
'angle': i * np.pi / 4,
'radius': 100 + i * 20,
'speed': 0.5 + i * 0.1,
'size': 15 + i * 5,
'color': (
(i * 32) % 256,
(i * 64) % 256,
(i * 96) % 256
)
})
print(f"✅ Created {len(particles)} particles and {len(shapes)} shapes")
frame_id = 0
start_time = time.time()
while True:
frame_start = time.time()
# ===== FIX 3: Time-based animation =====
elapsed = time.time() - start_time
# Clear with gradient
for y in range(HEIGHT):
color = int(20 + y / HEIGHT * 30)
pygame.draw.line(surface, (color, color, color + 20),
(0, y), (WIDTH, y))
# Draw rotating shapes
for shape in shapes:
shape['angle'] += shape['speed'] * 0.05
x = WIDTH // 2 + np.cos(shape['angle']) * shape['radius']
y = HEIGHT // 2 + np.sin(shape['angle']) * shape['radius']
# Draw shape
if np.random.random() < 0.3: # 30% chance of triangle
points = [
(x, y - shape['size']),
(x - shape['size'], y + shape['size']),
(x + shape['size'], y + shape['size'])
]
pygame.draw.polygon(surface, shape['color'], points)
else:
pygame.draw.circle(surface, shape['color'],
(int(x), int(y)), shape['size'])
# Connect shapes with lines
pygame.draw.circle(surface, (255, 255, 255, 100),
(int(x), int(y)), shape['size'], 2)
# Update and draw particles
for p in particles:
p['x'] += p['dx']
p['y'] += p['dy']
p['life'] -= 1
# Bounce or respawn
if (p['x'] < 0 or p['x'] > WIDTH or
p['y'] < 0 or p['y'] > HEIGHT or
p['life'] <= 0):
p['x'] = np.random.randint(0, WIDTH)
p['y'] = np.random.randint(0, HEIGHT)
p['dx'] = np.random.uniform(-3, 3)
p['dy'] = np.random.uniform(-3, 3)
p['life'] = np.random.randint(100, 300)
p['color'] = (
np.random.randint(50, 255),
np.random.randint(50, 255),
np.random.randint(50, 255)
)
# Draw particle with trail
for i in range(3):
trail_x = p['x'] - p['dx'] * i * 0.3
trail_y = p['y'] - p['dy'] * i * 0.3
trail_size = p['size'] * (1 - i * 0.3)
trail_alpha = 200 - i * 60
pygame.draw.circle(
surface,
(*p['color'], trail_alpha),
(int(trail_x), int(trail_y)),
int(trail_size)
)
# Add frame info
font = pygame.font.Font(None, 28)
info = [
f"Frame: {frame_id}",
f"Time: {elapsed:.1f}s",
f"FPS: {int(1/(time.time()-frame_start)) if frame_id>0 else 0}",
f"Particles: {len(particles)}"
]
for i, text in enumerate(info):
text_surface = font.render(text, True, (255, 255, 200))
surface.blit(text_surface, (10, 10 + i * 30))
# Convert to NumPy array
pixels = pygame.surfarray.pixels3d(surface)
# Update shared memory
with frame_data["lock"]:
frame_data["pixels"] = pixels.copy()
frame_data["frame_id"] = frame_id
frame_id += 1
# Log every 30 frames
if frame_id % 30 == 0:
current_fps = 1 / (time.time() - frame_start) if frame_id > 0 else 0
print(f"📊 Frame {frame_id} | FPS: {current_fps:.1f}")
# ===== FIX 4: Frame rate control =====
frame_time = time.time() - frame_start
target_time = 1/30 # 30 FPS
if frame_time < target_time:
time.sleep(target_time - frame_time)
# Start PyGame thread
threading.Thread(target=pygame_animation, daemon=True).start()
# Wait for initialization
print("⏳ Waiting for PyGame to start...")
time.sleep(3)
print("🌐 Starting Flask server...")
print(f"📡 Port: {PORT}")
print("="*60)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=PORT, threaded=True, use_reloader=False)