如何在不改变网格其他部分的情况下将两个网格沿着重叠的接缝缝合在一起?

1 人关注

我正试图将两个网格连接成一个单一的网格。两者的边界边缘完全重叠,但它们的间距并不相同--因此,需要删除所有接触接缝的面,然后将这些面的所有点(包括沿接缝的点)重新三角化以缩小差距。

请看这两个网格的一个例子。

这里的蓝色区域是非常细的网眼 这里的接缝被夸大了

从第一张图片可以看出,有一个粗网和一个密网。我需要沿着接缝处将这两个缝合在一起。

问题是,接缝可能非常随机,难以概括。

我还有一个条件,就是我不能做一个受限的Delaunay,然后重新三角化所有的东西,因为我的网格中有些地方z != F(x,y) ,这会被Delaunay弄乱。我只能修改接触接缝的面。

最好是基于 vtk 的解决方案,如果有人能提供帮助?

I've tried vtk.vtkDelaunay2D to re-mesh.

我还尝试了 vtk.vtkFeatureEdges 来提取边缘。然后尝试将边界点分割成片段,找到与其他网格片段重叠的片段,并在片段的集合上使用Delaunay,类似这样。

这并不奏效,因为我认为接缝是完全重叠的?

我得到了 vtk.vtkDecimatePro ,可以正常工作,但我不想实际修改网格。我只想缝合接缝处。

如果谁有什么想法,我就没办法了。

python
mesh
vtk
pyvista
trimesh
Derek Eden
Derek Eden
发布于 2022-07-03
2 个回答
已采纳
0 人赞同

很难说什么会对你的实际使用情况起作用,但我用两个不匹配的气缸作为例子数据拼凑了一些东西。

这个想法和你所阐述的差不多,只是可能做得更仔细一些。

  • find the boundary edges of both meshes
  • use some heuristic to identify edges that should be fused (I assume this is already solved for your use case),
  • pull out cells in both meshes that contain the respective edge lines, delete these cells (along with edge points) from the original meshes
  • triangulate these cells using delaunay_3d()
  • use some heuristic to discard the "capping" introduced by triangulation; looking at your screenshot what I did will probably work: check the cell normals and discard "too horizontal" cells
  • merge the two truncated input meshes and the middle triangulated strip
  • 我在代码中留下了一些评论,例如,假设你的网格是三角化的,或者如果不是,它们可以被三角化。如果我们不能对网格进行三角测量,那么 remove_points() 就不会起作用,所以我们必须通过单元提取来获得相同的结果(不是太难,只是比较麻烦)。

    该代码首先绘制了输入的网格以显示不匹配的情况,最后绘制了合并后的网格。替换代码0】抱怨网格质量问题,这可能与三角剖分中沿缝的8个较大的三角形有关。这是否会影响到你的真实用例,我无法猜测。

    import pyvista as pv
    # plotting setup
    theme = pv.themes.DocumentTheme()
    theme.show_edges = True
    # create dummy meshes
    mesh1 = pv.Cylinder(resolution=8, center=(0, 0, 0.5), direction=(0, 0, 1), capping=False).triangulate().subdivide(3)
    mesh2 = pv.Cylinder(resolution=16, center=(0, 0, -0.5), direction=(0, 0, 1), capping=False).triangulate().subdivide(3)
    mesh2.rotate_z(360/32, inplace=True)
    # plot them together
    plotter = pv.Plotter(theme=theme)
    plotter.add_mesh(mesh1, color='pink')
    plotter.add_mesh(mesh2, color='cyan')
    plotter.show()
    edge_kwargs = dict(
        manifold_edges=False,
        non_manifold_edges=False,
        feature_edges=False,
        boundary_edges=True,
    def tag_and_extract(mesh):
        """The work we have to do for both meshes.
        This adds some scalars and extracts cells involved in the common
        edge of both meshes. You'll need to tweak the filtering that selects
        the appropriate feature edges below, see the "first hack" comment.
        This will also modify the input mesh by deleting edge points and
        the cells containing them. If you want to preserve your input
        mesh, pass in a copy, and use that copy after calling this
        function.
        # grab interesting edges
        # add scalars to keep track of point and cell indices after extraction
        mesh.point_data['point_inds'] = range(mesh.n_points)
        mesh.cell_data['cell_inds'] = range(mesh.n_cells)
        mesh_edges = mesh.extract_feature_edges(**edge_kwargs)
        # first hack:
        # you'll need some way to locate corresponding pairs of edges; this is specific to your problem
        # in this example there's only one pair of edges so we'll clip with two planes
        mesh_edges = mesh_edges.clip('z', origin=mesh1.center).clip('-z', origin=mesh2.center)
        # extract original cells containing edge lines
        mesh_edge_cells = mesh.extract_points(mesh_edges['point_inds'])
        # delete edge points along with their containing cells
        # the example data is already triangulated, otherwise we'd have to triangulate it
        # (or have to do more work)
        mesh.remove_points(mesh_edges['point_inds'], inplace=True)
        return mesh_edge_cells
    mesh1_edge_cells = tag_and_extract(mesh1)
    mesh2_edge_cells = tag_and_extract(mesh2)
    # triangulate the edge strip
    edge_strip = (mesh1_edge_cells + mesh2_edge_cells).delaunay_3d().extract_surface()
    # second hack that needs fitting to your problem: remove capping
    normals = edge_strip.compute_normals().cell_data['Normals']
    horizontals = abs(normals[:, -1]) < 0.9  # has lots of leeway
    edge_strip = edge_strip.extract_cells(horizontals).extract_surface()
    # merge meshes
    merged = mesh1 + edge_strip + mesh2
    # plot the result
    plotter = pv.Plotter(theme=theme)
    plotter.add_mesh(merged)
    plotter.show()
    

    Screenshot of the input meshes:

    Merged mesh:

    希望有更好(和更可靠)的方法,但这是我最好的猜测。

    这也是我的想法,但正如你所说的,我想得更多,并仔细执行。非常感谢,我今晚会再试一次。唯一的问题是我的接缝有多复杂,但会进行测试的。谢谢你
    @DerekEden 我认为这种结构很难避免。原则上,我们可以使沿边的点不那么密集,但我没有想到一个好的方法。我在组装三部分之前运行了 edge_strip.decimate(0.5) ,虽然技术上看起来还不错,但我不会赌它对你的真实网格的稳定性。我想值得一试。
    我很感谢你的帮助,但我最终选择了一条稍有不同的路线。我没有把我的原始网格沿着这些方格切割,然后试图在方格中进行定位,而是根据每个方格中的中心点对单元进行分组,然后我把我想降样的方格中的所有单元分组,并使用vtk.vtkDecimatePro过滤器,但对边界顶点进行约束。这样一来,我的粗略降样部分和我的原始高分辨率部分已经有了相同的边界,这只是一个简单的合并。
    Derek Eden
    Derek Eden
    发布于 2022-07-07
    0 人赞同

    我最后修改了我的方法,但希望它能帮助别人。

    这里最初的目标是把一个高分辨率的网格,用一个预定的网格大小来通过它--如果某个网格方格中的细胞通过了某个测试,那么这个网格方格就会被保留,而不是原来的细胞。这样一来,在检查完所有的方格后,一部分网格的分辨率可以大幅降低。

    这里的整个目标是只在某些自定义定义的区域(也不是静态区域,而是基于规则)降低分辨率。

    Most of the solution is not pertinent to this question so I will just describe:

    -根据单元格的中心点所在的网格方格对其进行分组

    -决定保留哪些网格方块(从而决定对哪些原始单元进行降样)。

    -将所有要降样的单元格和所有要保留的单元格组合在一起(将网格分成两部分)。

    -使用 vtk.vtkDecimatePro 在你想要降样的部分,并确保你关闭边界顶点删除。

    -下采样后,两个网格仍有相同的边界,因此只需将网格串联起来即可。

    相关的代码位是。

    import vtk
    def decimate_polydata(polydata, 
                          reduction=0.5, 
                          error=0.01, 
                          accumulate_error=False,
                          degree=25,
                          feature=15,
                          splitting=True):
        Function to decimate a VTK polydata object
        deci = vtk.vtkDecimatePro()
        deci.SetTargetReduction(reduction)
        deci.PreserveTopologyOn()
        deci.SetFeatureAngle(feature)
        deci.SetSplitting(splitting)
        deci.SetErrorIsAbsolute(1)
        deci.SetAccumulateError(accumulate_error)
        deci.SetMaximumError(error)
        deci.SetDegree(degree)
        deci.BoundaryVertexDeletionOff() #this is the key!!
        deci.SetInputData(polydata)
        deci.Update()
        deci = deci.GetOutput()
        return deci
    def merge_polydata(polydatas):
        Function to append/merge two VTK polydata objects together
        pt and cell indexing is updated automatically
        poly = vtk.vtkAppendPolyData()
        for polydata in polydatas:
            poly.AddInputData(polydata)
        poly.Update()
        poly = poly.GetOutput()