Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| # rotating_checkerboard.py - Simple rotating pattern | |
| import numpy as np | |
| import time | |
| import threading | |
| import os | |
| from flask import Flask, Response | |
| PORT = int(os.getenv('PORT', 7860)) | |
| WIDTH, HEIGHT = 400, 300 | |
| TILE_SIZE = 40 | |
| print(f"🚀 Starting rotating checkerboard on port {PORT}") | |
| # Shared memory | |
| shared = { | |
| "pixels": np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8), | |
| "frame_count": 0, | |
| "streaming": True, | |
| "lock": threading.Lock(), | |
| "rotation": 0.0 | |
| } | |
| app = Flask(__name__) | |
| HTML = '''<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Rotating Checkerboard</title> | |
| <style> | |
| body { margin: 0; padding: 20px; background: #111; color: white; font-family: Arial; text-align: center; } | |
| canvas { border: 3px solid #0af; background: black; display: block; margin: 20px auto; } | |
| .stats { background: #222; padding: 15px; border-radius: 8px; display: inline-block; margin: 10px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🔄 Rotating Checkerboard</h1> | |
| <canvas id="canvas" width="400" height="300"></canvas> | |
| <div class="stats"> | |
| <div>Frames: <span id="frameCount">0</span></div> | |
| <div>Rotation: <span id="rotation">0°</span></div> | |
| <div>Status: <span id="status">Loading...</span></div> | |
| </div> | |
| <script> | |
| const canvas = document.getElementById('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| function loadFrame() { | |
| fetch('/frame') | |
| .then(r => r.arrayBuffer()) | |
| .then(buffer => { | |
| if (buffer.byteLength !== 400*300*3) { | |
| console.error('Wrong size:', buffer.byteLength); | |
| return; | |
| } | |
| const imageData = new ImageData(400, 300); | |
| const rgba = imageData.data; | |
| const rgb = new Uint8Array(buffer); | |
| // Convert RGB to RGBA | |
| for (let i = 0, j = 0; i < rgba.length; i += 4, j += 3) { | |
| [i] = rgb[j]; | |
| rgba[i + 1] = rgb[j + 1]; | |
| rgba[i + 2] = rgb[j + 2]; | |
| rgba[i + 3] = 255; | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| // Update stats | |
| fetch('/stats') | |
| .then(r => r.json()) | |
| .then(data => { | |
| document.getElementById('frameCount').textContent = data.frame_count; | |
| document.getElementById('rotation').textContent = data.rotation.toFixed(1) + '°'; | |
| document.getElementById('status').textContent = 'Streaming ✓'; | |
| document.getElementById('status').style.color = '#0af'; | |
| }); | |
| }) | |
| .catch(err => { | |
| console.error('Error:', err); | |
| document.getElementById('status').textContent = 'Error'; | |
| document.getElementById('status').style.color = 'red'; | |
| }); | |
| } | |
| setInterval(loadFrame, 33); | |
| loadFrame(); | |
| </script> | |
| </body> | |
| </html>''' | |
| def index(): | |
| return HTML | |
| def stats(): | |
| with shared['lock']: | |
| return { | |
| 'frame_count': shared['frame_count'], | |
| 'rotation': shared['rotation'] | |
| } | |
| def get_frame(): | |
| """Return rotating checkerboard""" | |
| with shared['lock']: | |
| # Create checkerboard with rotation | |
| pixels = shared['pixels'] | |
| # Update rotation | |
| shared['rotation'] = (shared['rotation'] + 1.0) % 360.0 | |
| rotation_rad = np.radians(shared['rotation']) | |
| # Center coordinates | |
| center_x = WIDTH / 2 | |
| center_y = HEIGHT / 2 | |
| # Create coordinate grids | |
| y_coords, x_coords = np.mgrid[:HEIGHT, :WIDTH] | |
| # Translate to center | |
| x_rel = x_coords - center_x | |
| y_rel = y_coords - center_y | |
| # Apply rotation | |
| x_rot = x_rel * np.cos(rotation_rad) - y_rel * np.sin(rotation_rad) | |
| y_rot = x_rel * np.sin(rotation_rad) + y_rel * np.cos(rotation_rad) | |
| # Translate back | |
| x_rot += center_x | |
| y_rot += center_y | |
| # Create rotated checkerboard | |
| tile_x = (x_rot // TILE_SIZE).astype(int) | |
| tile_y = (y_rot // TILE_SIZE).astype(int) | |
| # Checker pattern | |
| checker = ((tile_x + tile_y) % 2 == 0) | |
| # Set colors | |
| pixels[:, :] = [30, 30, 60] # Dark blue background | |
| # White tiles | |
| pixels[checker, 0] = 255 # R | |
| pixels[checker, 1] = 255 # G | |
| pixels[checker, 2] = 255 # B | |
| # Black tiles already set | |
| # Add a colored rotating dot | |
| dot_x = int(center_x + np.cos(rotation_rad * 2) * 100) | |
| dot_y = int(center_y + np.sin(rotation_rad * 2) * 80) | |
| # Draw dot | |
| dot_mask = ((x_coords - dot_x)**2 + (y_coords - dot_y)**2) < 15**2 | |
| pixels[dot_mask, 0] = 255 # Red | |
| pixels[dot_mask, 1] = 100 # Green | |
| pixels[dot_mask, 2] = 100 # Blue | |
| # Add frame counter text (simulated with colored pixels) | |
| frame_num = shared['frame_count'] | |
| if frame_num < 100: # Simple text in top-left | |
| for i in range(min(10, len(str(frame_num)))): | |
| digit = int(str(frame_num)[i]) if i < len(str(frame_num)) else 0 | |
| for dy in range(7): | |
| for dx in range(5): | |
| if 10 + dy*10 < HEIGHT and 10 + i*30 + dx*5 < WIDTH: | |
| pixels[10 + dy*10, 10 + i*30 + dx*5] = [100 + digit*15, 200, 100] | |
| # Convert to bytes | |
| pixel_bytes = pixels.tobytes() | |
| shared['frame_count'] += 1 | |
| print(f"📤 Frame {shared['frame_count']}, Rotation: {shared['rotation']:.1f}°") | |
| return Response( | |
| pixel_bytes, | |
| mimetype='application/octet-stream', | |
| headers={'Cache-Control': 'no-cache'} | |
| ) | |
| def update_pattern(): | |
| """Thread that continuously updates the pattern""" | |
| while shared['streaming']: | |
| # The pattern is generated on-demand in /frame endpoint | |
| # So we just sleep here | |
| time.sleep(0.01) | |
| def main(): | |
| print("="*60) | |
| print("🔄 Rotating Checkerboard Server") | |
| print("="*60) | |
| print(f"📡 Port: {PORT}") | |
| print(f"🎨 Resolution: {WIDTH}x{HEIGHT}") | |
| print(f"🧱 Tile size: {TILE_SIZE}") | |
| print("="*60) | |
| # Start update thread | |
| update_thread = threading.Thread(target=update_pattern, daemon=True) | |
| update_thread.start() | |
| # Start Flask | |
| app.run(host='0.0.0.0', port=PORT, threaded=True, use_reloader=False) | |
| if __name__ == "__main__": | |
| main() |