// codescreen.js — zeigt Code als Live-Textur auf einer Bildschirmfläche in der 3D-Welt. // Dient (a) dem eigenen Monitor und (b) später den Monitoren anderer ("über die Schulter schauen"). // Bewusst als Textur (nicht CSS3D): echte 3D-Geometrie -> korrekte Verdeckung, scharf, performant. import * as THREE from 'three'; const W = 1024, H = 640; // Textur-Auflösung const PAD = 22, LINE = 27; // Innenabstand / Zeilenhöhe in px const FONT_PX = 22; const MAX_COLS = 54, MAX_LINES = 20; export class CodeScreen { /** @param {number} width - Breite der Fläche in Metern (Höhe folgt dem Seitenverhältnis). */ constructor(width = 2.0) { this.canvas = document.createElement('canvas'); this.canvas.width = W; this.canvas.height = H; this.ctx = this.canvas.getContext('2d'); this.tex = new THREE.CanvasTexture(this.canvas); this.tex.colorSpace = THREE.SRGBColorSpace; // MeshBasicMaterial = unbeleuchtet -> Bildschirm leuchtet gleichmäßig, immer lesbar. const geo = new THREE.PlaneGeometry(width, width * H / W); this.mesh = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({ map: this.tex })); this._title = 'Monitor'; this.setCode(''); } setTitle(t) { this._title = t; } /** Zeichnet den Code neu auf die Textur (nur bei Änderung aufrufen, nicht pro Frame). */ setCode(text) { const g = this.ctx; g.fillStyle = '#0b1020'; g.fillRect(0, 0, W, H); g.strokeStyle = 'rgba(137,180,250,.45)'; g.lineWidth = 3; g.strokeRect(4, 4, W - 8, H - 8); // Kopfzeile g.fillStyle = '#11172a'; g.fillRect(6, 6, W - 12, 40); g.fillStyle = '#89b4fa'; g.font = `bold 22px ui-monospace, monospace`; g.fillText('💻 ' + this._title, PAD, 34); // Code g.fillStyle = '#dfe4f5'; g.font = `${FONT_PX}px ui-monospace, "JetBrains Mono", monospace`; const lines = String(text).replace(/\t/g, ' ').split('\n'); const shown = lines.slice(0, MAX_LINES); let y = 46 + LINE; for (const ln of shown) { g.fillText(ln.slice(0, MAX_COLS), PAD, y); y += LINE; } if (lines.length > MAX_LINES) { g.fillStyle = '#6c7086'; g.fillText(`… +${lines.length - MAX_LINES} Zeilen`, PAD, y); } this.tex.needsUpdate = true; } dispose() { this.tex.dispose(); this.mesh.geometry.dispose(); this.mesh.material.dispose(); } }