MySafeCode commited on
Commit
9f8b91e
Β·
verified Β·
1 Parent(s): d7b3bf7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +211 -0
app.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # rotating_checkerboard.py - Simple rotating pattern
3
+ import numpy as np
4
+ import time
5
+ import threading
6
+ import os
7
+ from flask import Flask, Response
8
+
9
+ PORT = int(os.getenv('PORT', 7860))
10
+ WIDTH, HEIGHT = 400, 300
11
+ TILE_SIZE = 40
12
+
13
+ print(f"πŸš€ Starting rotating checkerboard on port {PORT}")
14
+
15
+ # Shared memory
16
+ shared = {
17
+ "pixels": np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8),
18
+ "frame_count": 0,
19
+ "streaming": True,
20
+ "lock": threading.Lock(),
21
+ "rotation": 0.0
22
+ }
23
+
24
+ app = Flask(__name__)
25
+
26
+ HTML = '''<!DOCTYPE html>
27
+ <html>
28
+ <head>
29
+ <title>Rotating Checkerboard</title>
30
+ <style>
31
+ body { margin: 0; padding: 20px; background: #111; color: white; font-family: Arial; text-align: center; }
32
+ canvas { border: 3px solid #0af; background: black; display: block; margin: 20px auto; }
33
+ .stats { background: #222; padding: 15px; border-radius: 8px; display: inline-block; margin: 10px; }
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <h1>πŸ”„ Rotating Checkerboard</h1>
38
+ <canvas id="canvas" width="400" height="300"></canvas>
39
+
40
+ <div class="stats">
41
+ <div>Frames: <span id="frameCount">0</span></div>
42
+ <div>Rotation: <span id="rotation">0Β°</span></div>
43
+ <div>Status: <span id="status">Loading...</span></div>
44
+ </div>
45
+
46
+ <script>
47
+ const canvas = document.getElementById('canvas');
48
+ const ctx = canvas.getContext('2d');
49
+
50
+ function loadFrame() {
51
+ fetch('/frame')
52
+ .then(r => r.arrayBuffer())
53
+ .then(buffer => {
54
+ if (buffer.byteLength !== 400*300*3) {
55
+ console.error('Wrong size:', buffer.byteLength);
56
+ return;
57
+ }
58
+
59
+ const imageData = new ImageData(400, 300);
60
+ const rgba = imageData.data;
61
+ const rgb = new Uint8Array(buffer);
62
+
63
+ // Convert RGB to RGBA
64
+ for (let i = 0, j = 0; i < rgba.length; i += 4, j += 3) {
65
+ [i] = rgb[j];
66
+ rgba[i + 1] = rgb[j + 1];
67
+ rgba[i + 2] = rgb[j + 2];
68
+ rgba[i + 3] = 255;
69
+ }
70
+
71
+ ctx.putImageData(imageData, 0, 0);
72
+
73
+ // Update stats
74
+ fetch('/stats')
75
+ .then(r => r.json())
76
+ .then(data => {
77
+ document.getElementById('frameCount').textContent = data.frame_count;
78
+ document.getElementById('rotation').textContent = data.rotation.toFixed(1) + 'Β°';
79
+ document.getElementById('status').textContent = 'Streaming βœ“';
80
+ document.getElementById('status').style.color = '#0af';
81
+ });
82
+ })
83
+ .catch(err => {
84
+ console.error('Error:', err);
85
+ document.getElementById('status').textContent = 'Error';
86
+ document.getElementById('status').style.color = 'red';
87
+ });
88
+ }
89
+
90
+ setInterval(loadFrame, 33);
91
+ loadFrame();
92
+ </script>
93
+ </body>
94
+ </html>'''
95
+
96
+ @app.route('/')
97
+ def index():
98
+ return HTML
99
+
100
+ @app.route('/stats')
101
+ def stats():
102
+ with shared['lock']:
103
+ return {
104
+ 'frame_count': shared['frame_count'],
105
+ 'rotation': shared['rotation']
106
+ }
107
+
108
+ @app.route('/frame')
109
+ def get_frame():
110
+ """Return rotating checkerboard"""
111
+ with shared['lock']:
112
+ # Create checkerboard with rotation
113
+ pixels = shared['pixels']
114
+
115
+ # Update rotation
116
+ shared['rotation'] = (shared['rotation'] + 1.0) % 360.0
117
+ rotation_rad = np.radians(shared['rotation'])
118
+
119
+ # Center coordinates
120
+ center_x = WIDTH / 2
121
+ center_y = HEIGHT / 2
122
+
123
+ # Create coordinate grids
124
+ y_coords, x_coords = np.mgrid[:HEIGHT, :WIDTH]
125
+
126
+ # Translate to center
127
+ x_rel = x_coords - center_x
128
+ y_rel = y_coords - center_y
129
+
130
+ # Apply rotation
131
+ x_rot = x_rel * np.cos(rotation_rad) - y_rel * np.sin(rotation_rad)
132
+ y_rot = x_rel * np.sin(rotation_rad) + y_rel * np.cos(rotation_rad)
133
+
134
+ # Translate back
135
+ x_rot += center_x
136
+ y_rot += center_y
137
+
138
+ # Create rotated checkerboard
139
+ tile_x = (x_rot // TILE_SIZE).astype(int)
140
+ tile_y = (y_rot // TILE_SIZE).astype(int)
141
+
142
+ # Checker pattern
143
+ checker = ((tile_x + tile_y) % 2 == 0)
144
+
145
+ # Set colors
146
+ pixels[:, :] = [30, 30, 60] # Dark blue background
147
+
148
+ # White tiles
149
+ pixels[checker, 0] = 255 # R
150
+ pixels[checker, 1] = 255 # G
151
+ pixels[checker, 2] = 255 # B
152
+
153
+ # Black tiles already set
154
+
155
+ # Add a colored rotating dot
156
+ dot_x = int(center_x + np.cos(rotation_rad * 2) * 100)
157
+ dot_y = int(center_y + np.sin(rotation_rad * 2) * 80)
158
+
159
+ # Draw dot
160
+ dot_mask = ((x_coords - dot_x)**2 + (y_coords - dot_y)**2) < 15**2
161
+ pixels[dot_mask, 0] = 255 # Red
162
+ pixels[dot_mask, 1] = 100 # Green
163
+ pixels[dot_mask, 2] = 100 # Blue
164
+
165
+ # Add frame counter text (simulated with colored pixels)
166
+ frame_num = shared['frame_count']
167
+ if frame_num < 100: # Simple text in top-left
168
+ for i in range(min(10, len(str(frame_num)))):
169
+ digit = int(str(frame_num)[i]) if i < len(str(frame_num)) else 0
170
+ for dy in range(7):
171
+ for dx in range(5):
172
+ if 10 + dy*10 < HEIGHT and 10 + i*30 + dx*5 < WIDTH:
173
+ pixels[10 + dy*10, 10 + i*30 + dx*5] = [100 + digit*15, 200, 100]
174
+
175
+ # Convert to bytes
176
+ pixel_bytes = pixels.tobytes()
177
+ shared['frame_count'] += 1
178
+
179
+ print(f"πŸ“€ Frame {shared['frame_count']}, Rotation: {shared['rotation']:.1f}Β°")
180
+
181
+ return Response(
182
+ pixel_bytes,
183
+ mimetype='application/octet-stream',
184
+ headers={'Cache-Control': 'no-cache'}
185
+ )
186
+
187
+ def update_pattern():
188
+ """Thread that continuously updates the pattern"""
189
+ while shared['streaming']:
190
+ # The pattern is generated on-demand in /frame endpoint
191
+ # So we just sleep here
192
+ time.sleep(0.01)
193
+
194
+ def main():
195
+ print("="*60)
196
+ print("πŸ”„ Rotating Checkerboard Server")
197
+ print("="*60)
198
+ print(f"πŸ“‘ Port: {PORT}")
199
+ print(f"🎨 Resolution: {WIDTH}x{HEIGHT}")
200
+ print(f"🧱 Tile size: {TILE_SIZE}")
201
+ print("="*60)
202
+
203
+ # Start update thread
204
+ update_thread = threading.Thread(target=update_pattern, daemon=True)
205
+ update_thread.start()
206
+
207
+ # Start Flask
208
+ app.run(host='0.0.0.0', port=PORT, threaded=True, use_reloader=False)
209
+
210
+ if __name__ == "__main__":
211
+ main()