Revit API 中的图元高级过滤

问题描述 投票:0回答:3

我遵循此 YouTube 视频 (https://www.youtube.com/watch?v=WU_D2qNnuGg&index=7&list=PLc_1PNcpnV5742XyF8z7xyL9OF8XJNYnv) 中显示的示例,该示例说明了 Revit API 中的过滤方法相对于通常迭代的优越性。但我的代码比迭代方法慢得多:
过滤方式-0.16秒
迭代法-0.06秒

我使用过滤方法的代码是:

import Autodesk.Revit.DB as DB
doc=__revit__.ActiveUIDocument.Document
uidoc=__revit__.ActiveUIDocument

height_param_id=DB.ElementId(DB.BuiltInParameter.WALL_USER_HEIGHT_PARAM)
height_param_prov=DB.ParameterValueProvider(height_param_id)
param_equality=DB.FilterNumericEquals() # equality class
height_value_rule=DB.FilterDoubleRule(height_param_prov,param_equality,10,1e-02)
param_filter=DB.ElementParameterFilter(height_value_rule)

# This program significantly slows down for the next line
walls=DB.FilteredElementCollector(doc)\
                        .WherePasses(param_filter)\
                        .ToElementIds()

uidoc.Selection.SetElementIds(walls) 

对于迭代,使用了以下代码。

from System.Collections.Generic import List 
import Autodesk.Revit.DB as DB

doc=__revit__.ActiveUIDocument.Document
uidoc=__revit__.ActiveUIDocument

sheet_collector=DB.FilteredElementCollector(doc)\
             .OfCategory(DB.BuiltInCategory\
             .OST_Sheets)\
             .WhereElementIsNotElementType()\
             .ToElements()

walls=DB.FilteredElementCollector(doc)\
                        .OfCategory(DB.BuiltInCategory.OST_Walls)\
                        .WhereElementIsNotElementType()\
                        .ToElements()
tallwallsids=[]

for wall in walls:
    heightp=wall.LookupParameter('Unconnected Height')
    if heightp and heightp.AsDouble()==10: 
        tallwallsids.append(wall.Id)

uidoc.Selection.SetElementIds(List[DB.ElementId](tallwallsids)) 
revit-api revitpythonshell pyrevit
3个回答
1
投票

如果您考虑这两种方法必须考虑的元素数量,这是有道理的。第一种方法:

walls=DB.FilteredElementCollector(doc)\
                        .WherePasses(param_filter)\
                        .ToElementIds()

在此方法中,您要求过滤器考虑模型中的所有元素。这可能有很多元素要通过过滤器。这反对:

walls=DB.FilteredElementCollector(doc)\
                        .OfCategory(DB.BuiltInCategory.OST_Walls)\
                        .WhereElementIsNotElementType()\
                        .ToElements()

在此方法中,您使用 QUICK 过滤器

OfCategory()
和另一个
WhereElementIsNotElementType()
将选择范围缩小到仅
Wall
实例。尽管您使用一个简单的
for
循环来完成此操作(这里是较慢的组件),但它仍然比通过第一个过滤器传递模型中的所有元素更快。

您可以通过创建过滤器来优化它,如下所示:

walls=DB.FilteredElementCollector(doc)\
                        .OfCategory(DB.BuiltInCategory.OST_Walls)\
                        .WhereElementIsNotElementType()\
                        .WherePasses(param_filter)
                        .ToElements()

这实际上将快速类别过滤器、元素类型过滤器和慢速参数过滤器结合起来,可能成为一个更快、更容易阅读的整体解决方案。

尝试一下,让我知道这是否有意义。

干杯!


0
投票

什么迭代方法?

如今,过滤图元收集器通常是检索和迭代 Revit 数据库图元的唯一方法。

过滤元素收集器本身可能很快。

如果您的墙壁数量巨大且内存有限,则调用

ToElementIds
可能会消耗大量资源。

SetElementIds
也可能会花费时间。

查看有关 按管道系统类型过滤的图元收集器的广泛 Revit API 论坛讨论,了解更多相关信息。

我建议您提供一个完整的最小可重现示例案例,并为每个方法调用配备基准测试代码,以证明性能下降。


0
投票

所以原来的帖子中并没有提出问题,所以我将其解释为“为什么我的代码较慢?”。我并不是要过分批评,因为康拉德是正确的,但他的回答所包含的内容还不止于此。 他指出使用快速过滤器和慢速过滤器是正确的。

尽可能使用快速过滤器。在将元素扩展到内存之前(这就是它们如此快的原因),它们在 ElementRecord 上进行操作(即类似于 Revit DB 内部元素数据类型的文件头)。如果你不能使用快速,慢速也可以。

具体来说,类别和类别过滤器是我最喜欢的一些快速过滤器。

但是,当涉及到慢速过滤器(尤其是参数过滤逻辑)时,我更喜欢将 Lambda 表达式与 LINQ 库一起使用,而不是 ElementParameterFilter(恕我直言,其语法过于繁琐)。

我的查询看起来像这样: (在 C# 语法中)

List<Wall> filteredWalls = new FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Walls)
    .OfClass(typeof(Wall))
    //At this point, Elements are Marshalled into memory
    .Cast<Wall>()
    //At this point, elements are cast as Wall instead of Element
    //for Revit Versions that utilize BuiltInParameter -> see also newer ForgeTypeId
    .Where(w => w.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)?.AsDouble() == 10.0)
    .ToList();

在Python中,类别和类过滤语法看起来很相似,但您可以使用列表理解来代替LINQ库(C#独有): (在 Python 语法中 - 分隔以突出显示后过滤)

collector_results = DB.FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Walls)
    .OfClass(typeof(Wall))
    .ToList();
    
paramFilteredWalls = [w for w in collector_results if w.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM) != None and w.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble() == 10.0]

你可以使用Python方法进一步抽象最终的过滤逻辑:

def MeetsHeightRequirement(wall, required_height = 10.0) -> bool:
    height_param = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)
    return height_param != None and height_param.AsDouble() == required_height

这会让你的最终理解看起来像这样:

paramFilteredWalls = [w for w in collector_results if MeetsHeightRequirement(w)]

祝你好运!

© www.soinside.com 2019 - 2024. All rights reserved.