Revit獲取元素Solid和計算包圍盒

5YWo發表於2024-03-19

Revit獲取元素Solid和計算包圍盒

前言

Revit外掛開發中,獲取元素Solid和準確獲取元素的包圍盒是很有用的。比如說直接獲取的包圍盒可能很大,會有BoundingBoxIntersectsFilter過濾不準的問題,若能夠“準確”的獲取包圍盒,就可以解決這個問題。

在過去一段時間的外掛開發過程中,獲取元素包圍盒這部分出過幾次錯,挺有意思的,便寫一寫這方面的知識。


獲取元素的所有Solid

獲取Solid

要準確獲取元素的包圍盒,需要先獲取到該元素的所有Solid,這裡先講一下元素Solid的獲取。

檢視Revit API文件,搜尋Solid,有程式碼示例。

首先,我們得知道獲取元素Solid的方法:

// 選自Revit API Solid class示例
// Get geometry element of the selected element
Autodesk.Revit.DB.GeometryElement geoElement = element.get_Geometry(geoOptions);

get_Geometry方法要求要填寫一個Options型別引數,這裡不展開。

Options屬性,請檢視API文件。其各屬性設定效果,請使用RevitLookup外掛檢視。

至此,我們就能夠獲取到元素的Solid了。但是這並不夠,一個元素是可能存在巢狀族的,所以我們還需要獲取到巢狀族的Solid才行。

說到這裡,就需要用到上一篇《獲取元素的巢狀族》中的知識了。

獲取到巢狀族,再獲取巢狀族的Solid,這樣就能拿到元素所有Solid了。

問題點記錄

以下程式碼是獲取元素本身Solid的遞迴部分,其中有幾個問題點需要注意。

// 獲取元素Solid
private static void GetSolidsFromElement_Recursion(GeometryElement geometryElement, ref List<Solid> solids)
{
    foreach (GeometryObject geomObj in geometryElement)
    {
        if (geomObj is Solid solid)
        {
            if (solid.Faces != null && solid.Faces.Size > 0) // && solid.Edges != null && solid.Edges.Size > 0 
            {
                solids.Add(solid.Clone());
            }
        }
        else if (geomObj is GeometryInstance geometryInstance)
        {
            //GeometryElement transformedGeomElem = geometryInstance.GetInstanceGeometry(geometryInstance.Transform); 
            GeometryElement transformedGeomElem = geometryInstance.GetSymbolGeometry(geometryInstance.Transform); // apply transform
            GetSolidsFromElement_Recursion(transformedGeomElem, ref solids);
        }
    }
}

示例程式碼上有兩處註釋,第一處是過濾掉空Solid,第二處是矩陣轉換。

註釋2:應使用GetSymbolGeometry(transform),不能使用GetInstanceGeometry(...),至於原因,說實話我沒看懂API文件講了個啥,懂的小夥伴可以評論下。

註釋1:不可使用Solid.Edges進行判斷!雖然示例文件用了,但那不行,因為Solid可以沒有邊

上面提到“Solid可以沒有邊“,怎麼回事兒呢?

這就涉及到Revit模型的表示方法了,B-Rep(Boundary representation)表示法,這是後話了,總之不可以就是了。

P.S.(有個錘子的後話,本來是想寫寫,查查資料發現沒看懂,算了算了ㄟ( ▔, ▔ )ㄏ)

舉個例子:圓輪廓的閉合圓形放樣(甜甜圈)就沒有邊,只有面。

甜甜圈


元素包圍盒

當我們知道如何獲取到元素的Solid後,我們就可以“準確”計算出元素的包圍盒了。

“準確”:指的是實際Solid組成的包圍盒。

Element.BoundingBoxSolid.GetBoundingBox()是不同的。

element.get_BoundingBox():一個“可放置族”,族內部可能存在不可見的元素,這些物體也會參與到包圍盒計算中,導致最終計算出的包圍盒可能要大很多,而不是預期的剛好包住模型面。

// 部分程式碼
// 遍歷所有Solid
foreach (Solid solid in solids)
{
    BoundingBoxXYZ box = solid.GetBoundingBox();
    Transform transform = box.Transform;

    XYZ boxMinTransform = transform.OfPoint(box.Min);
    XYZ boxMaxTransform = transform.OfPoint(box.Max);

    min = min.Min(boxMinTransform).Min(boxMaxTransform);  // xx.Min(xx)是擴充套件方法,就是對三個點都取小的
    max = max.Max(boxMinTransform).Max(boxMaxTransform);
}

總結

不總結了,寫累了,完!

相關文章