Machine Skill
Du baust eine Maschine so, wie ein Produktstudio sie fotografiert und ein Ingenieur sie erklärt: das echte Objekt in Originalgröße, mit echten Materialien, auf einer sauberen Studiobühne, betrieben mit echten Gleichungen. Eine Zeile Eingabe, eine HTML-Datei Ausgabe und ein Film in einem Befehl.
Die Ausgabe ist immer:
<name>.html: eine einzige eigenständige Seite (three.js 0.170 von jsDelivr, das Kit inline eingefügt). Sie öffnet sich von der Festplatte, von jedem statischen Host oder in einem iframe.- Ein Film:
node scripts/record.mjs <name>.html out/<name>.mp4 --music freepd:<Kategorie>/<Name>.
Öffne zuerst examples/. Jedes Beispiel wurde aus der einen Zeile am oberen Rand seiner Seite erstellt. examples/turbofan.html ist die Referenz: lies sie vollständig durch, bevor du etwas schreibst.
Der Anspruch
Stell dir den Rahmen vor, den ein Zuschauer nach drei Sekunden sieht. Er muss bereits wie ein Studio-Produktfoto der echten Maschine aussehen: korrekte Silhouette und Proportionen, Metall, das wie Metall aussieht, ein weicher Schatten auf einem Papierboden, nichts schwebt, nichts schneidet, nichts ist Platzhalter. Dann bewegt es sich, und jede Zahl auf dem Bildschirm wird berechnet.
Ein Ergebnis ist nicht fertig, solange eines dieser Kriterien zutrifft:
- ein Teil, das ein Mechaniker erwarten würde, fehlt (keine Brennstoffdüsen in einer Brennkammer, keine Hemmung in einer Uhr, keine Ventile in einem Motor)
- etwas schwebt, durchdringt sich oder hat eine sichtbare Naht oder Lücke an einer Verbindung
- ein Material sieht aus wie Plastik, obwohl es Metall sein sollte, oder alles ist einheitlich grau
- eine Zahl auf dem Bildschirm ist eingegeben statt vom Solver berechnet
- die Schnittansicht zeigt hohle Schalen anstelle von soliden Schnittflächen
- der erste Bild des Films ist nicht schön
Arbeitsablauf
1. Recherchiere die reale Maschine (5 Minuten, kein Code)
Schreibe in einem Kommentarblock am Anfang des Maschinen-Skripts aus deinem Wissen (und dem Internet, falls vorhanden) Folgendes auf:
- Die realen Maße in Metern der 5 bis 10 Teile, die die Silhouette definieren (ein Turbofan: Lüfterdurchmesser 2,85 m, Länge 4,9 m, Gondel max. Durchmesser 3,44 m).
- Die Teileliste in Montagereihenfolge, jedes mit seinem Material. Ziel: 15 bis 40 benannte Teile; wiederholte Teile (Schaufeln, Zähne, Schrauben, Spulen) zählen einzeln.
- Die Physik, die es zum Laufen bringt, als 3 bis 6 Gleichungen, die du tatsächlich berechnen wirst, mit reellen Konstanten und dem Betriebspunkt (ein Turbofan: Brayton-Kreisprozess, Start bei N1 100 %, OPR 42, T4 1.750 K, etwa 330 kN).
- Die eine Zahl, die einen Zuschauer interessiert: der Held (Schub, Drehmoment, Schläge pro Stunde, Durchfluss, Leistung). Sie bekommt eine große Serifen-Anzeige und einen schmalen Balken.
- Der Eingabewert, den ein Zuschauer steuern möchte: ein Schieberegler (Schub, Windgeschwindigkeit, Drehzahl, Pumpendrehzahl, Mahlgrad).
- Der Bruch: das Versagen, vor dem sich ein Ingenieur fürchtet, und was der Solver als Folge berechnet (Abgebrochenes Fanblatt und die Unwuchtbelastung, Ventilflattern oberhalb der Drehzahlgrenze, eine blockierte Pumpe, Überdruck). Dies muss eine berechnete Konsequenz sein, keine Animation.
2. Erstelle die Seite aus der Vorlage
Kopiere kit/template.html nach <name>.html. Sie enthält die Import-Map (festgelegt auf [email protected]), die Schriftarten, einen markierten Block, in den das Kit eingefügt wird, und die vier Abschnitte, die du ausfüllst:
const { THREE, Lab, MAT, GEO, heatGlow, clamp, lerp, approach, fmt } = window.LabKit
const lab = new Lab({ prompt, no, title, sub, size, view, cut }) // 1. das Lab
// 2. die Teile lab.mesh(GEO..., MAT..., { name, pos, explode, xray, count })
// 3. der Solver lab.hero(...); lab.slider(...); lab.onTick((dt, lab) => { ... lab.setHero(...) })
// 4. Bruch, erklären, Film lab.breakIt(...); lab.how(...); lab.autoFilm(...); lab.start()
Führe direkt nach dem Kopieren node <skill dir>/scripts/inline.mjs <name>.html aus: Es fügt das Kit in den markierten Block ein, sodass die Seite eine eigenständige Datei ist, wo immer sie sich befindet (die Vorlage src="../kit/lab-kit.js" wird nur innerhalb dieses Repos aufgelöst). Schreibe dann deine Maschine im zweiten Skriptblock.
3. Geometrie: reale Volumenkörper in Originalgröße
Arbeite in Metern im eigenen Koordinatensystem der Maschine, Y nach oben, der Boden bei y = 0, es sei denn, du setzt ground. Setze size auf die längste reale Abmessung; das Kit skaliert die Anzeige so, dass jede Maschine gleich auf der Bühne sitzt, und der Maßstabsbalken zeigt die echte Größe.
- Runde Teile (Wellen, Scheiben, Gehäuse, Düsen, Kegel, Spinner, Kolben, Lünetten):
GEO.lathe([[r, y], ...])mit einem GESCHLOSSENEN Profil. Wände:GEO.shell(profil, dicke), Rohre:GEO.tube(rAußen, rInnen, h), abgeschrägte Scheiben:GEO.disc(r, h). Ecken bleiben scharf und Kurven werden automatisch glatt schattiert. - Schaufeln und Leitschaufeln:
GEO.rotor({ count, axis, r0, r1, chord, thick, camber, twist, pitch, sweep })baut echte gewölbte, verwundene Tragflächen (NACA-Dicke) als geschlossene Volumenkörper und ringt sie. Verwende[root, tip]-Arrays für chord, thick und camber. - Zahnräder:
GEO.gear({ teeth, module, width, bore, helix, spokes, rim }). Setze zwei Zahnräder im Achsabstand(z1 + z2) * module / 2und drehe sie im umgekehrten Zähnezahlverhältnis, um eine halbe Zahnbreite versetzt. - Rohre, Schläuche, Kabel:
GEO.pipe(points, r)(geschlossen, gedeckelt). Verlege sie entlang der Struktur von Anschluss zu Anschluss, niemals durch ein Teil hindurch. - Verbindungselemente:
GEO.bolts(n, R, size, h)an jedem Flansch. Federn:GEO.spring(R, wire, windungen, länge). Boxen:GEO.rbox(b, h, t, r)(immer abgerundet, niemals scharf). Platten mit Löchern:GEO.extrude(shape, tiefe). - Massen und Trägheiten ergeben sich aus der Geometrie:
GEO.volume(geo)liefert Volumen und Schwerpunkt, multipliziere mit der realen Dichte.
Regeln, die es sauber halten:
- Jedes Teil ist ein geschlossener Volumenkörper (Lathe-Profile geschlossen, Schalen mit Dicke, Rohre gedeckelt). Nur so kann die Schnittansicht solide Schnittflächen zeichnen. Offene Oberflächen erscheinen beim Schneiden hohl.
- Nichts durchdringt sich sichtbar und nichts schwebt. Jedes Teil berührt das, was es hält (ein Flansch sitzt auf einer Fläche, eine Welle läuft in einem Lager, ein Pfosten erreicht den Boden). Überprüfe Verbindungen in Nahaufnahmen.
- Detail, wo das Auge hinblickt: Schraubenkreise an Flanschen, Fasen, eine Lippe, ein Typenschild, Kraftstoffleitungen, ein Kabelbaum. Große ebene Flächen erhalten eine Naht, eine Bedienfeldlinie oder eine Textur-Oberfläche.
- Gib der Maschine eine Basis: einen Ständer, eine Wiege, einen Sockel, eine Werkbank oder eine Halterung mit Füßen auf dem Boden. Maschinen schweben im echten Leben selten.
- Budget: bis zu etwa 700k Dreiecke und 200 Meshes. Fasse wiederholte statische Teile mit
GEO.merge/GEO.ringzusammen; halte bewegliche Gruppen getrennt.
4. Materialien: niemals nur ein Grau
Verwende MAT.*-Voreinstellungen; sie enthalten gemessene Reflexionsgrade, sodass Stahl, Titan, Aluminium, Messing und Kupfer nebeneinander unterschiedlich aussehen. Jede Voreinstellung akzeptiert Überschreibungen (color, roughness, clearcoat, jedes MeshPhysicalMaterial-Feld) und eine Oberfläche (Mikrorauigkeits-Textur: turned Ringe auf Drehteilen, brushed Streifen, cast Flecken, smudge, knurl).
| Verwendung | Voreinstellung |
|---|---|
| Wellen, Scheiben, bearbeitete Teile | MAT.steel, MAT.stainless, MAT.chrome |
| Gehäuse, Gussteile | MAT.aluminium, MAT.castIron, MAT.steelDark |
| Schaufeln, Luft- und Raumfahrt | MAT.titanium, MAT.inconel (+ heatGlow) |
| Beschläge, Verkabelung | MAT.brass, MAT.copper, MAT.gold |
| Lackierte Karosserien | MAT.paint({ color }), MAT.satin, MAT.anodised({ color }) |
| Polymere | MAT.plastic, MAT.rubber, MAT.carbon |
| Fenster, Kristalle | MAT.glass, MAT.sapphire, MAT.ruby, MAT.enamel |
| Licht, Flamme, heißes Gas | MAT.glow, heatGlow(material, kelvin) |
Wähle eine Palette wie ein Industriedesigner: hauptsächlich Metalle und ein oder zwei lackierte Farben, höchstens ein Akzent. Heiße Teile glühen von der Temperatur des Solvers (material.userData.heat.value = T).
5. Der Solver: reelle Zahlen oder nichts
Schreibe eine kleine reine Funktion von der Eingabe und dem Zustand zu jeder Zahl, die du anzeigst, mit reellen Einheiten und reellen Konstanten. Integriere Dynamiken mit approach (Verzögerung erster Ordnung: Hochlauf, thermische Masse) oder rk4 (Federn, Hemmungen, Pendel). Treibe die Bewegung daraus ab: eine Drehzahl vom Solver dreht die Welle, ein Durchfluss vom Solver bewegt die Partikel, eine Temperatur erhitzt das Metall.
- Ein Held:
lab.hero({ label, unit, max })dannlab.setHero(wert, { state, sub, bad })jeden Tick.statebesteht aus zwei oder drei Wörtern (Hochlauf, Start, gedrosselter Durchfluss).subist eine Zeile mit 2 oder 3 weiteren Zahlen. - Ein Schieberegler:
lab.slider({ id, label, min, max, value, unit, ends }). Ein zweiter nur, wenn die Maschine ihn wirklich braucht. - Labels hängen an statischen Teilen an Punkten auf den Schnittebenen:
lab.label(obj, 'HD-Turbine', { at: [x, y, z], value: () => '12.979 U/min' }). lab.how(words, equations)füllt „Wie es berechnet wird“: das Modell in einfachen Worten und die Gleichungen wie im Code geschrieben. Sage, was vereinfacht wurde.- Wenn eine Bewegung zu schnell zum Sehen ist (ein Fan mit 2.700 U/min), zeige sie verlangsamt und sage dies in
how(„Drehung 1:60 dargestellt“). Verlangsame niemals die Zahlen. - Überprüfe den Betriebspunkt gegen den öffentlichen Wert, den du in Schritt 1 notiert hast. Wenn du um mehr als etwa 20 % abweichst, korrigiere das Modell, nicht die Zahl.
6. Strömung, Wärme und Effekte
lab.flow({ paths, count, length, color, parent }): Streifen, die entlang von Pfaden reiten, die du aus dem realen Strömungspfad baust (Luft durch einen Kanal, Blut durch eine Pumpe, Wasser durch einen Puck). Setze.speedvom Solver und.opacityvonlab.state.cut, sodass sie in der Schnittansicht sichtbar sind. Färbe nach einer berechneten Größe (Temperatur, Druck).lab.plume({ radius, length, dir, color: 'rocket' | 'jet', shocks }): ein Abgasstrahl mit Mach-Diamanten; setze.powervom Solver.lab.sparks.emit(weltPos, n, geschwindigkeit)undlab.shed(mesh, v, w)für Brüche: Funken fallen mit Schwerkraft und prallen ab; ein abgetrenntes Teil wird zu einem starren Körper, der auf dem Boden landet.
7. Modi (das Kit erledigt sie; du richtest sie ein)
- Explosion: Jeder
lab.add/lab.meshakzeptiertexplode: [dx, dy, dz]im Rahmen des Elternobjekts (in Metern). Bewege Teile entlang ihrer Montageachse, äußere Hüllen am weitesten, und behalte den Kern an Ort und Stelle. Nichts sollte sich auf dem Weg nach draußen kreuzen. - Schnittansicht:
cut: { a, b, center, reach }auf dem Lab.aundbsind die Richtungen des entfernten Bereichs:a = [0,0,1], b = [0,1,0]entfernt das Viertel in Richtung des Betrachters und nach oben (am besten für Teile mit einer Achse);b = aentfernt eine Hälfte. Schnittflächen werden automatisch für jeden geschlossenen Volumenkörper mit einer feinen Schraffur gezeichnet. - Röntgen:
xray: 'shell'auf äußeren Gehäusen macht sie zu Glas mit feinen Kanten, sodass der Kern während des Betriebs sichtbar bleibt. - Zeitlupe, Beschriftungen, Pause sind kostenlos (Tasten S, L, Leertaste). Die interaktiven Teile sind Maus-Orbit, der Schieberegler und die Schaltflächen.
- Break it:
lab.breakIt({ label, run(lab), reset(lab) }).runändert den Solver-Zustand (ein Blatt gelöst, ein Ventil klemmt, eine Dichtung geplatzt) und der Solver berechnet die Konsequenz. Zeige es im Helden (bad: true) und mit Funken, Trümmern oder Erschütterung.
8. Betrachte es wie ein Kritiker (erforderlich)
Rendere und LESE jedes Bild, das du erstellst. Verwende die Skripte:
node scripts/shot.mjs <name>.html out/review.png # der erste Blick
node scripts/shot.mjs <name>.html out/cut.png --js "lab.toggle('cut', true); lab.toggle('labels', true)"
node scripts/shot.mjs <name>.html out/near.png --js "lab.setView({ az: 70, el: 8, dist: 0.8, target: [x, y, z] })"
node scripts/shot.mjs <name>.html out/film --film 1,4,8,12,17,22,26 # Film-Stills
Mache mindestens vier Überprüfungsrunden. In jeder: Gesamtansicht, Schnittansicht mit Beschriftungen, Explosionsansicht, zwei Nahaufnahmen von Verbindungen und die Film-Stills. Vergleiche mit echten Produktfotos der Maschine in deiner Erinnerung: Proportionen, Teileanzahl, Materialien, wie Licht darauf fällt. Liste auf, was falsch ist, korrigiere es, rendere erneut. Höre erst auf, wenn die Liste leer ist. Das Shot-Skript gibt fps, Teileanzahl und Dreiecke aus; halte interaktive Bildraten über 30.
9. Der Film
lab.autoFilm({ runBeat, runSub, breakBeat, breakSub, sliderTo }) schreibt die standardmäßige 28-Sekunden-Geschichte aus deiner Maschine, in nummerierten Beats mit den redaktionellen Untertiteln:
- Ein Satz ist die gesamte Kurzbeschreibung (die Eingabezeile tippt sich selbst ein).
- Claude baut die Maschine (Teile fliegen in Montagereihenfolge ein, während der Zähler steigt: „84 von 3.597 Teilen“).
- Sie läuft (ein Cursor zieht den Schieberegler; die Heldenzahl steigt vom Solver).
- Schneide sie auf (Schnittkappen, Beschriftungen mit Live-Werten).
- Nimm sie auseinander (Explosionsansicht).
- Zerstöre sie (der Cursor klickt auf die Break-Schaltfläche; der Solver entscheidet, was versagt).
Es wiederholt sich: Die letzte Kamera-Position kehrt zur ersten zurück. Für einen benutzerdefinierten Film verwende lab.film('name', { len, assemble, shots, events }): Shots sind Kamera-Positionen { t, az, el, dist, target, fov }, Events sind { t, beat: ['07', 'Fetter Satz.', 'Grauer Satz.'], set: { cut: 1 }, slider: { id, to, dur }, cursor: { to: 'slider:id:0.8' | 'button:break' | [x, y], click: true }, do(lab) {} }.
Aufnahme (benötigt Node 18+, Playwright mit Chromium, ffmpeg):
node scripts/record.mjs <name>.html out/<name>.mp4 --music freepd:Classical/Easy_Lemon
node scripts/record.mjs <name>.html out/<name>-portrait.mp4 --w 1080 --h 1350
Die Seite läuft im Filmmodus (?film=1): Ihre Uhr bewegt sich nur, wenn der Rekorder sie schrittweise weiterschaltet, sodass jedes Bild vollständig und der Film bei jeder Aufnahme identisch ist. Der Rekorder schützt jeden Screenshot vor den magentafarbenen Bildern, die ein hängengebliebener GPU im Headless-Modus zurückgeben kann, und scannt den fertigen Film; er beendet sich mit einem Fehler, wenn ein Bild rosa ist. Musik von FreePD ist CC0; nenne den Track trotzdem.
Schreiben auf dem Bildschirm
Die Seite ist redaktionell, kein Dashboard: eine Eingabezeile, ein Serifen-Titel mit einem kursiven Untertitel, eine riesige Zahl mit kursiven Einheiten und einem schmalen Balken, eine Zeile mit unterstützenden Zahlen, ein Schieberegler. Schreibe wie eine gute Bildunterschrift: kurz, konkret, ohne Übertreibung, reale Einheiten mit Leerzeichen (338 kN, 1.750 K, 4.2 L/min), Tausender getrennt. Beat-Untertitel bestehen aus einem fetten Satz und einem grauen Satz.
Was du sagen solltest, wenn du es übergibst
Gib den Pfad der HTML-Datei an, den Filmbefehl, den Betriebspunkt, den der Solver im Vergleich zum öffentlichen Wert erreicht, und alles, was du vereinfacht hast. Biete die nächste Maschine an.