地形(Topography)是Revit中特殊一种实体类型,当需要在Dynamo中对地形进行分析、处理时,首先需要将Revit的地形实体转换成常用的Dynamo支持的图形类型:Surface或者Solid。但是Dynamo解析地形不会生成需要的Surface,而是Mesh。在Dynamo里面,既有单独的Topography.Mesh节点,也可以使用Element.Geometry节点来生成地形对应的Mesh。
Mesh在Dynamo里面需要特殊的处理,而且Mesh本身均是一系列的小三角形面,光滑度上远远不够,在Geometry的节点库中,对Mesh的处理节点也非常有限,因此基于Mesh的很多后继处理工作会比较困难。这种时候,或者需要另外搜素下载MeshToolKit的软件包,从中找出更多对Mesh进行处理的节点,或者使用该软件包及内置节点组合将Mesh转换成PolySurface。这样很多后继的处理可以在PolySurface上进行而Dynamo提供了大量的PolySurface操控节点。
使用Dynamo内置节点将Mesh转换成PolySurface,需要提取整个Mesh的点集合,然后根据点位索引重建一个个小三角面的Surface(提取3个点,连线成闭合三角形,再Patch三角形成Surface),最后组合成一个与Mesh对应的PolySurface。这个思路很简单,但是这种在Mesh的面非常多(超过10K)时,Dynamo的运行效率会大打折扣,因此需要从算法进行优化。优化的方法是把所有的Mesh分成几个组,每个组的Mesh总数量控制在一定范围(如10)内;或者创建多个线程(如4个)来分批处理Mesh分组,这样可以极大的提高运行速度。
下面的代码是摘自SpringNodes软件包中Mesh.ToPolySurface节点:
#Copyright(c) 2016, Dimitar Venkov
# @5devene, dimitar.ven@gmail.com
import clr
from System.Threading import Thread, ThreadStart
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
def tolist(obj1):
if hasattr(obj1,'__iter__'): return obj1
else: return [obj1]
def output1(l1):
if len(l1) == 1: return l1[0]
else: return l1
def chop1(l1, n):
return [l1[x:x+n] for x in xrange(0, len(l1), n)]
def mesh2ps(topo, f_chop = chop1,
f_ps1 = PolySurface.ByJoinedSurfaces):
vp = topo.VertexPositions
fi = topo.FaceIndices
xr1 = xrange(len(fi) )
ind = [(fi[i].A, fi[i].B, fi[i].C) for i in xr1]
ptslist = [ map(vp.__getitem__, ind[i]) for i in xr1]
len1 = int(round(len(ptslist) ) ) / 4 + 1
ptslist = f_chop(ptslist, len1)
surf = [None, None, None, None]
def ps_generator(i, f_chop = chop1,
f_ps1 = PolySurface.ByJoinedSurfaces,
f_sf1 = Surface.ByPerimeterPoints):
sflist = [f_sf1(pts) for pts in ptslist[i] ]
while len(sflist) > 10 :
sflist = f_chop(sflist,10)
sflist = [f_ps1(sf1) for sf1 in sflist]
surf[i] = sflist
return
def threaded_ps0(): return ps_generator(0)
def threaded_ps1(): return ps_generator(1)
def threaded_ps2(): return ps_generator(2)
def threaded_ps3(): return ps_generator(3)
threads = (Thread(ThreadStart(threaded_ps0) ),
Thread(ThreadStart(threaded_ps1) ),
Thread(ThreadStart(threaded_ps2) ),
Thread(ThreadStart(threaded_ps3) ) )
for t in threads: t.Start()
for t in threads: t.Join()
return f_ps1(surf[0] + surf[1] + surf[2] + surf[3])
for t in threads: t.Finalize()
meshes = tolist(IN[0])
OUT = output1(map(mesh2ps,meshes) )
在上面的代码里,Dimitar同时使用了分组和多线程这两种算法,运行速度提升很多。这是一种通用的方法。如果我们的地形恰巧是由点坐标文件形成的,而且我们的点位置再平面投影上是严格的矩形矩阵形式,如下图所示:
那么我们可以使用比较简单的Dynamo内置节点来组合出一个比较光滑的NurbsSurface。
具体方法如下:
这样的流程节点如下:
里面使用了一个自定义节点,此节点可在QQ群“Dynamo程序设计教程”中下载。
地形在Dynamo中转换成PolySuface后,还可以进一步生成Solid,具体的操作方法,请关注Dynamo应用秘籍:8。
-------我是分隔线--------
另外需要介绍一下,因为地形的复杂程度不一,尤其是有些时候,还在地形上绘制了SubRegion、道路后,使用Dynamo的节点转换出来的Mesh可能会有局部位置飞起的乱象,如下图所示:
因此比较稳妥的方式,还是需要使用Python Script的节点来实现,如下图: