#!/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 = ''' Rotating Checkerboard

🔄 Rotating Checkerboard

Frames: 0
Rotation: 0°
Status: Loading...
''' @app.route('/') def index(): return HTML @app.route('/stats') def stats(): with shared['lock']: return { 'frame_count': shared['frame_count'], 'rotation': shared['rotation'] } @app.route('/frame') 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()