Practice / Lab
From Prototype to Protocol
LoopFlow 1.0 began as field research inside Rhino: whether schematic design, design development, and construction documentation could stay in one model, with automation that the designer still controls.
LoopFlow did not begin as a software project. It began when the usual path from schematic design to construction drawings, already lived inside Rhino, would not stay connected without giving up control.
01 — 原本就在 Rhino 裡走完 SD 到 CD
我的作業模式本來就不是「在某個 BIM 裡設計,再另外一套軟體出圖」。
從 SD(Schematic Design)、DD(Design Development),到 CD(Construction Documentation),整條路都在 Rhino 裡走。模型是設計本身,圖面也從同一個模型長出來。
LoopFlow 1.0 要回答的,不是要不要改用別的系統,而是:
這條我本來就在走的路,能不能串得更順,同時讓自動化停在我還能介入的地方。
半自動的意思很具體:每個環節我都參與,每一步都由我確認後再繼續,像是檢驗停留點。系統幫忙傳資料、更新圖紙上那些不該用手 key 的東西;圖面最後長什麼樣子,仍由我決定。
02 — 模型要跟著改,圖面最後仍要由人控制
Rhino 8 的 Clipping Plane 與 Section Tools,已經可以讓 3D 模型快速產生平面、剖面與立面。看起來幾乎具備類似 BIM 的工作方式:模型改了,圖面跟著改。
一開始我也被這種方式吸引。
但實際拿來做施工圖之後,問題很快出現。我希望圖面是純線稿,希望能精確控制線寬,希望可以刪除不需要的線、補線、局部修改,也希望最後得到的是一份真正可以繼續整理與交付的 2D Drawing,而不是只能持續依附在 3D View 上的投影結果。
模型與圖面需要保持關係。
但圖面最後仍然必須由人控制。
LoopFlow 就從這個矛盾裡開始生長。
03 — 沒有架構:一題一隻 Python
我沒有程式開發背景。
所以 1.0 並不是先規劃軟體架構,再逐步完成模組。做法剛好相反:先遇到一個問題,就做一個工具。一個功能,一隻 Python。
需要把 Dictionary(Excel 裡定義的材質與工項)寫進模型,就做一個指令。
需要把 3D 資料發布出去,就做另一個。
需要讓圖面上的 Tag 找到模型,就做 Grab、Laser。
需要讓剖面索引知道自己指向哪張圖,就再做 Index。
需要把資料真正寫進圖面,就做 Infuser。
最後,這些原本彼此獨立的小工具逐漸串成一條完整工作鏈。
從軟體工程角度看,這並不漂亮。很多東西會重複;有些功能同時處理畫面、幾何、資料與檔案;同一種資料可能在不同 Python 裡各自尋找;新的需求也經常直接附掛在既有功能旁邊。
那不是軟體架構,那是散裝 Python 的組合包。這是一趟很有趣的旅程,探索階段的姿態並不總是從容優雅,而是要先確認這條工作鏈到底能不能成立?
只要真的拿來工作,很快就會知道哪些想法只是看起來有吸引力,哪些操作真的順手,哪些地方每天都會撞牆。
04 — Laser:我只是想讓 Tag 吸個資料
Laser 是一個很典型的例子。
需求其實非常小:在剖面圖上點一下,讓 Tag 知道這個位置對應哪個 3D 物件,再取得材質、高程等資料。聽起來像一個下午可以解決的事情。結果做了一個多星期。
一開始我並不知道,AI 底下使用 Bounding Box(bbox,包住物件的外框)作為位置判斷的一部分。我反覆測試,一直覺得結果不合理。直到真正理解它是用外框判斷,我才意識到:
那怎麼可能永遠判斷正確?
兩個物件的 bbox 可以互相重疊。複雜形狀的 bbox 裡,也可能存在大片根本沒有幾何的空間。最後我換了一個自己比較熟悉的思考方式,既然要知道一個位置到底碰到哪個物件,那就不要猜——直接畫一條線去撞它。這很接近平常使用 Grasshopper 的邏輯:建立幾何關係,再求交集。
Laser 的核心變成:
2D 上的一個點
→ 轉換成 3D 座標關係
→ 射出一條線
→ 找到真正被命中的 3D 物件
→ 取得它的資料
當初只是為了解決眼前問題想到的土炮方法,後來反而成為 2.0 正式架構的一部分。
05 — UserText:資料怎麼進去,怎麼出來
繪圖實務裡,另一件每天都會碰到的事,是物件身上到底帶了什麼資料。
Rhino 的物件可以掛 UserText:一組看不見的鍵與值,像貼在構件背面的小標籤。材質名稱、工項、高程基準,都可以寫在上面。圖面上的 Tag Block 再去讀這些值,不必人手 key 一次材質、再 key 一次編號。
聽起來只是「寫進去、再拿出來」。實際狀況卻很多。
同一個物件可能被複製、鏡射、做成 Block。圖層名稱改了,資料還在不在?Dictionary 改了一列,模型裡已經寫過的物件要不要重寫?Tag 吸到的是這個物件,還是它背後那份定義?剖面上看得到的線,對應的 3D 物件可能已經被藏起來、鎖住,或根本不在這一刀上。
1.0 對這些情況做了很多種技術嘗試:怎麼灌入、怎麼取出、取不到時人要不要看到、要不要允許手動改。每一種嘗試都對應一種真實繪圖時會發生的事,而不是對應一份預先寫好的規格。
後來我才有語言描述這件事。軟體裡所謂的 data contract(資料契約),對我而言很像施工圖上的共同尺寸與標高:大家必須講同一套數字,圖才接得起來。1.0 當下還沒有這份契約,只有一隻隻 Python 各自去找物件身上的字。
06 — 散裝組合包,不是軟體
把 Laser、UserText、Dictionary、Infuser 全部加在一起,1.0 已經可以工作。但它仍然比較像工作室裡一排專用功能,而不是一套軟體。
沒有清楚的模組邊界。誰負責寫資料、誰負責讀圖面、誰負責記住「這一版已經發布過」,常常混在同一支檔案裡。安裝是解壓縮、跑 bat、把工具列拖進 Rhino。同一個專案裡,按鈕順序就是工作流,工作流卻沒有被寫成可以換人接手的規則。
一切都還相當混亂與不完整,但沒有關係。
那時候要證明的不是「我做出了一個產品」,而是「這條從模型到圖紙的鏈,在實際出圖時站不站得住」。
07 — 第一版的混亂是一種研究
現在回頭看 LoopFlow 1.0,很多地方都亂七八糟。
但如果一開始就要求它具備 2.0 現在的架構,我認為根本不可能。因為那時候甚至還不知道真正的問題是什麼。
我不知道 View 需要穩定的 ID(識別碼:名稱會改,編號最好不要改,像門牌而不是房間暱稱)。
不知道 2D 與 3D 的 transform(位置與方向的對應關係)需要被保存。
不知道 Tag 應該綁物件、視圖還是圖紙。
不知道 Drawing 應該和 View 分開。
也不知道模型資料的發布需要版次、上一份還有效的結果、鎖定或退回。
這些東西不是坐在桌前想像就能完整規劃出來的。它們是在工作真的跑起來之後,一個一個從事故裡長出來的。
因此 1.0 後來對我而言不再是一個需要被嫌棄的舊版本。它比較像是一場持續數個月的實地研究。那些看起來凌亂的 Python,就是研究紀錄。
08 — 有些東西不帶進下一版
1.0 裡還長出了主工作鏈以外的東西,例如櫃體產生、部分 2D 繪圖工具。它們都是些實用工具,也消耗了大量嘗試。
到了 2.0,我得做出明確的決定:排除跟主鏈無直接相關的功能。櫃體、那些 2D 工具,可以另開專案處理與擴充。而 LoopFlow 本體只留下「模型資料如何寫入、檢核、發布,以及圖紙上的 Tag 如何對到同一版」。
這反而是一條更好的路徑:
→ LoopFlow 只專心在建立3D模到2D圖紙的工具鏈
→ LoopFlow Rhino to Octane Sync 處理 Rhino 模型同步到 OctaneRender
→ LoopFlow Rhino to Blender Sync 處理 Rhino 模型同步到 Blender
→ LoopFlow ToolKit 作為各種繪圖工具的合輯
在同一個大架構下,發展為 LoopFlow Series。
結尾
1.0 問的是:
這件事情做不做得到
所以用很多獨立的小工具,快速把工作鏈接起來。先讓它可以動,再看看它真正長成什麼。
第一版需要一些原始的生命力。需求來了就先接,問題出現就先解。這很像在做設計,很多真正有價值的想法,其實是在這種不合理、且無完整架構的環境中出現。
架構應該晚一點進來。等系統真的被使用過,等問題真的發生過,等我們知道哪些混亂只是缺陷,哪些混亂其實藏著產品真正的性格,再重新整理。
The first version was not a failed architecture. It was field research that could only be done by working.
第一版不是失敗的架構。它是一場只能靠實做才完成的實地研究。
下一篇要處理的問題已經不同:如果這套東西要持續成長,它們之間應該用什麼規則相處;以及,從散裝 Python 到 Package Manager,中間經過了怎樣的施工圖。