将 Networkx 中生成的 3D 图形保存为 VTK 格式以便在 Paraview 中查看

Saving a 3D graph generated in Networkx to VTK format for viewing in Paraview

我使用以下代码生成了一个 3D 图形网络,并使用 Mayavi 进行可视化。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import networkx as nx
from mayavi import mlab


pos = [[0.1, 2, 0.3], [40, 0.5, -10],
       [0.1, -40, 0.3], [-49, 0.1, 2],
       [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]
pos = pd.DataFrame(pos, columns=['x', 'y', 'z'])

ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]

G = nx.Graph()
G.add_edges_from(ed_ls)

nx.draw(G)
plt.show()


# plot 3D in mayavi
edge_size = 0.2
edge_color = (0.8, 0.8, 0.8)
bgcolor = (0, 0, 0)


mlab.figure(1, bgcolor=bgcolor)
mlab.clf()

for i, e in enumerate(G.edges()):
    # ----------------------------------------------------------------------------
    # the x,y, and z co-ordinates are here
    pts = mlab.points3d(pos['x'], pos['y'], pos['z'],
                        scale_mode='none',
                        scale_factor=1)
    # ----------------------------------------------------------------------------
    pts.mlab_source.dataset.lines = np.array(G.edges())
    tube = mlab.pipeline.tube(pts, tube_radius=edge_size)

    mlab.pipeline.surface(tube, color=edge_color)

mlab.show()

我想就如何将此 3D 图形保存为 VTK 格式/如何保存提出建议 将 Networkx 图形对象转换为 VTK 文件以在 Paraview 中可视化。

编辑: 我已尝试为输入 Networkx 图调整可用代码 here 以上分享。但是,我无法获得输出。我只是得到一个空的 window 并且 vtkpolyData 没有绘制在 window.

"""
This code converts netwrokx graph to vtk polyData
ref: https://networkx.github.io/documentation/networkx-0.37/networkx.drawing.nx_vtk-pysrc.html
"""

import vtk
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt

from vtk.util.colors import banana, plum


def draw_nxvtk(G, node_pos):
    """
    Draw networkx graph in 3d with nodes at node_pos.

    See layout.py for functions that compute node positions.

    node_pos is a dictionary keyed by vertex with a three-tuple
    of x-y positions as the value.

    The node color is plum.
    The edge color is banana.

    All the nodes are the same size.

    """
    # set node positions
    np={}
    for n in G.nodes():
       try:
           np[n]=node_pos[n]
       except nx.NetworkXError:
           print("node %s doesn't have position"%n)

    nodePoints = vtk.vtkPoints()

    i=0
    for (x,y,z) in np.values():
       nodePoints.InsertPoint(i, x, y, z)
       i=i+1

    # Create a polydata to be glyphed.
    inputData = vtk.vtkPolyData()
    inputData.SetPoints(nodePoints)

    # Use sphere as glyph source.
    balls = vtk.vtkSphereSource()
    balls.SetRadius(.05)
    balls.SetPhiResolution(20)
    balls.SetThetaResolution(20)

    glyphPoints = vtk.vtkGlyph3D()
    glyphPoints.SetInputData(inputData)
    glyphPoints.SetSourceData(balls.GetOutput())

    glyphMapper = vtk.vtkPolyDataMapper()
    glyphMapper.SetInputData(glyphPoints.GetOutput())

    glyph = vtk.vtkActor()
    glyph.SetMapper(glyphMapper)
    glyph.GetProperty().SetDiffuseColor(plum)
    glyph.GetProperty().SetSpecular(.3)
    glyph.GetProperty().SetSpecularPower(30)

    # Generate the polyline for the spline.
    points = vtk.vtkPoints()
    edgeData = vtk.vtkPolyData()

    # Edges

    lines = vtk.vtkCellArray()
    i = 0
    for e in G.edges():
        # The edge e can be a 2-tuple (Graph) or a 3-tuple (Xgraph)
        u = e[0]
        v = e[1]
        if v in node_pos and u in node_pos:
            lines.InsertNextCell(2)
            for n in (u, v):
                (x, y, z) = node_pos[n]
                points.InsertPoint(i, x, y, z)
                lines.InsertCellPoint(i)
                i = i+1

    edgeData.SetPoints(points)
    edgeData.SetLines(lines)

    # Add thickness to the resulting line.
    Tubes = vtk.vtkTubeFilter()
    Tubes.SetNumberOfSides(16)
    Tubes.SetInputData(edgeData)
    Tubes.SetRadius(.01)
    #
    profileMapper = vtk.vtkPolyDataMapper()
    profileMapper.SetInputData(Tubes.GetOutput())

    #
    profile = vtk.vtkActor()
    profile.SetMapper(profileMapper)
    profile.GetProperty().SetDiffuseColor(banana)
    profile.GetProperty().SetSpecular(.3)
    profile.GetProperty().SetSpecularPower(30)

    # Now create the RenderWindow, Renderer and Interactor
    ren = vtk.vtkRenderer()
    renWin = vtk.vtkRenderWindow()
    renWin.AddRenderer(ren)

    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)

    # Add the actors
    ren.AddActor(glyph)
    ren.AddActor(profile)

    renWin.SetSize(640, 640)

    iren.Initialize()
    renWin.Render()
    iren.Start()


if __name__ == "__main__":

    pos = [[0.1, 2, 0.3], [40, 0.5, -10],
           [0.1, -40, 0.3], [-49, 0.1, 2],
           [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]
    pos = pd.DataFrame(pos, columns=['x', 'y', 'z'])
    pos_d = pos.T.to_dict(orient='list')
    
    ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]

    G = nx.Graph()
    G.add_edges_from(ed_ls)
    # nx.draw(G, with_labels=True, pos=nx.spring_layout(G))
    # plt.show()
    draw_nxvtk(G=G, node_pos=pos_d)

关于如何在 运行 上述代码时使用显示的 polyData 查看输出以及如何保存 vtkPolyData 以便在 Paraview 中导入的建议将非常有帮助。

如果您可以使用构建在 vtk 之上的 vedo,这就变得简单了:

import networkx as nx

pos = [[0.1, 2, 0.3],    [40, 0.5, -10],
       [0.1, -40, 0.3],  [-49, 0.1, 2],
       [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]

ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]

G = nx.Graph()
G.add_edges_from(ed_ls)
nxpos = nx.spring_layout(G)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
# nx.draw(G, with_labels=True, pos=nxpos)
# plt.show()

raw_lines = [(pos[x],pos[y]) for x, y in ed_ls]
nx_lines = []
for x, y in ed_ls:
    p1 = nxpos[x].tolist() + [0] # add z-coord
    p2 = nxpos[y].tolist() + [0]
    nx_lines.append([p1,p2])

from vedo import *
raw_pts = Points(pos, r=12)
raw_edg = Lines(raw_lines).lw(2)
show(raw_pts, raw_edg, raw_pts.labels('id'),
     at=0, N=2, axes=True, sharecam=False)

nx_pts = Points(nxpts, r=12)
nx_edg = Lines(nx_lines).lw(2)
show(nx_pts, nx_edg, nx_pts.labels('id'),
     at=1, interactive=True)

write(nx_edg, 'afile.vtk') # save the lines

该包还支持有向图,所以第二个选项是:

from vedo import *
from vedo.pyplot import DirectedGraph

# Layouts: [2d, fast2d, clustering2d, circular, circular3d, cone, force, tree]
g = DirectedGraph(layout='fast2d')
g.arrowScale =0.1
for i in range(6): g.addChild(i)
g.build()
show(g, axes=1)

write(g.unpack(0), 'afile.vtk')

编辑:跟进请求,

如何根据 cellColors():

的标量包含线条的颜色映射
# ... from the first example

from vedo import *
raw_pts = Points(pos, r=12)
raw_edg = Lines(raw_lines).lw(3)

nx_pts = Points(nxpts, r=12).c('red').alpha(0.5)
nx_edg = Lines(nx_lines).lw(2)

v1 = [sin(x)  for x in range(6)]
v2 = [sqrt(x) for x in range(6)]
vc = [x for x in range(nx_edg.NCells())]
labs1 = nx_pts.labels(v1, scale=.05).c('green').addPos(0.02,.04,0)
labs2 = nx_pts.labels(v2, scale=.05).c('red').addPos(0.02,-.04,0)
labsc = nx_edg.labels(vc, cells=True, scale=.04, precision=1, rotZ=-45)
labsc.c('black')

nx_edg.cellColors(vc, cmap='viridis').addScalarBar3D(c='k').addPos(.2,0,0)
# nx_edg.cellColors(vc, cmap='jet').addScalarBar() # this is a 2D scalarbar

show(nx_pts, nx_edg, labs1, labs2, labsc, axes=1)

如何用鼠标悬停点弹出flag消息flag():

from vedo import *
raw_pts = Points(pos, r=12)
raw_edg = Lines(raw_lines).lw(3)

nx_pts = []
for p in nxpts:
    ap = Point(p, r=20).c('red').alpha(0.5)
    ap.flag('some text:\n'+'x='+precision(p[0],2)+'\ny='+precision(p[1],2))
    nx_pts.append(ap)

nx_edg = Lines(nx_lines).lw(3)
show(nx_pts, nx_edg, axes=1)

如何将线条颜色插入节点值:

(注意:此处 clean() 删除了重复点,因此请仔细检查是否与初始数组可能不匹配)

from vedo import *

nx_pts = Points(nxpts, r=12).c('grey').alpha(0.5)
nx_edg = Lines(nx_lines).lw(5)

v1 = [sin(x) for x in range(6)]
labs1 = nx_pts.labels(v1, scale=.05).c('green').addPos(0.02,.04,0)

nx_edg.clean().pointColors(v1, cmap='viridis').addScalarBar()

show(nx_pts, nx_edg, labs1, axes=1)