การเช็คการชนกันของวัตถุต่างๆใน Revit เป็นเทคนิคที่ถูกนำไปใช้ในหลายกรณี เช่นการเช็คระยะความสูงฝ้าเพดาน เช็คการเว้นระยะของวัตถุ เช็คว่าหัวคนจะชนท่อไหม โดยรวมคือการเช็คว่าวัตถุชนกันหรือไม่นั่นเอง ซึ่งใน Revit สามารถทำได้ทั้งจากใน Dynamo และด้วย RevitAPI ในบทความนี้จะนำเสนอวิธีการเช็คการชนกัน (Intersection) ด้วย RevitAPI (Python) ระหว่าง Solid และ Element ด้วย ElementIntersectsSolidFilter
เคสที่หลายคนมักนำไปใช้คือการเช็คความสูงฝ้าเพดาน หรือเช็คว่ามีพื้น หรือ Element อะไรโผล่เข้ามาในห้องหรือไม่ โดยในตัวอย่างนี้เราจะเน้นไปที่การเช็คการชนกันของ Solid และ Element โดย Solid คือห้อง และ Element จะเป็นฝ้าเพดาน หลังคา เสา ผนัง และพื้น
โมเดลที่ผมจะใช้เป็นตัวอย่างคือห้องที่มีฝ้าเพดานสูงในระดับที่แตกต่างกันตั้งแต่ระยะ 2.3 เมตร ไปจนถึง 2.6 เมตร และจะมีพื้นกับผนังที่ทะลุข้ามกำแพงเข้ามาอยู่บริเวณฝ้าเพดานสำหรับห้องซ้ายสุดและขวาสุด
ภาพที่ 1 แสดง 3D Section View ของห้องตัวอย่าง
เมื่อ Python Script ที่เราเตรียมไว้ทำงาน เราจะสามารถระบุความสูงจากพื้นห้องเพื่อเช็คการ Intersect กับ Elements ได้
ภาพที่ 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 ของเรา
ภาพที่ 3 แสดง Alert Dialog บอกจำนวน Element ที่มีการ Intersect
ภาพที่ 4 แสดง Element ที่ถูกเลือกเมื่อ Intersect กับ Room Solid
คำสั่งที่สำคัญของ 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 หรือห้อง ซึ่งจะอธิบายในครั้งต่อๆไป