在ANSA二次开发中经常获取当前视角下被选中单元和视角一致的法向量以备后续操作。本文主要通过创建视角向量和被选中单元的法向量来展示这一过程。
首先,需要对中的错误进行更正:ansa中GetViewAngles函数旋转顺序为Z->Y->Z;且以顺时针为正,和一般笛卡尔坐标相反。因此有:
import math
from typing import List, Tuple
from ansa import base
from ansa import constants
from ansa import calc
class ANSATYPE(object):
'''
不同求解器对应关键字名字
'''
@class method
def eshell(cls, deck:int)->str:
typDict:dict = {
constants.LSDYNA: 'ELEMENT_SHELL',
constants.ABAQUS:'SHELL',
constants.NASTRAN: 'SHELL',
constants.RADIOSS: 'SHELL',
constants.ANSYS: 'SHELL',
22: 'SHELL', # OPTISTRUCT
23: 'SHELL' # MARC
}
try:
_type = typDict[deck]
except KeyError:
raise ValueError('ERROR: 模型Deck设置有误')
return _type
@class method
def node(cls, deck:int)->str:
typDict:dict = {
constants.LSDYNA: 'NODE',
constants.ABAQUS:'NODE',
constants.NASTRAN: 'GRID',
constants.RADIOSS: 'NODE',
constants.ANSYS: 'NODE',
22: 'GRID', # OPTISTRUCT
23: 'NODE' # MARC
}
try:
_type = typDict[deck]
except KeyError:
raise ValueError('ERROR: 模型Deck设置有误')
return _type
class Vector(object):
@class method
def point2Point(cls, point1:List[float], point2:List[float], unit:bool=True)->List[float]:
'''
计算两点构成的向量. 向量由point1指向point2,即point2-point1
'''
if isinstance(point1, base.Entity): point1 = point1.position
if isinstance(point2, base.Entity): point2 = point2.position
vect:List[float] = [p2-p1 for p1, p2 in zip(point1, point2)]
return cls.unit(vect) if unit else vect
@staticmethod
def unit(vector:List[float])->List[float]:
'''
将一个向量单位化
'''
return calc.Normalize(vector)
@staticmethod
def distance(point1:List[float], point2:List[float])->float:
'''
计算两点间的距离
'''
if isinstance(point1, base.Entity): point1 = point1.position
if isinstance(point2, base.Entity): point2 = point2.position
return math.dist(point1, point2)
@class method
def angleCos(cls, vector1:List[float], vector2:List[float])->float:
'''
计算两个空间向量的夹角的余弦值。
'''
unit_vect01 = cls.unit(vector1)
unit_vect02 = cls.unit(vector2)
return sum([v1*v2 for v1, v2 in zip(unit_vect01, unit_vect02)])
@staticmethod
def angle(vector1:List[float], vector2:List[float])->float:
'''
计算两个空间向量的夹角,返回值以弧度形式存在
'''
return calc.CalcAngleOfVectors(vector1, vector2)
@class method
def elemNormal(cls, shellElem:base.Entity)->List[float]:
'''
根据单元的前三个节点计算单元的法向量
'''
vect:List[float] = base.GetNormalVectorOfShell(shellElem)
if isinstance(vect, list):
return vect
else:
nodeType:str = ANSATYPE.node(DECK)
nodes:List[base.Entity] = base.CollectEntities(DECK, shellElem, nodeType, recursive=True)
return cls.vec3Point(nodes[0], nodes[1], nodes[2])
@class method
def vec3Point(cls, point1:List[float], point2:List[float], point3:List[float])->List[float]:
'''
计算空间三点构成平面的法向量
'''
vect1:List[float] = cls.point2Point(point1, point2)
vect2:List[float] = cls.point2Point(point1, point3)
return calc.CrossProduct(vect1, vect2)
def getViewVector()->List[float]:
'''
通过视角函数获取视角旋转角并将其装换为视角向量
'''
# 获取视角转动角
angles:List[float] = base.GetViewAngles()
angleRad = list(map(math.radians, angles))
angleSin: List[float] = list(map(math.sin, angleRad))
angleCos: List[float] = list(map(math.cos, angleRad))
# 计算视角向量三分量
x:float = -angleSin[1]
y:float = angleSin[0]*angleCos[1]
z:float = angleCos[0]*angleCos[1]
return [x, y, z]
def getShellViewVector(elem:base.Entity)->List[float]:
'''
名称:
getShellViewVector
描述:
获取shell单元与当前视角方向一致的法向向量
参数:
elem object shell类型单元
返回值:
返回一个向量(list),该向量表示输入shell单元与当前视角一致的单元法向量。
'''
# 对输入对象进行参数检查
if not isinstance(elem, base.Entity):
raise TypeError('请输入ansa对象')
shellType = ANSATYPE.eshell(DECK)
elemType = elem.ansa_type(DECK)
if elemType != shellType and elemType != 'SOLIDFACET':
raise TypeError(f'''输入ansaType类型有误, 此函数要求输入的为shell单元或solid单元的表面类型(SOLIDFACET), 现输入类型为{elemType}''')
# 获取单元法向量
elemVect:List[float] = Vector.elemNormal(elem)
# 获取视角向量
viewVect:List[float] = getViewVector()
# 计算两个向量的夹角,若为锐角,单元法向量为所求方向;若为钝角,单元法向量反方向为所求方向
angle:float = Vector.angle(elemVect, viewVect)
if angle==-math.pi:
raise ValueError(f'输入向量有误, 现输入向量为{elemVect}和{viewVect}, 请核查')
if angle>math.pi/2.0:
elemVect = [-vect for vect in elemVect]
return elemVect
def calcCog(entities:List[base.Entity])->List[float]:
'''
名称:
calcCog
描述:
计算输入的entities对象的cog
参数:
entities List[base.Entity] 存在cog的可迭代对象或entity对象
返回值:
返回一个Cog向量(list)
'''
if isinstance(entities, base.Entity): entities = [entities]
length = len(entities)
if length==0: return[0.0, 0.0, 0.0]
if length==1: return base.Cog(entities[0])
x, y, z = 0.0, 0.0, 0.0
for ent in entities:
cog = base.Cog(ent)
x += cog[0]
y += cog[1]
z += cog[2]
return [x/length, y/length, z/length]
def getMinDistEntityFromPoint(point:List[float], entities:List[base.Entity])->base.Entity:
'''
名称:
getMinDistEntityFromPoint
描述:
从输入的entities对象中选出距point最近的对象。
参数:
point List[float] 参考点坐标
entities List[base.Entity] 存在cog的可迭代对象或entity对象
返回值:
返回距point点最近的对象。
'''
if not entities: return None
if isinstance(entities, base.Entity): return entities
if len(entities)==1: return entities[0]
minDistance:float = math.inf
minEntity:base.Entity = None
for ent in entities:
cog = base.Cog(ent)
distance = Vector.distance(cog, point)
if distance<minDistance:
minDistance = distance
minEntity = ent
return minEntity
def main():
types:Tuple[str] = (ANSATYPE.eshell(DECK), 'SOLIDFACET')
entities:List[base.Entity] = base.PickEntities(DECK, types, initial_type=types[0])
if not entities: return None
if len(entities)==1:
entity = entities[0]
cog = base.Cog(entity)
else:
cog = calcCog(entities)
entity = getMinDistEntityFromPoint(cog, entities)
vect = getShellViewVector(entity)
fields = {'XT': cog[0], 'YT': cog[1], 'ZT': cog[2],
'XH': cog[0]+vect[0], 'YH': cog[1]+vect[1], 'ZH': cog[2]+vect[2],
'Name': 'element normal vector'}
base.CreateEntity(DECK, 'DEFINE_VECTOR', fields)
viewVect = getViewVector()
fields = {'XT': cog[0], 'YT': cog[1], 'ZT': cog[2],
'XH': cog[0]+viewVect[0], 'YH': cog[1]+viewVect[1], 'ZH': cog[2]+viewVect[2],
'Name': 'view vector'}
base.CreateEntity(DECK, 'DEFINE_VECTOR', fields)
return None
if __name__ == '__main__':
main()