Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 615 lines 18 kB view raw view rendered
1# Robo System Plan 2 3**Date:** 2025.09.16 4**Purpose:** Create an automated brush system that can robotically execute brushes in non-interactive mode 5 6## Overview 7 8The `robo` command will create an automated drawing system that can import and execute existing brush files (like `box.mjs`) in a programmatic, non-interactive mode. This creates a "plotter-like" system that can generate procedural art using the existing brush ecosystem. 9 10## Core Concept 11 12``` 13robo box # Load box.mjs brush and run automatically 14robo box fade:red-blue # Load box.mjs with color parameters 15robo circle count:50 # Load circle.mjs and draw 50 circles 16``` 17 18## Architecture 19 20### 1. Robo Piece Structure (`robo.mjs`) 21 22```javascript 23// robo.mjs - Main robo system piece 24function boot({ params, colon }) { 25 const brushName = params[0]; // e.g., "box" 26 const brushParams = params.slice(1); // e.g., ["fade:red-blue"] 27 const robotSettings = parseColon(colon); // e.g., speed:2, count:10 28} 29 30async function paint({ frame }) { 31 // Execute frame-by-frame robotic drawing 32 if (loadedBrush && robotState.active) { 33 executeRoboticFrame(frame); 34 } 35} 36``` 37 38### 2. Dynamic Brush Loading 39 40Based on research of `disk.mjs` loading mechanisms: 41 42```javascript 43// Inside robo.mjs boot function 44async function loadBrush(brushName) { 45 try { 46 const brushModule = await import(`../disks/${brushName}.mjs`); 47 return { 48 overlay: brushModule.overlay, 49 lift: brushModule.lift, 50 brush: brushModule.brush, // Legacy support 51 boot: brushModule.boot 52 }; 53 } catch (err) { 54 console.error(`Failed to load brush: ${brushName}`, err); 55 return null; 56 } 57} 58``` 59 60### 3. Path Generation System 61 62The robo system needs to generate synthetic path data that mimics user interaction: 63 64```javascript 65class RoboPathGenerator { 66 constructor() { 67 this.currentPath = []; 68 this.pathQueue = []; 69 } 70 71 // Generate geometric patterns 72 generateGrid(rows, cols, spacing) { 73 for (let row = 0; row < rows; row++) { 74 for (let col = 0; col < cols; col++) { 75 this.pathQueue.push(this.createBoxPath( 76 col * spacing, 77 row * spacing, 78 spacing * 0.8, 79 spacing * 0.8 80 )); 81 } 82 } 83 } 84 85 generateSpiral(centerX, centerY, radius, steps) { 86 // Create spiral path 87 } 88 89 generateRandom(count, bounds) { 90 // Create random placement paths 91 } 92 93 createBoxPath(x, y, w, h) { 94 return { 95 type: 'drag', 96 startPoint: { x, y }, 97 endPoint: { x: x + w, y: y + h }, 98 duration: 60 // frames 99 }; 100 } 101} 102``` 103 104### 4. Brush Execution Engine 105 106Mock the nopaint system's brush execution: 107 108```javascript 109class RoboBrushExecutor { 110 constructor(brushModule, screen) { 111 this.brush = brushModule; 112 this.screen = screen; 113 this.mockSystem = this.createMockNopaintSystem(); 114 } 115 116 createMockNopaintSystem() { 117 return { 118 nopaint: { 119 color: [255, 0, 0, 255], // Default red 120 brush: null, 121 finalDragBox: null, 122 buffer: null // Will be created 123 } 124 }; 125 } 126 127 executePath(path, frame) { 128 const progress = frame / path.duration; 129 const currentBox = this.interpolateDragBox(path, progress); 130 131 // Mock the overlay call during drawing 132 if (progress < 1.0) { 133 this.mockSystem.nopaint.brush = { dragBox: currentBox }; 134 this.callBrushFunction('overlay', { 135 mark: currentBox, 136 color: this.mockSystem.nopaint.color, 137 ink: this.createMockInk() 138 }); 139 } 140 141 // Mock the lift call at completion 142 if (progress >= 1.0) { 143 this.mockSystem.nopaint.finalDragBox = currentBox; 144 this.callBrushFunction('lift', { 145 mark: currentBox, 146 color: this.mockSystem.nopaint.color, 147 ink: this.createMockInk() 148 }); 149 return true; // Path complete 150 } 151 152 return false; // Path in progress 153 } 154 155 interpolateDragBox(path, progress) { 156 const { startPoint, endPoint } = path; 157 const currentEnd = { 158 x: startPoint.x + (endPoint.x - startPoint.x) * progress, 159 y: startPoint.y + (endPoint.y - startPoint.y) * progress 160 }; 161 162 return { 163 x: startPoint.x, 164 y: startPoint.y, 165 w: currentEnd.x - startPoint.x, 166 h: currentEnd.y - startPoint.y 167 }; 168 } 169 170 callBrushFunction(functionName, api) { 171 if (this.brush[functionName]) { 172 this.brush[functionName](api); 173 } 174 } 175 176 createMockInk() { 177 // Return ink function that interfaces with the robo rendering system 178 return (color) => ({ 179 box: (mark, mode = 'fill') => { 180 this.renderBox(mark, color, mode); 181 return this; // Chainable 182 } 183 }); 184 } 185} 186``` 187 188### 5. Robo Timing System with `sim` Function 189 190Based on AC disk API research, use the `sim` function for precise timing control: 191 192```javascript 193// In robo.mjs - Main timing controller 194let robotState = { 195 active: false, 196 currentPath: null, 197 pathQueue: [], 198 completedPaths: [], 199 frameCounter: 0, 200 speed: 1.0, 201 pattern: 'grid', 202 brushName: null 203}; 204 205// 🧮 Sim (Runs once per logic frame at 120fps locked) 206function sim({ simCount, needsPaint }) { 207 if (!robotState.active) return; 208 209 // Speed control: advance robot logic every N sim frames 210 const speedFrames = Math.max(1, Math.round(120 / (60 * robotState.speed))); 211 212 if (simCount % BigInt(speedFrames) === 0n) { 213 advanceRobotLogic(); 214 needsPaint(); // Request paint update when robot state changes 215 } 216 217 // Update debug panel state 218 debugPanel.update(robotState); 219} 220 221function advanceRobotLogic() { 222 if (!robotState.currentPath && robotState.pathQueue.length > 0) { 223 // Start next path 224 robotState.currentPath = robotState.pathQueue.shift(); 225 robotState.frameCounter = 0; 226 } 227 228 if (robotState.currentPath) { 229 robotState.frameCounter++; 230 231 // Calculate progress through current path 232 const progress = robotState.frameCounter / robotState.currentPath.duration; 233 234 if (progress >= 1.0) { 235 // Path complete - trigger lift 236 executePathCompletion(robotState.currentPath); 237 robotState.completedPaths.push(robotState.currentPath); 238 robotState.currentPath = null; 239 robotState.frameCounter = 0; 240 } else { 241 // Path in progress - trigger overlay 242 executePathProgress(robotState.currentPath, progress); 243 } 244 } 245 246 // Check if all paths complete 247 if (!robotState.currentPath && robotState.pathQueue.length === 0) { 248 robotState.active = false; 249 console.log("🤖 Robot drawing complete!"); 250 } 251} 252``` 253 254**Key `sim` Function Benefits:** 255- **120fps locked timing** - Consistent logic updates regardless of display refresh rate 256- **`simCount`** - BigInt counter for precise frame tracking 257- **`needsPaint()`** - Request paint updates only when robot state changes (performance) 258- **Speed control** - Use modulo with simCount to control robot execution speed 259- **Frame precision** - Perfect for smooth path interpolation and timing control 260 261Support various robotic drawing modes: 262 263```javascript 264const ROBO_MODES = { 265 // Grid patterns 266 'grid': { rows: 10, cols: 10, spacing: 50 }, 267 'grid:5x3': { rows: 5, cols: 3, spacing: 80 }, 268 269 // Organic patterns 270 'random': { count: 20, bounds: 'screen' }, 271 'spiral': { center: 'screen', radius: 200, steps: 50 }, 272 273 // Animation patterns 274 'wave': { amplitude: 100, frequency: 0.1 }, 275 'orbit': { center: 'screen', radius: 150 } 276}; 277 278// Example: robo box grid:5x3 fade:red-blue speed:2 279``` 280 281### 6. Time Control 282 283```javascript 284class RoboTimeController { 285 constructor() { 286 this.speed = 1.0; // 1.0 = normal speed 287 this.pauseRequested = false; 288 this.stepMode = false; 289 } 290 291 // Speed control: robo box speed:0.5 (half speed) 292 setSpeed(multiplier) { 293 this.speed = Math.max(0.1, Math.min(10.0, multiplier)); 294 } 295 296 // Step through frame by frame: robo box step 297 enableStepMode() { 298 this.stepMode = true; 299 this.speed = 0; 300 } 301 302 shouldAdvanceFrame() { 303 return !this.pauseRequested && (this.speed > 0 || this.stepMode); 304 } 305} 306``` 307 308### 7. Virtual Orange Robot Cursor 309 310Add a CSS virtual cursor that shows where the robot is currently drawing: 311 312```javascript 313// Virtual cursor management 314function updateVirtualCursor(x, y, isDrawing = false) { 315 let cursor = document.getElementById('robo-virtual-cursor'); 316 317 if (!cursor) { 318 cursor = document.createElement('div'); 319 cursor.id = 'robo-virtual-cursor'; 320 cursor.style.cssText = ` 321 position: absolute; 322 width: 12px; 323 height: 12px; 324 background: orange; 325 border: 2px solid white; 326 border-radius: 50%; 327 pointer-events: none; 328 z-index: 10000; 329 box-shadow: 0 0 4px rgba(255, 165, 0, 0.6); 330 transition: all 0.1s ease; 331 ${isDrawing ? 'transform: scale(1.2);' : ''} 332 `; 333 document.body.appendChild(cursor); 334 } 335 336 // Position cursor at robot drawing location 337 cursor.style.left = `${x - 6}px`; 338 cursor.style.top = `${y - 6}px`; 339 cursor.style.opacity = robotState.active ? '1' : '0'; 340 341 // Scale effect when drawing 342 cursor.style.transform = isDrawing ? 'scale(1.2)' : 'scale(1.0)'; 343} 344 345function removeVirtualCursor() { 346 const cursor = document.getElementById('robo-virtual-cursor'); 347 if (cursor) cursor.remove(); 348} 349``` 350 351**Virtual Cursor Features:** 352- **Orange robot dot** - Distinct from regular UI cursors 353- **Real-time position** - Shows exact robot drawing location 354- **Drawing state feedback** - Scales up when actively drawing 355- **Always visible** - Appears even when browser cursor is elsewhere 356- **Smooth movement** - CSS transitions for fluid robot motion 357 358Similar to the nopaint performance overlay, create a visual debug panel: 359 360```javascript 361class RoboDebugPanel { 362 constructor() { 363 this.visible = true; 364 this.position = { x: 10, y: 10 }; 365 this.size = { w: 300, h: 200 }; 366 } 367 368 render({ ink, write, screen }) { 369 if (!this.visible) return; 370 371 const { x, y } = this.position; 372 const { w, h } = this.size; 373 374 // Semi-transparent background panel 375 ink(0, 0, 0, 180).box(x, y, w, h); 376 ink(255, 255, 255, 80).box(x, y, w, h, "outline"); 377 378 // Title 379 ink("cyan").write("🤖 ROBO DEBUG", x + 8, y + 16); 380 381 // Current state info 382 let lineY = y + 32; 383 const lineHeight = 12; 384 385 ink("white").write(`Brush: ${this.currentBrush || 'none'}`, x + 8, lineY); 386 lineY += lineHeight; 387 388 ink("yellow").write(`Pattern: ${this.currentPattern || 'none'}`, x + 8, lineY); 389 lineY += lineHeight; 390 391 ink("lime").write(`Speed: ${this.speed.toFixed(1)}x`, x + 8, lineY); 392 lineY += lineHeight; 393 394 ink("orange").write(`Frame: ${this.currentFrame}/${this.totalFrames}`, x + 8, lineY); 395 lineY += lineHeight; 396 397 // Progress bar 398 const progressW = w - 16; 399 const progress = this.totalFrames > 0 ? this.currentFrame / this.totalFrames : 0; 400 ink(50, 50, 50).box(x + 8, lineY, progressW, 8); 401 ink("cyan").box(x + 8, lineY, progressW * progress, 8); 402 lineY += 16; 403 404 // Current path info 405 if (this.currentPath) { 406 ink("magenta").write(`Path: ${this.currentPath.type}`, x + 8, lineY); 407 lineY += lineHeight; 408 409 ink("gray").write(`Start: ${this.currentPath.startPoint.x}, ${this.currentPath.startPoint.y}`, x + 8, lineY); 410 lineY += lineHeight; 411 412 ink("gray").write(`End: ${this.currentPath.endPoint.x}, ${this.currentPath.endPoint.y}`, x + 8, lineY); 413 lineY += lineHeight; 414 } 415 416 // Queue status 417 ink("white").write(`Queue: ${this.pathQueue.length} paths`, x + 8, lineY); 418 lineY += lineHeight; 419 420 // Mini grid visualization 421 this.renderMiniGrid(ink, x + 8, lineY, 100, 60); 422 } 423 424 renderMiniGrid(ink, x, y, w, h) { 425 // Background 426 ink(30, 30, 30).box(x, y, w, h); 427 ink(80, 80, 80).box(x, y, w, h, "outline"); 428 429 // Grid lines 430 const gridSize = 10; 431 ink(60, 60, 60); 432 for (let gx = gridSize; gx < w; gx += gridSize) { 433 ink().line(x + gx, y, x + gx, y + h); 434 } 435 for (let gy = gridSize; gy < h; gy += gridSize) { 436 ink().line(x, y + gy, x + w, y + gy); 437 } 438 439 // Show completed paths as green dots 440 this.completedPaths?.forEach(path => { 441 const miniX = x + (path.startPoint.x / this.screenW) * w; 442 const miniY = y + (path.startPoint.y / this.screenH) * h; 443 ink("lime").circle(miniX, miniY, 2, true); 444 }); 445 446 // Show current path as yellow 447 if (this.currentPath) { 448 const miniX = x + (this.currentPath.startPoint.x / this.screenW) * w; 449 const miniY = y + (this.currentPath.startPoint.y / this.screenH) * h; 450 ink("yellow").circle(miniX, miniY, 3, true); 451 } 452 453 // Show queued paths as gray dots 454 this.pathQueue?.forEach(path => { 455 const miniX = x + (path.startPoint.x / this.screenW) * w; 456 const miniY = y + (path.startPoint.y / this.screenH) * h; 457 ink("gray").circle(miniX, miniY, 1, true); 458 }); 459 } 460 461 update(roboState) { 462 this.currentBrush = roboState.brushName; 463 this.currentPattern = roboState.pattern; 464 this.speed = roboState.speed; 465 this.currentFrame = roboState.frame; 466 this.totalFrames = roboState.totalFrames; 467 this.currentPath = roboState.currentPath; 468 this.pathQueue = roboState.pathQueue; 469 this.completedPaths = roboState.completedPaths; 470 this.screenW = roboState.screenWidth; 471 this.screenH = roboState.screenHeight; 472 } 473 474 toggle() { 475 this.visible = !this.visible; 476 } 477} 478``` 479 480Integration in main robo piece: 481 482```javascript 483// In robo.mjs 484let debugPanel = new RoboDebugPanel(); 485 486function paint({ ink, write, screen, keyboard }) { 487 // Toggle debug panel with 'd' key 488 if (keyboard.pressed.d) { 489 debugPanel.toggle(); 490 } 491 492 // Execute robotic drawing 493 if (robotState.active) { 494 executeRoboticFrame(); 495 } 496 497 // Update and render debug panel 498 debugPanel.update(robotState); 499 debugPanel.render({ ink, write, screen }); 500} 501``` 502 503### 8. Visual Pattern Preview 504 505Show upcoming pattern visually: 506 507```javascript 508function renderPatternPreview(ink, x, y, w, h) { 509 ink(0, 0, 0, 100).box(x, y, w, h); 510 511 // Show the complete pattern as wireframe 512 pathGenerator.pathQueue.forEach((path, index) => { 513 const alpha = Math.max(50, 255 - index * 10); // Fade distant paths 514 const previewX = x + (path.startPoint.x / screen.width) * w; 515 const previewY = y + (path.startPoint.y / screen.height) * h; 516 const previewW = (path.endPoint.x - path.startPoint.x) / screen.width * w; 517 const previewH = (path.endPoint.y - path.startPoint.y) / screen.height * h; 518 519 ink(100, 100, 255, alpha).box(previewX, previewY, previewW, previewH, "outline"); 520 }); 521} 522 523## Implementation TODO 524 5251. **Create basic robo.mjs piece** (Start here!) 526 - Core structure with sim() function for 120fps logic timing 527 - Simple grid pattern generator (3x3 box grid) 528 - Robot state management with speed control 529 - Command parsing: `robo grid 3 box 0.5` (pattern, size, brush, speed) 530 - needsPaint() integration for performance 531 - Basic console logging for debug feedback 532 533### Phase 2: Path Generation 5341. Implement `RoboPathGenerator` class 5352. Add basic patterns: grid, random, spiral 5363. Create path interpolation system 5374. **Add `RoboDebugPanel` with mini-grid visualization** 5385. Test automated box drawing in patterns 539 540### Phase 3: Advanced Features 5411. Add time controls (speed, pause, step mode) 5422. Implement complex patterns (waves, orbits) 5433. Add parameter parsing for brush configuration 5444. Support fade colors and brush-specific parameters 5455. **Enhance debug panel with pattern preview and state visualization** 546 547### Phase 4: Integration & Polish 5481. Integrate with existing color system 5492. Add recording/playback capabilities 5503. Create export functionality for generated art 5514. Add real-time control interface 552 553## Technical Research Findings 554 555### Brush Loading Pattern 556- Use `await import()` for dynamic loading (found in `disk.mjs:4776`) 557- Brushes export `overlay`, `lift`, and optionally `brush` functions 558- Need to mock the nopaint system structure for brush execution 559 560### Nopaint System Structure 561- Brushes expect `mark` (dragBox coordinates) and `color` parameters 562- `overlay` function shows preview during drawing 563- `lift` function executes final drawing when stroke completes 564- System provides `ink()` function for actual rendering 565 566### Drawing Data Flow 567- User interaction creates `dragBox` coordinates 568- `dragBox` contains `{x, y, w, h}` rectangle data 569- Brushes use this data to determine what/where to draw 570- For robo mode, we synthesize this data programmatically 571 572## Example Usage 573 574```bash 575# Basic automated box drawing 576robo box 577 578# Grid of red boxes with debug panel 579robo box grid fade:red debug 580 581# Spiral pattern with debug visualization 582robo box spiral fade:blue-green speed:0.5 debug 583 584# Random placement with custom count and mini-grid view 585robo box random count:50 fade:rainbow debug 586 587# Step-through mode for debugging (press 'd' to toggle panel) 588robo box grid:3x3 step 589``` 590 591**Debug Panel Features:** 592- 🤖 Real-time robo state display 593- 📊 Progress bar and frame counter 594- 🗺️ Mini-grid showing completed/current/queued paths 595- ⏱️ Speed and timing information 596- 🎯 Current path coordinates and type 597- 👀 Pattern preview wireframe overlay 598 599## Future Extensions 600 6011. **Brush Composition**: Combine multiple brushes in sequence 6022. **Physics Simulation**: Add gravity, collision for realistic movement 6033. **AI Integration**: Use ML to generate interesting patterns 6044. **Live Coding**: Real-time pattern modification while drawing 6055. **Network Sync**: Coordinate multiple robo instances across devices 606 607## Benefits 608 6091. **Automated Art Generation**: Create complex artworks without manual drawing 6102. **Brush Testing**: Systematically test brushes across different scenarios 6113. **Pattern Libraries**: Build reusable pattern generation systems 6124. **Educational Tool**: Demonstrate brush behavior and geometric concepts 6135. **Performance Art**: Live automated drawing performances 614 615This system would essentially create a "turtle graphics" style plotter that can use any existing brush in the Aesthetic Computer ecosystem, opening up powerful possibilities for procedural art generation and automated drawing systems.