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