เช็คการชนกันของ Element ใน Revit ด้วย Revit API

Patana Rattananavathong 25/Nov/2024

การเช็คการชนกันของวัตถุต่างๆใน Revit เป็นเทคนิคที่ถูกนำไปใช้ในหลายกรณี เช่นการเช็คระยะความสูงฝ้าเพดาน เช็คการเว้นระยะของวัตถุ เช็คว่าหัวคนจะชนท่อไหม โดยรวมคือการเช็คว่าวัตถุชนกันหรือไม่นั่นเอง ซึ่งใน Revit สามารถทำได้ทั้งจากใน Dynamo และด้วย RevitAPI ในบทความนี้จะนำเสนอวิธีการเช็คการชนกัน (Intersection) ด้วย RevitAPI (Python) ระหว่าง Solid และ Element ด้วย ElementIntersectsSolidFilter

เช็ค Head Clearance ของห้อง

เคสที่หลายคนมักนำไปใช้คือการเช็คความสูงฝ้าเพดาน หรือเช็คว่ามีพื้น หรือ Element อะไรโผล่เข้ามาในห้องหรือไม่ โดยในตัวอย่างนี้เราจะเน้นไปที่การเช็คการชนกันของ Solid และ Element โดย Solid คือห้อง และ Element จะเป็นฝ้าเพดาน หลังคา เสา ผนัง และพื้น

โมเดลที่ผมจะใช้เป็นตัวอย่างคือห้องที่มีฝ้าเพดานสูงในระดับที่แตกต่างกันตั้งแต่ระยะ 2.3 เมตร ไปจนถึง 2.6 เมตร และจะมีพื้นกับผนังที่ทะลุข้ามกำแพงเข้ามาอยู่บริเวณฝ้าเพดานสำหรับห้องซ้ายสุดและขวาสุด

Sample Model for Room Solid Intersect Element RevitAPI Python

ภาพที่ 1 แสดง 3D Section View ของห้องตัวอย่าง

เมื่อ Python Script ที่เราเตรียมไว้ทำงาน เราจะสามารถระบุความสูงจากพื้นห้องเพื่อเช็คการ Intersect กับ Elements ได้

ระบุความสูงจากพื้นห้องเพื่อเช็ค Intersection ด้วย RevitAPI

ภาพที่ 2 แสดง Dialog เพื่อระบุความสูง

เมื่อกด OK กระบวนการสร้าง Solid จาก Room จะเริ่มขึ้นด้วย

        
GeometryCreationUtilities.CreateExtrusionGeometry(roomLoop, XYZ.BasisZ, UnitUtils.ConvertToInternalUnits(height, UnitTypeId.Meters))
"""
roomLoop - List[CurveLoop]([])  ของแต่ละห้อง
height - ความสูงตามที่ระบุ
"""
        
    

จากนั้น Solid เหล่านี้จะเข้าไปใน Filter ที่ชื่อว่า ElementIntersectsSolidFilter และนำไปใช้เป็น Filter ใน FilteredElementCollector โดยเว็บ RevitAPIDocs ได้แสดงตัวอย่างเป็นภาษา C# ไว้ดังนี้

        
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfClass(typeof(FamilyInstance));
collector.WherePasses(new ElementIntersectsSolidFilter(solid)); // Apply intersection filter to find matches
        
    

เมื่อแปลงเป็นภาษา Python ก็เหลือดังนี้

        
collector = FilteredElementCollector(doc)
collector = collector.OfClass(FamilyInstance)
collector = collector.WherePasses(ElementIntersectsSolidFilter(solid))
## Apply intersection filter to find matches
        
    

ถ้าหากมี Element ที่ชนกับ Solid ของห้อง จะมีหน้าต่างแสดงจำนวนให้รู้ว่ามีกี่ชิ้น พร้อมกับการเลือก Element เหล่านั้น เพื่อให้เราสามารถเห็นได้ทันทีว่า Element ไหนบ้างที่ Intersect กับ Solid ของเรา

Alert บอกจำนวน Element ที่มีการชนกับ Solid ของห้อง

ภาพที่ 3 แสดง Alert Dialog บอกจำนวน Element ที่มีการ Intersect

Element จะถูกเลือกถ้าหาก Intersect กับ Room Solid

ภาพที่ 4 แสดง Element ที่ถูกเลือกเมื่อ Intersect กับ Room Solid

อธิบาย Code

คำสั่งที่สำคัญของ RevitAPI ที่ใช้เพื่อเช็ค Intersection ระหว่าง Solid และ Element คือ ElementIntersectsSolidFilte โดยจะรับ Solid ที่ต้องการหา Intersection เข้าไป เมื่อใช้งานจะเป็น

ElementIntersectsSolidFilter(solid)

จากนั้นให้ใส่ Filter ด้านบนไปใน WherePasses ซึ่งเป็น method ของ FilteredElementCollector ก็จะได้ Element ที่ Intersect กับ Solid มา

ในกรณีที่ต้องการเช็คกับ Element หลายประเภท การใช้ ElementMulticategoryFilter จะทำให้สะดวกมากขึ้น

สุดท้ายก็จบด้วย

uidoc.Selection.SetElementIds(selectedIds) เพื่อเลือก Element ที่ Intersect กับ Solid

ส่วนหลักของ Code ที่ใช้ในกรณีนี้แสดงไว้ด้านล่าง

        
categories = List[BuiltInCategory]([
	BuiltInCategory.OST_Walls,
    BuiltInCategory.OST_Floors,
    BuiltInCategory.OST_Roofs,
    BuiltInCategory.OST_Columns,
    BuiltInCategory.OST_Ceilings,
    ### มีอีกก็ใส่อีก
])

## Filter เพื่อหา Elements ใน Category เหล่านี้
multi_category_filter = ElementMulticategoryFilter(categories) 

for room in rooms:
    solid = CreateSolidFromRoomWithHeight(room, room_height) ## สร้าง Solid จาก Room
    filter =  ElementIntersectsSolidFilter(solid) ## สร้าง Intersect Filter
    intersected_elements = FilteredElementCollector(doc)\
        .WhereElementIsNotElementType()\
            .WherePasses(multi_category_filter)\
                .WherePasses(filter)\
                    .ToElements() ## หา Intersect
    for interfound in intersected_elements: ## Loop หาตัว Elements ที่เจอใน intersected_elements
        if interfound.Id not in already_intersected_ids: ## เช็คว่าเราเจอ element นี้ไปแล้วหรือยัง
            intersects.append(interfound) ## ถ้ายังก็ใส่เข้าไป
            selectedIds.Add(interfound.Id) ## ถ้ายังก็ใส่ Id เข้าไป
            already_intersected_ids.append(interfound.Id) ## จำเลข id เอาไว้เช็คครั้งต่อไป

if len(intersects) > 0:
    forms.alert('มี Element(s) จำนวน {} ชิ้น ที่ Intersect'.format(len(intersects)))

## เลือก Element ที่ Intersects กับ elements
uidoc.Selection.SetElementIds(selectedIds)
        
    

สรุป

การเช็คการชนกันของ Element กับ Solid สามารถทำได้ด้วยการใช้ ElementIntersectsSolidFilter ซึ่งเป็น Filter ที่ใช้งานคู่กับ FilteredElementCollector แม้ว่าในบทความนี้จะใช้ตัวอย่างของห้อง แต่ในความเป็นจริงแล้วสามารถเป็น Solid จาก Element ใดๆก็ได้ ซึ่งเราสามารถหา Solid ได้จาก Element ด้วย Element.get_Geometry(options)

ในฝั่งของ Element ที่ต้องการจะเช็คการชนกับ Solid สามารถใช้ OfClass หรือ OfCategory เพื่อระบุประเภทของ Element ที่ต้องการ แต่ในกรณีที่ต้องการ Element จากหลาย Category ก็สามารถใช้ ElementMulticategoryFilter ได้

ส่วนสำคัญที่ไม่ได้ถูกอธิบายในบทความนี้คือการสร้าง Solid ขึ้นมาจาก Room หรือห้อง ซึ่งจะอธิบายในครั้งต่อๆไป