齊保良,藺玉璞,楊秀芝,汪晴晴
(山東建筑大學 信息與電氣工程學院,濟南 250101)
近年來,建筑信息模型(Building Information Modeling,BIM)技術已廣泛應用于建筑行業,Revit 是BIM 技術常用的建模軟件之一[1].目前,鋼筋BIM 模型三維視圖中鋼筋型號、直徑等信息能夠采用引線方式標注[2,3],CAD 圖中的房間名稱等信息可以自動轉換到Revit 樓層平面圖中[4,5],但是,Revit 三維視圖無法顯示房間名稱及ID 信息,在仿真漫游或觀察剖面視圖時,需要由三維視圖切換到二維樓層平面圖查閱標注的房間名稱及ID,降低了工作效率.
本文旨在設計一種在Revit 三維視圖中自動標注房間名稱及ID 信息的中間件,減少仿真漫游或觀察剖面視圖時Revit 二維與三維視圖之間的多次切換,并自動準確定位房間位置,解決標注信息位置與模型重疊問題.
中間件,作為Revit 樓層平面圖與Revit 三維視圖之間的數據中轉站,在提取、存儲二維樓層平面數息標注到三維視圖模型房間方面做到了起承轉合的作用.
Visual Studio 是微軟基于.NET Framework 框架的一個集成開發環境.在本文中,Visual Studio 軟件作為Revit 二次開發的平臺.Revit 的應用程序接口(API)能夠訪問Revit 模型的圖形數據、參數數據.
基于Revit API 接口設計中間件有兩種方式:外部應用函數(IExternalApplication ())和外部命令函數(IExternalCommand ()).Revit 開發平臺通過兩者后綴為“.addin”的文件識別和加載外部插件,其中IExternalApplication ()需要調用兩個接口命令函數(OnStartup ()和OnShutdown ())創建應用程序.該工作方式在運行的Revit 開發平臺上即可進行編程和調試工作,調試完畢重新加載運行調試的程序實現功能擴展,節省編程調試時間.而IExternalCommand ()函數在程序調試時,需要關閉Revit 軟件平臺,返回到外部命令函數調用Execute ()函數調試程序,由于反復關閉和啟動Revit 軟件平臺,會需要更多的調試時間.
本文選用IExternalApplication ()函數創建功能面板,通過加載Addin Manager 來識別外部工具.該方式利用Visual Studio 集成開發環境、C#編程語言,添加引用Revit API.dll 和Revit API UI.dll 接口,搭建Revit 軟件應用平臺,通過IExternalApplication ()函數來讀取應用插件.基于Revit 的中間件二次開發技術的外部應用操作流程如圖1所示.
在Revit 軟件平臺上,鏈接CAD 圖紙,由二次開發的“CAD 文字轉化”插件自動識別鏈接圖紙的房間名稱及ID 文字信息,并標注于Revit 樓層平面房間[6].自動標注Revit 三維視圖房間信息的中間件在此基礎上進行設計.具體設計可以分為三部分:中間件樓層房間信息獲取方法、中間件數據存儲及中間件模型房間信息標注方法.
在以標準建筑規范為準的Revit 二維樓層平面中,各房間通過Revit 軟件“標記房間”功能標識房間,并且它也是由建筑墻體、門窗等構件無縫連接組成的封閉輪廓.其中不同房間名稱可能相同但是其ID 信息和坐標定位不同.通過Revit API 獲取樓層房間信息需對建筑房間內的構件進行訪問、類別過濾以及坐標定位.
2.1.1 建筑構件的訪問
在建筑設計建模時,類別(Category)是一組圖元,類名是該組圖元的名稱[6].根據構件標識特征,Revit API 對不同構件進行辨別和訪問.該方法訪問系統族實例僅需要判斷構件的類名屬性即可,訪問可載入族實例需要共同判斷構件的類名及類別.
由于門、窗、柱等構件沒有專用的系統類來表示,都是FamilyInsurance 的載入族實例,無法通過類名辨別,故Revit 二次開發的中間件需要采用類別和類名判斷載入族實例的構件.

圖1 基于Revit 的中間件二次開發技術的外部應用流程圖
2.1.2 建筑構件的類別過濾
Revit 開發平臺中所有的對象構件遍歷都要使用過濾收集器(FilteredElementCollector)收集被過濾的對象,在過濾操作時結合多個過濾器共同使用,過濾后,過濾收集器獲得滿足條件的對象構件集合[7,8].
Revit API 創建過濾收集器后,選擇其中的構件過濾條件,過濾當前樓層平面圖中的構件,指定視圖中的可見對象或集合,例如房間(Room)、墻體(Wall)、門(Door)等.以Room 類為例:
FilteredElementCollector roomFilter=new Filtered ElementCollector(doc);//創建過濾集合
ElementCategoryFilter orCategory=new Element CategoryFilter(BuiltInCategory.OST_rooms);//創建類別過濾條件
roomFilter.WherePasses(roomCategory);//這個返回的是房間類別下的所有Element,包括了文件中的房間實例(對應type 為FamilyInstance),和用來創建房間實例的類型(type 對應FamilySymbol)
ElementClassFilter roomType=new Element Class Filter(typeof(FamilyInstance));//創建一個type 過濾器
roomFilter.WherePasses(doorType);
2.1.3 樓層房間信息獲取
由于建筑模型內部是由多個房間和公共區域組成,所以判斷房間是獲取信息的主要依據,而樓層房間內的建筑構件應該由統一坐標系進行定位.Revit 樓層平面圖中,房間名稱及ID 信息標注于封閉房間墻體輪廓內部.
(1)坐標轉化
在Revit 樓層平面圖內,不同圖元構件間的坐標基準值不同,圖元構件通過圖元間的相對位置進行定位.因此,圖元構件首要進行坐標轉換,轉化為統一的模型坐標系.
Revit 軟件提供了Transform 類來進行坐標轉換,開發人員可以給Transform 對象進行賦值構造轉換矩陣,使用該轉換矩陣將給定的坐標點坐標轉成模型坐標系.首先,初始化Transform,然后設置其目標坐標系的三個方向向量BasisX,BasisY,BasisZ 的值,使用Of Point 進行點坐標轉換,用Of Vector 進行向量坐標轉換,把目標點或向量坐標轉換到模型坐標系的坐標.
Revit 開發平臺內的GeometryInstance.Tr ansform函數訪問族定義到模型中的位置矩陣,使用Transform轉換矩陣計算出樓層平面圖中的圖元構件門、窗等族實例中的所有點在模型坐標系的位置(從族實例中獲取其點、線、面的坐標也可以通過GeometryInstance對象的GeometryInstance.GetIns tanceGeomery()函數直接返回在模型坐標系下的坐標).
(2)坐標定位
Revit 圖元構件的定位信息通過兩種方式來獲取:
1)使用Revit.Location 函數來獲取定位簡單族實例位置.包含兩類對象:
①基于點的族實例:例如柱子、門窗和家具等.基于點的族實例的Location 屬性返回Location Point IP 對象,通過IP Point 獲得插入點的位置坐標以及IP Rotation 獲得該點的旋轉角度,其值為弧度值,逆時針旋轉為正.
②基于線的族實例:例如梁、墻等.基于線的族實例的Location 屬性返回Location Curve Ic 對象,通過Ic Curve 可返回基線,從而獲得路徑的起始點和終止點的位置信息.
2)使用Element Geometry 函數來獲取復雜圖元的點、面、線空間定位.例如:樓梯、屋頂、斜墻.
(3)確定房間邊界
在Revit 樓層平面中,通過Revit API 獲取標識房間的參數及封閉房間邊界坐標.房間邊界可以為幾種圖元構件:墻體、門、窗、柱等.
圖元構件元素為房間邊界的條件:
1)對于模型曲線的元素,它的類別是BuiltInCategory.OST_AreaSeparationLines.
2)對于其他元素,比如墻、柱,它們的房間邊界(BuihInParameter.WALL_ATTR_ROOM_B OUNDING)參數必須設為True.
Revit API 獲取所有房間邊界信息可以用Room 里的
public IList
(
SpatialElementBoundaryOptions options
)
BoundarySegment 函數集合IList

圖2 房間邊界類圖
(4)獲取房間信息
樓層房間經建筑構件的訪問和過濾收集器過濾后,將指定的集合房間存入創建好的收集器中,接著,使用Transform 類轉化為統一的模型坐標系,使用Revit.Location 獲取建筑樓層房間邊界的坐標定位信息;再通過Revit API 的內置參數BuiltInParameter.ELEM_ROOM_NAME 直接識別和提取房間名稱、ID 字符串,為下一步數據存儲做好準備.
利用各樓層房間邊界的區域坐標數據范圍來表示中間件獲取的樓層房間名稱及ID 信息.在Revit 樓層平面圖中,定義圖紙樓層房間的左下角為坐標系原點(0,0),分別利用直角坐標系表示各個封閉多邊形的4 個節點,而4 個節點分別做4 條直線X=Xi,X=Xj,Y=Yi,Y=Yj,該四條直線所圍成的封閉多邊形為房間邊界輪廓,利用Boundary Segment 函數來確定房間邊界,取該封閉多邊形區域的陰影部分任意一點表示該房間的名稱及ID 信息.如圖3所示.

圖3 封閉多邊形的定義信息圖
以某幼兒園施工圖的一層樓層平面為例(如圖4所示),取施工圖中某一特征點A(xi,yi),其中(x0≤xi≤x1,y0≤yi≤y1)內的點代表活動室101;(x1≤xi≤x2,y1≤yi≤y2)內的點代表辦公室103 以此類推.

圖4 以某幼兒園施工圖的一層樓層平面
數據庫作為Revit 樓層平面圖與Revit 三維視圖之間的數據中轉站,在數據儲存、數據調用方面起到重要作用.
工程技術人員在進行模型房間標注時,需要鼠標移動選擇待標注的模型房間位置發送到中間件,而中間件需要獲取該位置的空間坐標系數據信息,才可以進行下一步的運算.所以,數據表存放著Revit 樓層平面圖中的各樓層房間直角坐標系、樓層信息以及房間名稱及ID 信息.
本方案中BIM 數據庫(Epplus 庫)的構件主要包含2 張數據表,分別為:1)房間名稱及ID 的數據表,保存了BIM 模型中房間名稱及ID 信息;2)房間邊界的數據表,保存了BIM 模型中房間輪廓的坐標數據信息.
為清楚每條記錄都能夠被唯一識別,便于和其相關的語句相映射,需要定義房間名稱及ID 來使數據庫中每條記錄可以被唯一識別,以便于在各個表之間建立主鍵和外鍵關系.表1所示為一個房間存儲信息數據表,以房間的名稱及ID 作為主鍵,其他的字段包括文件名名稱、圍成房間邊界的墻體ID 及門窗等,清晰地反映房間的坐標數據信息.

表1 房間名稱及ID 數據表
以上述幼兒園為例,其中一層活動室101,教室103,二層辦公室201 等樓層分布,如圖4所示.提取直線X、Y 坐標數據信息,經過E p p l us 庫輸出到Excel 表格,其中,表格抬頭分為“樓層信息”、“房間邊界坐標數據”、“房間名稱”、“房間ID”,按組將坐標信息寫入表2某幼兒園生成中間件的Excel 文件.
在Revit 三維視圖下,結合Revit API 判斷待標注的模型房間空間坐標系的位置,匹配中間件內存儲坐標數據,并提取模型待標注信息,將需要標注的房間名稱及ID 信息加載到模型房間中,完成標注.

表2 某幼兒園生成中間件的Excel 文件
2.3.1 Revit 坐標數據匹配算法
在Revit 三維視圖中,獲取模型標注信息,匹配中間件內坐標數據信息算法思路:確定需要標注的模型房間,取其模型房間內任意一點A,獲取該點空間坐標系(xi,yi,zk),令zk代表樓層的高度,(xi,yj)代表樓層房間內的位置,如圖5所示.

圖5 獲取空間坐標系
根據zk的大小,判斷其所屬樓層平面內的樓層范圍,從而獲得樓層ID;再將A點在XY 平面內分別作X 軸、Y 軸垂線,根據(xi,yj)能夠通過與中間件文件內數據的匹配,準確判斷獲得樓層ID 的直角坐標系所屬范圍,從而獲得整個模型房間的名稱及ID.三維模型房間的數據信息匹配算法流程圖,如圖6所示.
具體的判斷方法:在空間坐標系中,令Z 軸代表樓層的高度,X、Y 軸代表樓層房間內水平位置.以圖5的A點為例,根據zk的大小,判斷其樓層平面內的樓層ID,假如 0≤zk≤1,則代表樓層平面的一層;1≤zk≤2,則代表二層.而A點的(xi,yj)坐標代表樓層內的房間位置,判斷該點的(xi,yj)是匹配中間件存儲的直角坐標系數據組的范圍,根據表2生成的.xls 文件,假如x0≤xi≤x1,y0≤yj≤y1,0≤zk≤1,則確定該房間是活動室101;x1≤xi≤x2,y0≤yj≤y1,1≤zk≤2,則確定該房間是教室102,以此類推.

圖6 模型信息數據匹配算法流程圖
訪問中間件存儲數據,需要C#語言在VS 中進行添加引用.dll 文件,以下為連接中間件的程序主要代碼:
string strConnect="Provider=Microsoft.Ace.OleDb.12.0;" + "data source=" + excelFilePath + ";Extended Properties='Excel 12.0;HDR=Yes;IMEX=1'";
2.3.2 Revit 三維視圖中模型信息提取標注
在Revit 三維視圖中,對中間件內存儲的樓層平面內坐標數據的訪問、提取,利用遍歷特征,獲取房間名稱和ID 的屬性信息.提取數據信息的具體流程如圖7所示.
在仿真漫游或是剖面視圖下,采取鼠標觸發事件技術,顯示房間的名稱和ID 信息,利用提示節點(Tooltip)函數功能,提取中間件信息實現標注.當鼠標移動其模型房間區域內任意一點時,模型房間信息將會通過提示節點出現在鼠標旁,適時顯示模型房間的信息;當鼠標移開之后,信息消失.具體實現顯示信息流程如圖8所示.

圖7 Revit 數據信息提取流程圖

圖8 模型房間顯示方式
經過上述中間件房間數據信息的描述,自動標注Revit 三維房間信息中間件框架設計流程圖,如圖9所示.
具體總結為幾個步驟:
Step 1.在Revit 樓層平面圖中,通過Revit API 接口對建筑構件訪問,創建過濾收集器、過濾條件,將房間Room 存入到收集器中.
Step 2.各樓層房間圖元構件利用Transform 函數坐標轉化為統一的模型坐標系,RevitLocation 函數坐標定位獲取房間區域邊界輪廓的坐標數據,利用Revit API 內置參數BuiltInParameter.ELEM_ROOM_NAME獲取房間名稱及ID 字符串信息.
Step 3.結合SQL server,建立基于BIM 數據庫(Epplus 庫),并定義ID 使得數據庫中每條記錄可以被唯一識別,確立各個表之間主鍵和外鍵關系,接著,將Step 2 中提取的房間信息存儲到數據庫,并將數據庫存儲信息寫入Excel 文件.
Step 4.在Revit 三維視圖中,判斷待標注的模型房間空間坐標系的位置,再次借助Revit API 接口,提取空間位置坐標數據.
Step 5.采用坐標數據匹配算法,將空間定位的位置坐標數據組與中間件存儲的直角坐標數據信息進行匹配,提取待標注數據信息.
Step 6.利用鼠標觸發事件,顯示三維視圖中模型房間名稱及ID 信息,完成標注.

圖9 自動標注Revit 三維房間信息中間件設計流程圖
根據分析以上所提出的三維視圖房間標注方法,采用面向對象技術的C#語言,使用Visual Studio 2013 進行代碼編寫,在Autodesk Revit 2016 中進行測試.其中在Revit 中,利用外部命令Addin Manager 來加載運行編譯完成的中間件名稱“fangjian.dll”—點擊“fangjian.Command”—“Run”運行加載模型房間的標注.圖10為Revit 附加模塊生成的中間件.
本測試是以某幼兒園實際工程為案例,一樓三維剖面視圖上進行標注,其標注內容為房間名稱及ID,完成效果如圖11所示.

圖10 在Revit 中運行的中間件

圖11 某幼兒園1 樓活動室三維剖面視圖模型房間標注
Revit 三維模型的標注,將房間的名稱及ID 添加到三維模型中,能夠讓工程技術人員清晰的看到房間的名稱和ID,相比較于之前需要查閱Revit 樓層平面上的標注來確定房間名稱及ID,節省了部分時間,提高了閱圖效率.
本文主要結合Revit 二次開發技術,重點研究模型房間在三維視圖中自動標注房間名稱和ID 信息的方法,具體得到以下結論:
1)本文借助Revit API 接口,研究了Revit 三維模型視圖中自動標注樓層平面房間信息的方法,該方法能夠將樓層平面房間坐標信息提取并存儲到中間件中,而且調用中間件存儲信息自動匹配坐標數據對模型房間標注名稱和ID 信息.
2)以某幼兒園工程為例驗證程序的可行性及穩定性.通過工程圖紙房間測試,能夠看到三維視圖中的房間名稱及ID 信息.給設計人員和審圖人員節省了部分時間,提高了工作效率.
3)本文研究的三維標注模型房間自動標注方法有待更多的工程實例測試,此外,由于BIM 模型本身的復雜性,更多的豐富信息標注待進一步研究,這也為Revit在三維模型標注方面或其他程序開發提供一些經驗.