Joinery 是做什麼的?
一張圖片、一個頁面、一整套。參觀者看到組裝好的成品,拖動滑桿就能分解,逐步了解組裝過程,點擊零件即可查看其木材規格。驅動零件移動的同一個模型也負責撰寫裁切清單、圖紙和手冊,因此紙本資料與螢幕顯示絕無不一致。
SKILL_DIR 是此檔案所在的資料夾。建構成果與工匠包會輸出到執行指令所在資料夾的 ./joinery-out 目錄。
首次執行
cd "$SKILL_DIR" && npm install
需要 Node 20.10+。若要產生審閱用的靜態圖檔與手冊圖片,需要 Chromium:npx playwright install chromium,或將 CHROMIUM 環境變數指向任何 Chrome/Chromium 執行檔(例如 CHROMIUM=/usr/bin/google-chrome)。
工作流程
照片 + 錨點 ─► spec.json ─► model.js ─► 驗證 ─► 頁面 ─► 靜態圖 ─► 工匠包
| 步驟 | 指令 | 產出內容 |
|---|---|---|
| 1. 詮釋 | 你,查看照片 | spec.json:類型、錨點、材料、每個零件命名、每個接合選擇、每個假設記錄 |
| 2. 建模 | 你,撰寫 model.js | 以參數表示的成品:方塊、接合、步驟 |
| 3. 驗證與打包 | node "$SKILL_DIR/pack.mjs" ./my-piece | 如果零件碰撞或懸空則拒絕打包;輸出裁切清單、圖紙、板材切割圖、木材、工具與塗裝建議、手冊(如有 Chromium 則輸出 manual.pdf) |
| 4. 建構頁面 | node "$SKILL_DIR/build.mjs" ./my-piece | joinery-out/dist/my-piece.html,離線可用,約 570 KB |
| 5. 審閱 | node "$SKILL_DIR/capture.mjs" my-piece --stills steps | 包含每個視角與步驟的聯絡表 — 閱讀、修正、重複 |
| 6. 再次打包 | node "$SKILL_DIR/pack.mjs" ./my-piece | 手冊與 PDF 現在包含步驟圖片 |
從最接近成品範例的資料夾開始複製。每個範例都包含一個作品所需的所有檔案:spec.json、model.js、main.js。作品通常存放在使用者自己的資料夾中,而非 SKILL_DIR:複製後,請將 model.js 和 main.js 中的兩處 ../../kit/ 匯入路徑改為絕對路徑 $SKILL_DIR/kit/。
| 範例 | 適用於複製到 | 展示內容 |
|---|---|---|
parsons-table | 簡單桌子、書桌、長凳 | 最小配置:桌腳、裙板、桌面、榫卯、螺絲鈕。64 行 |
walnut-racetrack-table | 圓形、橢圓或錐形零件 | roundedRect / circle 輪廓、錐形圓腳、長度足以在桌腳內交錯的短榫頭 |
walnut-display-cabinet | 帶曲線或玻璃的箱體家具 | 彎曲膠合角 (quarterRing、stock.layers)、使用夾鉗式鉸鏈的玻璃門、層板銷、嵌槽背板、底座 |
walnut-entry-unit | 系統櫃、衣櫃、廚房式單元 | 多個箱體、鑲板門、滑軌抽屜、浮動層板、貼皮 MDF 與實木貼皮並用、LED 與掛鉤配件 |
walnut-hallway-unit | 鋼木混合作品 | 焊接鋼架支撐木箱、藤編門板、為電箱保留的開放式凹槽 |
步驟 1:誠實解讀照片
一張照片無法提供尺寸與隱藏結構。此技能是設計詮釋,而非攝影測量。在撰寫任何幾何之前:
- 取得錨點。 詢問一至三個真實尺寸(總長 × 深 × 高,或一個已知尺寸如「桌面 180 公分」)。如果對方無法提供,請使用
kit/validate.js中的人體工學標準(餐桌高 750、座椅高 450)並告知對方。其他所有尺寸都是根據照片比例與錨點計算得出。 - 命名類型。
dining-table、chair、bookshelf… 這會決定人體工學檢查與典型結構。 - 列出可見與不可見部分。 桌腳、桌面、可見層板標記為
visible。厚桌面下的裙板、背板、橫撐、抽屜滑軌、角塊標記為inferred— 在零件上設inferred: true,使其在每個輸出中都被標示。工匠可以覆寫這些設定;他們必須知道哪些是你推斷的。 - 從接合庫選擇接合方式 (
templates/joinery-library.md),根據類型 × 材料 × 工坊類型。如果工坊類型不明顯,詢問工匠的工坊類型(shop:hand、cabinet、cnc);這也會決定工具清單。預設為細木工坊:桌腳/裙板使用榫卯、層板使用槽接、面板使用木釘或 Dominos、隱藏處使用口袋螺絲。將選擇與原因寫入spec.source.assumptions。 - 解讀材料與塗裝。 根據顏色與紋理判斷木材種類,根據邊緣比例判斷厚度,根據高光判斷邊緣處理(圓角 / 倒角 / 直角)。厚度必須符合標準木材規格 (
kit/validate.js中的STOCK_T) — 40 而不是 38.7。根據光澤設定finish:開放、霧面表面為hardwax-oil(預設值)或oil,均勻緞面薄膜為lacquer。塗裝與金屬零件則根據其材料設定。
將所有內容寫入 spec.json,並使用 templates/spec.schema.json。規格書是給人看的:製作者會先閱讀 source.assumptions。
步驟 2:建立模型
model.js 匯出 piece = definePiece({name, type, shop, finish, params, source, materials, build})。build(P) 回傳 {parts, joints, steps, hardware}。範例約 70 行;椅子約 120 行。
零件是在組裝姿態下的軸向對齊方塊:box(id, name, [sx, sy, sz], [x, y, z], {grain, material, edge, explode, inferred, note})。x 方向為長度,y 方向為向上(地板在 0),z 方向為深度。grain 是木材紋理方向 — 它決定裁切清單中的木材長度與渲染中的年輪圖案。相同裁切的零件使用相同的 name,並合併為裁切清單中的一行;id 保持唯一。
彎曲與圓角零件保留邊界框作為 size/at,並新增 profile: {axis, path, note}:跨 axis 的真實輪廓,以方塊為中心(彎角用 quarterRing(R, t, [su, sv])、圓角玻璃/背板/橢圓桌面用 roundedRect(w, h, radii)、圓形桌腳用 circle(d);profile.taper: {to, end} 向一端逐漸縮小)。渲染時擠出,驗證時檢查輪廓,圖紙上繪製輪廓。當方塊尺寸不等於木材規格時(例如彎曲膠合),加入 stock: {L, W, T, layers, layerT},裁切清單與採購清單將使用此規格。在兩個方向彎曲的零件(如雕刻椅背、複合式靠板)尚無本體類型:請告知使用者,將最接近的直線擠出件建模,並在其 note 中說明。
配件是購買的五金件,繪製在零件上,而非裁切:fittings: [{kind, size, at, material, axis, dir, holes: {n, d, cc, axis}}],其中 at 是相對於零件中心的位置。把手、掛鉤和 LED 型材會渲染;它們的螺絲孔會出現在零件圖紙上。同時也需將它們列在 hardware 中。
每個零件的顏色: materials 對應表中的條目可以帶有 base、dark、ring、streak、rough、clearcoat、env 參數,以針對照片調整套件材料,而不影響其他零件。大片平面上的深色油處理木材需要 rough ≈ 0.9,否則主燈光的光澤會使其變灰;請比對照片與渲染的數值,而非主觀印象。
接合連接兩個零件 ID:{a, b, type},其中 a 帶有榫頭/螺絲,b 接受它。類型:mortise-tenon(榫頭長度、榫頭厚度、榫肩、斜接)、dowel(數量、直徑、長度)、dado(深度)、rebate(面、深度:背板嵌入 b 的邊緣槽)、shelf-pin(數量、直徑、長度、內縮量:層板藉由銷釘固定在 b 上)、hinge(面、at:[世界 y 座標…]、cup:35 表示在 a 上鑽杯孔:a 門面向 +z 安裝在 b 上)、groove(寬度、深度:面板或抽屜底板浮動安裝在 b 的邊緣或面上)、runner(抽屜盒在 b 中的側裝滑軌上)、weld(鋼對鋼)、pocket-screw、button、figure-8、screw、glue。shelf-pin 和 hinge 允許五金件所需的幾毫米間隙;每個零件都必須與其他零件接合,否則驗證會拒絕。applyJoinery 將匹配的特徵寫入兩個構件:榫頭會延長裁切清單中的木材長度,榫眼會以其面和位置顯示在桌腳的圖紙上,並且兩者都會渲染。接合也告訴 validate 這兩個本體可以接觸。
步驟是手冊:{title, parts: [ids], note, check, view}。每個零件必須恰好出現在一個步驟中。按木工習慣排序:先子組立(端框、抽屜盒),然後是連接它們的部件,接著是放在上面的部件,如果需要反轉放置就反轉。view 設定該步驟在頁面和手冊中的攝影機視角。
參數。 所有數值都在 P 中。更改 L,裙板、榫頭、螺絲鈕間距和裁切清單都會隨之更新。這就是關鍵:同一個 model.js 可以製作 1600 版本和 2200 版本。
決定能否建造的規則
- 從照片出發,絕不憑空想像。 依照照片建構,並並排比較渲染圖:輪廓、桌腳與桌面比例、懸垂、裙板陰影線的位置。憑空想像的「漂亮橡木桌」會顯得很普通;同樣的程式碼在有參考目標時能緊密重建真實的桌子。
- 真實木材規格。 厚度必須是標準規格。任何寬度超過 200 mm 的部件都不是單塊木板 —
pack.mjs會將其轉換為膠合拼接板條並註明。任何厚度超過 50 mm 的部件都不是單塊木板 — 它會變成積層材。紋理方向必須沿著每根材料的長度方向,否則驗證會警告短紋理問題。 - 每個接合都是真實接合。 兩個零件只能透過宣告的接合共享體積。在桌腳內相交的榫頭必須設為
mitred: true或縮短;驗證會檢查碰撞。寬大的實木桌面必須使用螺絲鈕、figure-8 連接件或長孔固定 — 絕不能剛性膠合 — 驗證會對此提出警告。 - 用數字修正碰撞。 當驗證報告一個零件穿過另一個零件時,請更改
P或build中的數值。絕不能透過攝影機角度來隱藏問題。 - 記錄假設,而不只是零件。 照片中未顯示的裙板應設為
inferred: true並加上note。手冊的第一個方塊會列出所有假設。 - 一個數值,一個來源。 裁切清單、圖紙、頁面上的 HUD 和手冊都讀取
stockOf(part)。如果某個數值看起來有誤,請修正模型,而非輸出。
即時頁面規則
- 頁面是一個離線 HTML 檔案,小於 1 MB(內嵌 three.js)。包含許多零件的大型作品仍能保持快速,因為每個零件都是一個圓角方塊,木材是著色器而非紋理。
- 工作室燈光:暖色主燈、冷色補光、柔和地面陰影。木材著色器會沿著
grain方向自動繪製弦切紋理,並在端面繪製端面紋理。保持roughness ≥ 0.45;淺色地面上的光澤木材看起來像塑膠。 - 靜置 20 秒後,頁面會自動導覽:分解、逐步顯示、底部 X 光透視。任何輸入操作都會交回控制權。
- URL 狀態驅動截圖:
?step=2、?explode=1、?iso=leg-fl、?view=under&xray=1、?panel=0。設定這些參數後,頁面會直接顯示靜態畫面而非動畫,因此無論在 5 fps 還是 60 fps 下,靜態圖都保持一致。
審閱循環
你無法直接查看頁面,但可以查看靜態圖。絕不要在未讀取當前建構的聯絡表之前就報告作品已完成。
node "$SKILL_DIR/capture.mjs" <name> --stills steps→joinery-out/<name>-sheet.png:組裝圖、分解圖、正面圖、底部 X 光圖、每個步驟一個圖塊。- 根據檢查清單閱讀每個圖塊。將參考照片放在
front和assembled圖塊旁。 - 對於零件,使用
--stills parts隔離每個零件;對於特定狀態,使用--still "step=3&view=under"。 - 修正、重建、返回步驟 1。直到通過檢查且未發現任何問題為止。
- 以同樣方式開啟
joinery-out/<name>/manual.html,並像工匠一樣閱讀。
檢查清單:
- 比例。 對比照片:桌腳厚度與長度、桌面厚度與長度、懸垂量、裙板線高度。誤差在 5% 以內,否則調整參數。
- 接合。 榫眼位於裙板接觸的面上;分解圖與 X 光圖中可見榫頭;分解圖中沒有任何零件懸空(沒有接合的零件其分解向量沒有意義)。
- 步驟。 每個步驟添加可看到的東西;攝影機顯示正在添加的零件;已放置的零件在下方可見。步驟 1 絕不能是桌面。
- 圖紙。 圖紙上的每個榫眼、槽孔和孔洞都標示其面與距端點的距離。包含榫頭的裁切清單長度需標明。
- 木材。 材料清單中沒有寬度超過 200 或厚度超過 50 的板材;列出膠合板與積層材。每個零件都位於切割圖中的板材上。
- 工具與塗裝。
tools.md列出工匠工坊會使用的工具,夾具長度應符合最大膠合件的需求。塗裝、貼皮和金屬零件各自有其塗裝說明。 - 渲染。 所有內容距離邊緣至少 60 像素,面板絕不能遮蓋作品,接近白色的木材(楓木、白蠟木)在淺色背景上需有可見邊緣。
材料
kit/stage.js → MATERIALS:白橡木、紅橡木、胡桃木、楓木、白蠟木、山毛櫸、松木、櫻桃木、樺木夾板、MDF、鋼、黃銅。新增木材種類時需提供基礎色/深色以及年輪與紋理強度。此外還有:胡桃木-MDF、胡桃木彎曲板(貼皮板材)、黑色 MDF、玻璃(透明,以邊緣繪製,在工匠包中按尺寸訂購而非從板材裁切)。definePiece 中的 materials 對應表為裁切清單命名這些材料(例如「白橡木,實木」)。