Machine Skill (기계 스킬)
당신은 마치 제품 스튜디오에서 사진을 찍고 엔지니어가 설명하듯 기계를 구축합니다: 실제 크기, 실제 재료, 깨끗한 스튜디오 세트, 실제 방정식으로 작동합니다. 한 줄 입력, 하나의 HTML 파일 출력, 한 번의 명령으로 영화까지.
출력물은 항상 다음과 같습니다:
<name>.html: 단일 자체 포함 페이지(jsDelivr의 three.js 0.170, 키트는 인라인). 디스크, 정적 호스트, 또는 iframe 내에서 열립니다.- 영화:
node scripts/record.mjs <name>.html out/<name>.mp4 --music freepd:<Category>/<Name>.
먼저 examples/를 열어보세요. 모든 예제는 페이지 상단의 한 줄 설명으로 만들어졌습니다. examples/turbofan.html이 참조 예제입니다: 무언가를 작성하기 전에 처음부터 끝까지 읽어보세요.
기준
시작하기 전에, 시청자가 3초 후에 보게 될 프레임을 상상해보세요. 이미 실제 기계의 스튜디오 제품 사진처럼 보여야 합니다: 올바른 실루엣과 비율, 금속은 금속처럼 읽히고, 종이 바닥에 부드러운 그림자, 떠 있거나 잘리거나 임시방편인 것 없이. 그런 다음 움직이고, 화면의 모든 숫자는 계산됩니다.
다음 중 하나라도 해당되면 결과물은 완료된 것이 아닙니다:
- 정비사가 예상할 부품이 누락됨(연소실에 연료 노즐 없음, 시계에 이스케이프먼트 없음, 엔진에 밸브 없음)
- 무언가가 떠 있거나, 교차하거나, 이음새에 보이는 틈이나 간격이 있음
- 재료가 금속이어야 하는데 플라스틱처럼 보이거나, 모든 것이 하나의 회색임
- 화면의 숫자가 솔버에 의해 계산되지 않고 입력된 값임
- 절개도가 단단한 단면 대신 빈 껍질을 보여줌
- 영화의 첫 프레임이 아름답지 않음
워크플로우
1. 실제 기계 조사 (5분, 코드 없음)
머신 스크립트 상단의 주석 블록에, 알고 있는 것(그리고 웹에서 찾은 것)을 기록하세요:
- 실제 치수(미터): 실루엣을 정의하는 5~10개 부품의 치수 (터보팬: 팬 직경 2.85m, 길이 4.9m, 나셀 최대 직경 3.44m).
- 부품 목록: 조립 순서, 각 재료와 함께. 15~40개의 이름이 있는 부품을 목표로 하세요; 반복되는 부품(블레이드, 톱니, 볼트, 코일)은 개별적으로 계산합니다.
- 물리: 작동 원리, 3~6개의 방정식(실제 상수와 작동점 포함)으로 표현 (터보팬: Brayton 사이클, 이륙 시 N1 100%, OPR 42, T4 1,750K, 약 330kN).
- 하나의 숫자: 시청자가 관심을 가질 한 가지 수치(주인공: 추력, 토크, 시간당 박동수, 유량, 출력). 큰 세리프 숫자와 얇은 막대로 표시됩니다.
- 입력: 시청자가 제어하고 싶어하는 하나의 슬라이더 (스로틀, 풍속, RPM, 펌프 속도, 분쇄도).
- 파손: 엔지니어가 걱정하는 고장과 솔버가 예측하는 결과 (팬 블레이드 이탈 및 언밸런스 하중, 레드라인 이상의 밸브 플로트, 펌프 스톨, 과압). 이는 계산된 결과여야 하며, 애니메이션이 아니어야 합니다.
2. 템플릿에서 페이지 구축
kit/template.html을 <name>.html로 복사하세요. 여기에는 import 맵([email protected] 고정), 글꼴, 키트가 들어갈 표시된 블록, 그리고 당신이 채울 네 개의 섹션이 있습니다:
const { THREE, Lab, MAT, GEO, heatGlow, clamp, lerp, approach, fmt } = window.LabKit
const lab = new Lab({ prompt, no, title, sub, size, view, cut }) // 1. 랩
// 2. 부품 lab.mesh(GEO..., MAT..., { name, pos, explode, xray, count })
// 3. 솔버 lab.hero(...); lab.slider(...); lab.onTick((dt, lab) => { ... lab.setHero(...) })
// 4. 파손, 설명, 영화 lab.breakIt(...); lab.how(...); lab.autoFilm(...); lab.start()
복사 직후, node <skill dir>/scripts/inline.mjs <name>.html을 실행하세요: 키트를 표시된 블록에 붙여넣어 페이지가 어디에 있든 단일 자체 포함 파일이 되도록 합니다(템플릿의 src="../kit/lab-kit.js"는 이 저장소 내에서만 해결됩니다). 그런 다음 두 번째 스크립트 블록에 기계를 작성하세요.
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 }). 중심 거리(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)는 부피와 중심을 제공하고, 실제 밀도를 곱하세요.
깔끔함을 유지하기 위한 규칙:
- 모든 부품은 닫힌 솔리드입니다(닫힌 선반 프로파일, 두께가 있는 쉘, 캡이 있는 파이프). 이것이 절개도가 단단한 단면을 그릴 수 있게 하는 이유입니다. 열린 표면은 절단 시 속이 빈 것으로 표시됩니다.
- 아무것도 눈에 띄게 교차하거나 떠 있지 않습니다. 모든 부품은 그것을 고정하는 것과 접촉합니다(플랜지는 면 위에, 샤프트는 베어링 안에, 포스트는 바닥에 닿음). 클로즈업에서 조인트를 확인하세요.
- 눈이 가는 곳에 디테일을 넣으세요: 플랜지의 볼트 원, 모따기, 립, 라벨 플레이트, 연료 라인, 하네스. 넓은 평평한 영역에는 이음매, 패널 라인 또는 텍스처 마감 처리를 하세요.
- 기계에 베이스를 제공하세요: 스탠드, 크래들, 받침대, 벤치 또는 발이 바닥에 닿는 마운트. 실제 생활에서 기계는 거의 공중에 떠 있지 않습니다.
- 예산: 최대 약 700k 삼각형과 200개의 메시. 반복되는 정적 부품은
GEO.merge/GEO.ring으로 병합하고, 움직이는 그룹은 분리하여 유지하세요.
4. 재료: 절대 단일 회색 금지
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) |
산업 디자이너처럼 팔레트를 선택하세요: 주로 금속과 한두 가지 도장된 색상, 최대 한 가지 악센트. 뜨거운 부품은 솔버의 온도에서 빛납니다(material.userData.heat.value = T).
5. 솔버: 진짜 숫자 아니면 없음
입력과 상태로부터 표시하는 모든 숫자까지 작고 순수한 함수를 작성하세요. 실제 단위와 실제 상수를 사용하세요. approach(1차 지연: 스풀업, 열 질량) 또는 rk4(스프링, 이스케이프먼트, 진자)로 동역학을 통합하세요. 그런 다음 솔버로부터 모션을 구동하세요: 솔버의 RPM은 샤프트를 회전시키고, 솔버의 유량은 입자를 움직이며, 온도는 금속을 가열합니다.
- 하나의 주인공:
lab.hero({ label, unit, max })그런 다음 매 틱마다lab.setHero(value, { state, sub, bad }).state는 23단어(스풀업 중, 이륙, 질식 흐름).3개의 추가 숫자가 있는 한 줄입니다.sub는 2 - 하나의 슬라이더:
lab.slider({ id, label, min, max, value, unit, ends }). 기계가 정말로 필요로 하는 경우에만 두 번째 슬라이더를 추가하세요. - 라벨은 단면 평면의 지점에서 정적 부품에 부착됩니다:
lab.label(obj, 'HP 터빈', { 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 }): 실제 유로(덕트를 통한 공기, 펌프를 통한 혈액, 퍽을 통한 물)를 따라 구축한 경로를 따라 흐르는 줄무늬..speed를 솔버에서 설정하고.opacity를lab.state.cut에서 설정하여 절개도 내부에 표시되도록 하세요. 계산된 양(온도, 압력)으로 색상을 지정하세요.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]은 시청자 쪽으로 위쪽 사분면을 제거합니다(축이 있는 물체에 가장 적합).b = a는 절반을 제거합니다. 단면은 모든 닫힌 솔리드에 대해 자동으로 그려지며, 희미한 해치가 적용됩니다. - X-ray: 외부 케이싱의
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 # 영화 스틸
적어도 네 번의 검토 라운드를 수행하세요. 각각: 전체 보기, 라벨이 있는 절개도, 분해도, 두 개의 조인트 클로즈업, 그리고 영화 스틸. 기억 속의 실제 기계 제품 사진과 비교하세요: 비율, 부품 수, 재료, 빛이 어떻게 떨어지는지. 잘못된 것을 나열하고, 수정하고, 다시 렌더링하세요. 목록이 빌 때까지 멈추지 마세요. 샷 스크립트는 fps, 부품 수 및 삼각형을 출력합니다; 대화형 프레임 속도를 30 이상으로 유지하세요.
9. 영화
lab.autoFilm({ runBeat, runSub, breakBeat, breakSub, sliderTo })는 기계에서 표준 28초 스토리를 작성하며, 편집 캡션이 있는 번호가 매겨진 비트로 구성됩니다:
- 한 문장이 전체 브리핑입니다(프롬프트 바가 스스로 입력됨).
- Claude가 기계를 구축합니다(부품이 조립 순서대로 날아들고 카운터가 올라갑니다: "3,597개 중 84개 부품").
- 작동합니다(커서가 슬라이더를 드래그하고, 주인공 숫자가 솔버에서 올라갑니다).
- 열어 봅니다(단면 캡, 실시간 값이 있는 라벨).
- 분해합니다(분해).
- 파괴합니다(커서가 파괴 버튼을 클릭하고, 솔버가 실패를 결정합니다).
루프: 마지막 카메라 키가 첫 번째로 돌아갑니다. 사용자 정의 영화의 경우 lab.film('name', { len, assemble, shots, events })를 사용하세요: 샷은 카메라 키 { t, az, el, dist, target, fov }이고, 이벤트는 { t, beat: ['07', '굵은 문장.', '회색 문장.'], 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가 반환할 수 있는 마젠타 프레임으로부터 보호하고, 완성된 영화를 스캔합니다; 어떤 프레임이 분홍색이면 0이 아닌 값으로 종료됩니다. FreePD의 음악은 CC0입니다; 그럼에도 트랙을 크레딧하세요.
화면에 쓰기
페이지는 대시보드가 아닌 에디토리얼입니다: 프롬프트 바, 이탤릭체 부제목이 있는 세리프 제목, 이탤릭체 단위와 얇은 막대가 있는 하나의 거대한 숫자, 한 줄의 지원 숫자, 하나의 슬라이더. 좋은 캡션처럼 작성하세요: 짧고, 구체적이며, 과장 없고, 공백이 있는 실제 단위(338 kN, 1,750 K, 4.2 L/min), 천 단위 구분. 비트 캡션은 굵은 문장과 회색 문장입니다.
인계할 때 할 말
HTML 파일의 경로, 영화 명령어, 공개 수치에 대해 솔버가 도달하는 작동점, 그리고 단순화한 모든 것을 제공하세요. 다음 기계를 제안하세요.