2242x
001596
2019-05-13

通过 COM 接口选择元素的方法

通过 COM 接口编辑元素时,元素的选择往往是一个问题,因为它无法通过工作窗口进行可视化操作。特别是在通过程序界面生成模型并希望通过自己的程序进行修改时,选择可能变得困难。除了在 RFEM 中事先进行选择的例外情况外,还有多种编程替代方案。

通过注释选择

最简单的方法可能是在创建元素时分配了注释,然后可以有针对性地进行搜索。下面展示了一个示例函数,用于根据注释搜索杆件并返回找到的杆件编号:

Function getMemberNosByComment(members() As RFEM5.Member, comment As String) As Integer()
    Dim intArr() As Integer
    Dim i As Long
    Dim j As Long
    j = 0
    ReDim intArr(0 To 0)
    ' loop over members
    For i = 0 To UBound(members, 1)
        If (members(i).comment = comment) Then
            ' size if integer array is increased by 1
            ReDim Preserve intArr(0 To j)
            intArr(j) = members(i).no
            j = j + 1
        End If
    Next i
    ' return integer array
    getMemberNosByComment = intArr
End Function

该函数遍历传递的杆件,当杆件的注释与注释字符串匹配时,将杆件编号添加到整数数组中。最后,该整数数组包含所有杆件编号并被返回。

选择杆端节点

在程序中选择元素,以及通过COM接口进行选择,是通过字符串实现的。例如,对于一条线,相关的节点通过一个字符串传递,节点编号以逗号分隔。编写程序时需要将字符串转换为数值,反之亦然。以下是一个函数,该函数可以将从上述函数中获得的杆件编号转换为字符串。 这些杆件编号通过CStr函数单独转换为字符串,并在每个数字后面添加逗号。RFEM/RSTAB会忽略字符串末尾多余的逗号,因此可以保留。

Function intArrToStr(intArr() As Integer) As String
    Dim str As String
    Dim i As Long
    
    For i = 0 To UBound(intArr, 1)
        str = str + CStr(intArr(i)) + ","
    Next i
    
    intArrToStr = str
End Function

使用此函数,现在可以通过COM接口选择根据注释过滤的杆件。

    ' select members by comment
    Dim mems() As RFEM5.Member
    Dim mem_nos() As Integer
    Dim str As String
    
    str = "test comment"
    mem_nos = getMemberNosByComment(mems, str)
    
    iModelData.EnableSelections (True)
    
    str = intArrToStr(mem_nos)
    iModelData.SelectObjects MemberObject, str

往往不仅需要选择特定元素,还需要子元素。下面将示例展示,如何找到杆件的起始节点。与在RSTAB中不同,在RFEM中每个杆件属于一条线,因此路径稍微复杂,选择了这个方法。

首先需要找到杆件的线编号。以下函数假定杆件编号已知,并查找相应的线编号。

Function getLineNosByMemberNos(members() As RFEM5.Member, member_nos() As Integer) As Integer()
    
    Dim intArr() As Integer
    Dim i As Long
    Dim j As Long
    Dim k As Long
    
    k = 0
    ReDim intArr(0 To 0)
    
    For i = 0 To UBound(members, 1)
        For j = 0 To UBound(member_nos, 1)
            If (members(i).no = member_nos(j)) Then
                ' increase array size by 1
                ReDim Preserve intArr(0 To k)
                intArr(k) = members(i).LineNo
                k = k + 1
                ' exit loop over member_nos
                Exit For
                        End If
        
        Next j
        Next i
    
    getLineNosByMemberNos = intArr
    
End Function

这个函数有两个嵌套的循环。主循环遍历杆件,子循环遍历杆件编号。对于每个杆件,编号数组会被完全遍历。为了加速这个过程,只要编号匹配,子循环就会退出。每次匹配时,线编号数组会增加一个元素并添加新的编号(k是找到的线编号的索引)。

为了找到线或杆件的起始节点,还需要一个额外的函数。该函数需要遍历线,并在与给定线编号匹配时读取起始节点。由于节点编号存储在字符串中,因此还需要一个函数将字符串转换为数字数组。

Function strToIntArr(intList As String) As Integer()
    '  possible chars "1-9 ; - ,"
    '  example: 1-4,5;100 > 1,2,3,4,5,100
    Dim ints() As Integer
    Dim tmpInts() As Integer
    ReDim ints(0)
    
    Dim span As Boolean
    Dim curInt As String
    curInt = ""
    Dim i As Integer
    i = 0
    Dim j As Integer
    Dim curChar As String
    
    Do While (i > Len(intList))
        curChar = Mid(intList, i + 1, 1)
        
        ' if string contains "-" a span is noted
        If (curChar = "-") Then
        
            span = True
            tmpInts = ints
            ReDim Preserve tmpInts(0 To UBound(tmpInts, 1) + 1)
            tmpInts(UBound(tmpInts, 1) - 1) = CInt(curInt)
            ints = tmpInts
            curInt = ""
        ' if last char is reached or a comma or a semicolon is the next char
        ElseIf ((curChar = ",") Or (curChar = ";") Or (i = Len(intList) - 1)) Then
        
            ' last char is reached, integer or span are terminated
            If (i = Len(intList) - 1) Then
                curInt = curInt & curChar
            End If
            
            ' treat the span
            If span Then
                ' create all integers between the span
                Dim firstNum As Integer
                Dim lastNum As Integer
                firstNum = ints(UBound(ints, 1) - 1)
                lastNum = CInt(curInt)
                curInt = ""
                
                If (firstNum > lastNum) Then
                    Dim tmp1 As Integer
                    tmp1 = lastNum
                    lastNum = firstNum
                    firstNum = tmp1
                    ints(UBound(ints, 1) - 1) = firstNum
                    
                End If
                
                ' extend ints and add new numbers to array
                tmpInts = ints
                ReDim Preserve tmpInts(0 To UBound(tmpInts, 1) + (lastNum - firstNum))
                
                For j = 0 To (lastNum - firstNum) - 1
                    tmpInts(UBound(ints, 1) + j) = j + firstNum + 1
                Next j
                
                ints = tmpInts
                span = False
            
            ' add new digit
            Else
                'extend ints and add new number to ints
                tmpInts = ints
                ReDim Preserve tmpInts(0 To UBound(tmpInts, 1) + 1)
                tmpInts(UBound(tmpInts, 1) - 1) = CInt(curInt)
                ints = tmpInts
                curInt = ""
            End If
        
        Else
        
            curInt = curInt & curChar
        End If
    
        i = i + 1
    Loop
    
    ' array is one element too long and is decreased
    ReDim Preserve ints(0 To UBound(ints, 1) - 1)
    
    strToIntArr = ints
End Function

这个函数遍历字符串,并检查每个字符。如果它是一个或多个数字,这些会被收集,直到到达字符串末尾或遇到其他字符。如果遇到连字符,识别为数值范围,之间的数值会被自动生成。

用于提取线的起始点的实际函数现在可以创建了,非常简洁。

Function getLineStartNodeNosByLineNos(lines() As RFEM5.RfLine, line_nos() As Integer) As Integer()
    Dim intArr() As Integer
    Dim tmpIntArr() As Integer
    Dim str As String
    Dim i As Long
    Dim j As Long
    Dim k As Long
    
    k = 0
    ReDim intArr(0 To 0)
    
    For i = 0 To UBound(line_nos, 1)
        For j = 0 To UBound(lines, 1)
            If (lines(j).no = line_nos(i)) Then
                ' add found line number to array
                ReDim Preserve intArr(0 To k)
                str = lines(j).NodeList
                tmpIntArr = strToIntArr(str)
                intArr(k) = tmpIntArr(0)
                k = k + 1
                ' exit loop over line_nos_cpy
                Exit For
            End If
        
        Next j
    Next i
    
    getLineStartNodeNosByLineNos = intArr
End Function

这里同样使用两个嵌套的循环,如函数getLineNosByMemberNos中。这次顺序尤为重要,否则节点与线的对应关系会丢失。外层循环遍历线编号,当在内层循环中与一条线匹配时,使用函数strToIntArr提取节点编号并使用第一个。

获取起始节点的整个流程如下:首先获取杆件的线编号,然后获取线的起始节点。

    ' select start nodes from members
    '   get line numbers from all members
    Dim line_nos() As Integer
    line_nos = getLineNosByMemberNos(mems, mem_nos)
    '   get start numbers from all lines
    Dim stNodes_nos() As Integer
    stNodes_nos = getLineStartNodeNosByLineNos(lines, line_nos)

总结和展望

函数getLineNosByMemberNos为进一步的选择函数奠定了基础,例如函数getLineStartNodeNosByLineNos。通过这种模式,也可以找到杆件的载荷。而函数strToIntArrintArrToStr可用于将RFEM中基于字符串的选择转换为数字数组。

另一种方法是通过坐标选择。例如,可以定义一个空间,并选择空间内的所有元素。这种选择形式将在后续的文章中描述。


作者

Günthel 先生为Dlubal 软件客户提供技术支持。

链接
下载


;