想试一下tetgen和CGAL的网格生成。
折腾了半天,一个坑接着一个坑
- tetgen官网上出了最新的版本1.6.0,可惜什么都下不下来,新版本旧版本都下不下来。
- 发现conda-forge和pip都有tetgen模块,而且都根据最新版本tetgen进行了更新。于是,又折腾了半天,不知道为什么,总是在安装中和import中出错。
- 只好退而求其次,去github上找到了tetgen的一份旧版本的拷贝1.5.1,并且编译运行成功。实际上tetgen的源代码就3个文件。
- tetview是tetgen配套的可视化工具。然而不支持windows10,试着下载运行了一下,没有成功。tetgen生成的数据是一个特有的格式,不是obj,也不是fbx一类的。
- 于是,为了能看结果,还需要自己写一套转换的程序,将tetgen的数据转化为其他软件支持的格式。这里用的可视化软件是houdini。
用python将自定义格式的mesh文件转换为houdini可以读取的文件格式,这里推荐json格式,注意:如果转成obj格式,obj不支持四面体,四面体的primitive就会被转成三角形,拓扑相当于被改变了,不希望这样。
先导出一个json格式的网格,观察里面的结构。
不需要太复杂的信息,总结起来,有这么几点是必要的: 每个点的位置+ 每个tet由哪几个点组成(点的顺序很重要)
将houdini导出的json信息精简过后:

接下来,需要自己按照格式写出这么一个json文件。作为一个python白痴,还是要先查查python怎么写json~~
python有非常好用的json库,网上资料特别多
写个一个tet的json:
import os
import json
fileversion='17.5.425'
pointcount=4
vertexcount=4
primitivecount=1
# topology: pointref: indices
indices=[0,1,2,3]
pointref=['indices',indices]
topology=['pointref',pointref]
# attributes: pointattributes,
option={"type":{"type":"string","value":"point"}}
posTuples=[[0,0,0],[0,1,0],[1,0,0],[0,0,1]]
op1=["scope","public","type","numeric","name","P",
"options",option]
op2=["size",3,"storage","fpreal32","defaults",["size",1,"storage","fpreal64","values",[0]],
"values",["size",3,"storage","fpreal32","tuples",posTuples]]
pointattributes=[[op1,op2]]
attributes=['pointattributes',pointattributes]
# primitives
primitives=[[["type","Tetrahedron_run"],["startvertex",0,"nprimitives",1]]]
dict=['fileversion',fileversion, 'pointcount',pointcount,'vertexcount',vertexcount,
'primitivecount',primitivecount,'topology',topology,'attributes',attributes,
'primitives',primitives]
jsdump=json.dumps(dict)
with open("C:\\Users\\sun\\Desktop\\outjson0.json",'w') as f:
f.write(jsdump)
f.close()
一个tet的会写了,数据文件中的tet也不难了,接下来就是读取文件,
得到这些信息,并写成json
观察CGAL mesh文件的格式,分成这么几个部分:
Vertices //顶点,相当于houdini中的point,接下来的n行是点的位置
Triangles // 表面三角,在这里我们不太需要这个信息,只想要tet的信息
Tetrahedra //四面体,接下来的n行是每个tet中顶点的id
End结尾
python读取文本资料也很多,要点:
- 首行声明字符编码:utf8\gbk\等
- 打开关闭,以及模式open、close,'r','w','a'
- 读:readline 每次读取一行,返回字符串
read 读取整个文件,返回字符串
readlines 读取,每一行内容作为字符串数组返回 - 处理:
Split() 函数将字符串分割为多个字符串的array
strip()函数去掉头尾指定字符
比较字符串:is、==
- 其他知识点:
* Enum
* Python里没有switch
* 处理一行用空格分隔的数字,直接用split转化为数组就行了
这里是一个简单的脚本,后续还需要重新包装一下,方便调用
import os
import json
from enum import Enum
path_cgal="C:\\Users\\sun\\Desktop\\out.mesh"
path_json="C:\\Users\\sun\\Desktop\\outjson1.json"
class DealState(Enum):
IDLE = 0
NUMVERT=1
POSVERT=2
NUMTET=3
IDTET=4
fileversion='17.5.425'
pointcount=0
vertexcount=0
primitivecount=0
indices=[]
posTuples=[]
# real cgal file
with open(path_cgal,'r',encoding="utf-8") as inputFile:
# read each line
inputState=DealState.IDLE
for line in inputFile:
thisline=line.strip('\n')
# state transfer
if(thisline=='Vertices'):
inputState=DealState.NUMVERT
print('Begin to read Vertices.....\n')
continue
if(thisline=='Triangles'):
inputState=DealState.IDLE
print('Skip reading of Triangles....\n')
continue
if(thisline=='Tetrahedra'):
inputState=DealState.NUMTET
print('Begin to read Tetrahedron....\n')
continue
if(thisline=='End'):
inputState=DealState.IDLE
print('CGAL READING FINISHED !!\n')
break
# check state to deal with this line
if(inputState==DealState.IDLE):
continue
if(inputState==DealState.NUMVERT):
# pointcount and transfer state
pointcount=int(thisline)
inputState=DealState.POSVERT
print('numpoints=',int(pointcount))
elif(inputState==DealState.POSVERT):
# input point pos
pos=thisline.split()
pos=pos[0:-1]
pos=list(map(float,pos))
# append posTuples
posTuples.append(pos)
if(inputState==DealState.NUMTET):
# primitivecount and vertexcount and state transfer
primitivecount=int(thisline)
vertexcount=4*int(primitivecount)
inputState=DealState.IDTET
print('numtets=',int(primitivecount))
print('numverts=',vertexcount)
elif(inputState==DealState.IDTET):
# indices append
ids=thisline.split()
ids=ids[0:-1]
ids= list(map(int,ids))
result = [x - 1 for x in ids]
indices=indices+result
inputFile.close()
# topology: pointref: indices
pointref=['indices',indices]
topology=['pointref',pointref]
# attributes: pointattributes,
option={"type":{"type":"string","value":"point"}}
op1=["scope","public","type","numeric","name","P",
"options",option]
op2=["size",3,"storage","fpreal32","defaults",["size",1,"storage","fpreal64","values",[0]],
"values",["size",3,"storage","fpreal32","tuples",posTuples]]
pointattributes=[[op1,op2]]
attributes=['pointattributes',pointattributes]
# primitives
primitives=[[["type","Tetrahedron_run"],["startvertex",0,"nprimitives",primitivecount]]]
dict=['fileversion',fileversion, 'pointcount',pointcount,'vertexcount',vertexcount,
'primitivecount',primitivecount,'topology',topology,'attributes',attributes,
'primitives',primitives]
jsdump=json.dumps(dict)
with open(path_json,'w') as jsonFile:
jsonFile.write(jsdump)
jsonFile.close()
注意!!!! CGAL的顶点ID是从1开始的,而houdini是从0开始的。。。。
参考资料:
TetGen用户手册中文版 - 百度文库 (baidu.com)
Tetgen软件学习和使用_Shrekul的博客-CSDN博客
tetgen/tetgen/cython/tetgen at master · pyvista/tetgen (github.com)
https://www.sidefx.com/tutorials/how-to-import-files-in-houdini-with-python-part-1-2/