Machine Skill(マシンスキル)
マシンを構築する方法は、プロダクトスタジオが製品を撮影し、エンジニアがそれを説明する方法と同じです。つまり、実物を実寸で、実素材で、清潔なスタジオセット上で、実計算式で動作させます。1行の入力から、1つのHTMLファイルと、1つのコマンドでフィルムを出力します。
出力は常に以下の通りです。
<name>.html:単一の自己完結型ページ(Three.js 0.170.0をjsDelivrから読み込み、キットはインライン化)。ディスクから、任意の静的ホストから、またはiframe内で開きます。- フィルム:
node scripts/record.mjs <name>.html out/<name>.mp4 --music freepd:<Category>/<Name>
最初に examples/ を開いてください。すべての例は、そのページ上部の1行から作成されました。examples/turbofan.html がリファレンスです。コードを書き始める前に、最初から最後まで読んでください。
目標レベル
始める前に、視聴者が3秒後に目にするフレームを想像してください。それはすでに、実物のマシンをスタジオで撮影したプロダクト写真のように見えている必要があります。正しいシルエットとプロポーション、金属として認識される金属、ペーパーフロア上のソフトシャドウ、浮いているもの、クリッピングしているもの、仮置きのものは一切ありません。その後、動き出し、画面上のすべての数値が計算されます。
以下のいずれかに該当する場合、結果は完成とは見なされません。
- 整備士が期待する部品が欠けている(燃焼器に燃料ノズルがない、時計に脱進機がない、エンジンにバルブがないなど)
- 何かが浮いている、交差している、または接合部に目に見える継ぎ目や隙間がある
- 素材が金属であるべきなのにプラスチックのように見える、またはすべてが一色のグレーである
- 画面上の数値がソルバーによって計算されず、手入力されている
- カットアウェイが中空のシェルではなく、中実の断面を示していない
- フィルムの最初のフレームが美しくない
ワークフロー
1. 実機の調査(5分、コードは書かない)
マシンスクリプトの先頭にあるコメントブロックに、あなたの知識(および必要に応じてWeb)から、以下の項目を書き出してください。
- 実寸法(メートル単位):シルエットを定義する5~10の部品の寸法(ターボファン:ファン直径2.85 m、長さ4.9 m、ナセル最大直径3.44 m)
- 部品リスト:組み立て順、各素材付き。15~40の名前付き部品を目指します。繰り返し部品(ブレード、歯、ボルト、コイル)は個別にカウントします。
- 物理法則:実際に計算する3~6の方程式、実定数と動作点付き(ターボファン:ブレイトンサイクル、離陸時N1 100%、OPR 42、T4 1,750 K、約330 kN)
- 視聴者が気にするひとつの数値:ヒーロー(推力、トルク、毎時拍数、流量、出力)。大きなセリフ体の読み出しと細いバーで表示されます。
- 視聴者が操作したい入力:1つのスライダー(スロットル、風速、rpm、ポンプ速度、グラインドサイズ)
- 破損:エンジニアが心配する故障と、ソルバーが何を起こすか(ファンブレード脱落とその不均衡荷重、レッドラインを超えたときのバルブフロート、ポンプの失速、過圧)。これは計算結果であり、アニメーションであってはなりません。
2. テンプレートからページを構築する
kit/template.html を <name>.html にコピーします。これには、インポートマップ(fixed [email protected])、フォント、キットが配置されるマークされたブロック、および以下の4つのセクションが含まれています。
const { THREE, Lab, MAT, GEO, heatGlow, clamp, lerp, approach, fmt } = window.LabKit
const lab = new Lab({ prompt, no, title, sub, size, view, cut }) // 1. the lab
// 2. the parts lab.mesh(GEO..., MAT..., { name, pos, explode, xray, count })
// 3. the solver lab.hero(...); lab.slider(...); lab.onTick((dt, lab) => { ... lab.setHero(...) })
// 4. break, explain, film lab.breakIt(...); lab.how(...); lab.autoFilm(...); lab.start()
コピーした直後に、node <skill dir>/scripts/inline.mjs <name>.html を実行します。これにより、キットがマークされたブロックに貼り付けられ、ページがどこにあっても1つの自己完結型ファイルになります(テンプレートの src="../kit/lab-kit.js" はこのリポジトリ内でのみ解決されます)。その後、2つ目のスクリプトブロックにマシンを記述します。
3. ジオメトリ:実サイズの実体
マシン独自のフレームでメートル単位で作業します。Y軸を上とし、ground を設定しない限り床は y = 0 です。size を最長の実寸法に設定します。キットは表示をスケーリングして、すべてのマシンがセット上で同じように配置されるようにし、スケールバーは実サイズを表示します。
- 丸い部品(シャフト、ディスク、ケーシング、ノズル、コーン、スピナー、ピストン、ベゼル):
GEO.lathe([[r, y], ...])で閉じたプロファイルを使用。壁:GEO.shell(profile, thickness)、チューブ:GEO.tube(rOut, rIn, h)、面取りされたディスク:GEO.disc(r, h)。角はシャープに保ち、曲線は自動的にスムーズにシェーディングされます。 - ブレードとベーン:
GEO.rotor({ count, axis, r0, r1, chord, thick, camber, twist, pitch, sweep })は、実用的なキャンバーとツイストを備えた翼型(NACA厚み分布)を閉じたソリッドとして構築し、それらをリング状に配置します。コード、厚み、キャンバーには[root, tip]配列を使用します。 - ギア:
GEO.gear({ teeth, module, width, bore, helix, spokes, rim })。2つのギアを中心距離(z1 + z2) * module / 2でメッシュし、歯数の逆比で回転させ、半歯分オフセットします。 - パイプ、ホース、ワイヤー:
GEO.pipe(points, r)(閉じた、キャップ付き)。ポートからポートへと構造に沿って配線し、部品の中を通らないようにします。 - ファスナー:すべてのフランジに
GEO.bolts(n, R, size, h)。スプリング:GEO.spring(R, wire, turns, len)。ボックス:GEO.rbox(w, h, d, r)(常に角丸、決してシャープではありません)。穴あきプレート:GEO.extrude(shape, depth)。 - 質量と慣性モーメントはジオメトリから計算されます。
GEO.volume(geo)は体積と重心を返し、実際の密度を掛けます。
クリーンに保つためのルール:
- すべての部品は閉じたソリッド(ラッチプロファイルは閉じている、シェルには厚みがある、パイプにはキャップがある)。これにより、カットアウェイで中実の断面を描画できます。開いたサーフェスは、切断時に中空として表示されます。
- 何も視認できる範囲で交差せず、何も浮いていません。 すべての部品は、それを保持するものに接触しています(フランジは面に、シャフトはベアリングに、ポストは床に届く)。接合部を拡大して確認してください。
- 目が行く場所にディテールを:フランジのボルトサークル、面取り、リップ、ラベルプレート、燃料ライン、ハーネス。広い平面には、継ぎ目、パネルライン、またはテクスチャ仕上げを施します。
- マシンにベースを与える:スタンド、クレードル、台座、ベンチ、または床に脚があるマウント。実際のマシンが浮いていることはほとんどありません。
- 予算:最大約70万トライアングル、200メッシュ。繰り返される静的部品は
GEO.merge/GEO.ringでマージします。移動グループは個別に保ちます。
4. マテリアル:決して1色のグレーにはしない
MAT.* プリセットを使用します。これらは測定された反射率を備えているため、スチール、チタン、アルミニウム、真鍮、銅が並んでいても異なって見えます。各プリセットはオーバーライド(color、roughness、clearcoat、任意の MeshPhysicalMaterial フィールド)と finish(微細な粗さテクスチャ:旋盤部品には turned リング、brushed ストリーク、cast ブロッチ、smudge、knurl)を受け入れます。
| 使用箇所 | プリセット |
|---|---|
| シャフト、ディスク、機械加工部品 | MAT.steel、MAT.stainless、MAT.chrome |
| ハウジング、鋳造品 | MAT.aluminium、MAT.castIron、MAT.steelDark |
| ブレード、航空宇宙 | MAT.titanium、MAT.inconel (+ heatGlow) |
| 継手、配線 | MAT.brass、MAT.copper、MAT.gold |
| 塗装ボディ | MAT.paint({ color })、MAT.satin、MAT.anodised({ color }) |
| ポリマー | MAT.plastic、MAT.rubber、MAT.carbon |
| 窓、クリスタル | MAT.glass、MAT.sapphire、MAT.ruby、MAT.enamel |
| 光、炎、高温ガス | MAT.glow、heatGlow(material, kelvin) |
インダストリアルデザイナーのようにパレットを選びます。ほとんどが金属で、1つか2つの塗装色、アクセントは最大1つです。高温部品はソルバーの温度から光ります(material.userData.heat.value = T)。
5. ソルバー:実数値でなければ意味がない
入力と状態から表示するすべての数値への、小さな純粋関数を記述します。実単位と実定数を使用します。動的を統合するには、approach(一次遅れ:スプールアップ、熱容量)または rk4(スプリング、脱進機、振り子)を使用します。そして、それからモーションを駆動します。ソルバーからのrpmがシャフトを回転させ、ソルバーからの流量がパーティクルを移動させ、温度が金属を加熱します。
- 1つのヒーロー:
lab.hero({ label, unit, max })その後、毎ティックlab.setHero(value, { state, sub, bad })を実行します。stateは2~3語(スプールアップ中、離陸、チョーク流)。subは2~3の数値を持つ1行です。 - 1つのスライダー:
lab.slider({ id, label, min, max, value, unit, ends })。マシンが本当に必要とする場合にのみ、2つ目のスライダーを使用します。 - ラベルは、セクションプレーン上のポイントで静的な部品に表示されます:
lab.label(obj, 'HP turbine', { at: [x, y, z], value: () => '12,979 rpm' })。 lab.how(words, equations)は「計算方法」を埋めます。平易な言葉でのモデルと、コードに記述された方程式です。簡略化したことを述べてください。- モーションが速すぎて見えない場合(2,700 rpmのファンなど)、速度を落として表示し、その旨を
howに記述します(「回転を1:60で表示」)。数値を遅くしないでください。 - 動作点を、ステップ1で書き留めた公開値と照らし合わせて検証します。約20%以上ずれている場合は、数値ではなくモデルを修正してください。
6. 流れ、熱、エフェクト
lab.flow({ paths, count, length, color, parent }):実際の流路(ダクト内の空気、ポンプ内の血液、パuck内の水)に沿って構築したパスに沿って移動するストリーク。ソルバーから.speedを設定し、lab.state.cutから.opacityを設定して、カットアウェイ内部に表示します。計算された量(温度、圧力)で色を付けます。lab.plume({ radius, length, dir, color: 'rocket' | 'jet', shocks }):マッハダイヤモンドを伴う排気。ソルバーから.powerを設定します。lab.sparks.emit(worldPos, n, speed)およびlab.shed(mesh, v, w)は破損用。スパークは重力で落下し、跳ね返ります。脱落した部品は剛体になり、床に着地します。
7. モード(キットが実行します。設定はあなたが行います)
- 分解:各
lab.add/lab.meshは、親フレーム内でexplode: [dx, dy, dz](メートル単位)を受け入れます。組み立て軸に沿って部品を移動し、外側のシェルを最も遠くに、コアはそのままにします。移動中に何も交差してはいけません。 - カットアウェイ:Lab の
cut: { a, b, center, reach }。aとbは除去される領域の方向です。a = [0,0,1], b = [0,1,0]は、視聴者に向かって上の4分の1を除去します(軸を持つものに最適)。b = aは半分を除去します。断面は、すべての閉じたソリッドに対して自動的に描画され、かすかなハッチが付きます。 - X線:外側のケーシングの
xray: 'shell'は、それらを細かいエッジを持つガラスに変え、コアが動作中に表示されるようにします。 - スローモーション、ラベル、一時停止はデフォルトで利用可能です(キー S、L、Space)。インタラクティブな部分は、マウスオービット、スライダー、ボタンです。
- 破損:
lab.breakIt({ label, run(lab), reset(lab) })。runはソルバーの状態を変更し(ブレード放出、バルブ固着、シール破損)、ソルバーが結果を計算します。ヒーローに表示し(bad: true)、スパーク、デブリ、シェイクで表現します。
8. 批評家のように見る(必須)
作成したすべての画像をレンダリングし、読み取ります。スクリプトを使用します。
node scripts/shot.mjs <name>.html out/review.png # 最初のルック
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 # フィルムスチル
最低4回のレビューラウンドを実施します。各ラウンド:全体像、ラベル付きカットアウェイ、分解、2つの接合部の拡大、そしてフィルムスチル。記憶の中のマシンの実際のプロダクト写真と比較します。プロポーション、部品数、素材、光の当たり方。問題点をリストアップし、修正し、再レンダリングします。リストが空になったら停止します。ショットスクリプトはfps、部品数、トライアングル数を表示します。インタラクティブフレームレートは30以上を維持してください。
9. フィルム
lab.autoFilm({ runBeat, runSub, breakBeat, breakSub, sliderTo }) は、マシンから標準の28秒のストーリーを、番号付きビートと編集用キャプションで作成します。
- 1文が全体の概要です(プロンプトバーが自動的に入力されます)。
- Claude がマシンを構築します(部品が組み立て順に飛来し、カウンターが上昇します:「3,597パーツ中84パーツ」)。
- それが動作します(カーソルがスライダーをドラッグし、ヒーロー数値がソルバーから上昇します)。
- 切り開きます(セクションキャップ、ライブ値のラベル)。
- 分解します。
- 破損させます(カーソルが破損ボタンをクリックし、ソルバーが何が故障するかを決定します)。
ループします。最後のカメラキーが最初のキーに戻ります。カスタムフィルムの場合は、lab.film('name', { len, assemble, shots, events }) を使用します。shots はカメラキー { t, az, el, dist, target, fov }、events は { t, beat: ['07', 'Bold sentence.', 'Grey sentence.'], set: { cut: 1 }, slider: { id, to, dur }, cursor: { to: 'slider:id:0.8' | 'button:break' | [x, y], click: true }, do(lab) {} } です。
録画(Node 18+、Playwright with 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
ページはフィルムモード(?film=1)で実行されます。レコーダーがステップ実行するときにのみクロックが進むため、すべてのフレームが完全で、フィルムは毎回同じになります。レコーダーは、停止したヘッドレスGPUが返す可能性のあるマゼンタフレームに対してすべてのスクリーンショットをガードし、完成したフィルムをスキャンします。ピンクのフレームがある場合、非ゼロで終了します。FreePDの音楽はCC0です。それでもトラックのクレジットを表示してください。
画面への書き込み
ページはダッシュボードではなく、編集記事です。プロンプトバー、イタリック体のサブタイトル付きセリフ体のタイトル、イタリック体の単位と細いバーを備えた1つの巨大な数値、1行の補助数値、1つのスライダー。優れたキャプションのように書きましょう。短く、具体的で、誇張なし、実単位とスペース(338 kN、1,750 K、4.2 L/min)、3桁区切り。ビートキャプションは、太字の文とグレーの文です。
引き渡し時に伝えること
HTMLファイルのパス、フィルムコマンド、ソルバーが公開値に対して到達した動作点、および簡略化したことを伝えてください。次のマシンを提案してください。