一种对曲面顶部和底部切片进行排序的算法

An algorithm to sort top and bottom slices of curved surfaces

我尝试做:

  1. 剪切STL文件https://www.dropbox.com/s/pex20yqfgmxgt0w/wing_fish.stl?dl=0 at Z-coordinate using PyVsita https://docs.pyvista.org/)
  2. 在给定的部分 Z 处提取点的坐标 X、Y
  3. 将点排序到 Upper 和 Down 组以便进一步操作

这是我的代码:

import pyvista as pv
import matplotlib.pylab as plt
import numpy as np
import math

mesh = pv.read('wing_fish.stl')
z_slice = [0, 0, 1]     # normal to cut at

single_slice = mesh.slice(normal=z_slice, origin=[0, 0, 200]) #  slicing
a = single_slice.points                    #  choose only points

# p = pv.Plotter()          #show section
# p.add_mesh(single_slice)
# p.show()

a = a[a[:,0].astype(float).argsort()]       #  sort all points by Х coord
#  X min of all points
x0 = a[0][0]
#  Y min of all points
y0 = a[0][1] 

#  X tail 1 of 2 
xn = a[-1][0]
#  Y tail 1 of 2 
yn = a[-1][1]

#  X tail 2 of 2
xn2 = a[-2][0]
#  Y tail 2 of 2
yn2 = a[-2][1]

def line_y(x, x0, y0, xn, yn):
    # return y coord at arbitary x coord of x0, y0  xn, yn LINE
    return ((x - x0)*(yn-y0))/(xn-x0)+y0

def line_c(x0, y0, xn, yn):
    # return x, y middle points of LINE
    xc = (x0+xn)/2
    yc = (y0+yn)/2
    return xc, yc

def chord(P1, P2):
    return math.sqrt((P2[1] - P1[1])**2 + (P2[0] - P1[0])**2)

xc_end, yc_end = line_c(xn, yn, xn2, yn2)   # return midle at trailing edge

midLine = np.array([[x0,y0],[xc_end,yc_end]],dtype='float32')

c_temp_x_d = []
c_temp_y_d = []

c_temp_x_u = []
c_temp_y_u = []

isUp = None
isDown = None

for i in a:
    if i[1] == line_y(i[0], x0=x0, y0=y0, xn=xc_end, yn=yc_end):
        continue

    elif i[1] < line_y(i[0], x0=x0, y0=y0, xn=xc_end, yn=yc_end):
       
        c_temp_y_d.append(i[1])
        c_temp_x_d.append(i[0])
        isDown = True
    else:
        c_temp_y_u.append(i[1])
        c_temp_x_u.append(i[0])
        isUp = True
    
    if len(c_temp_y_d) != 0 and len(c_temp_y_u) != 0:
        print(c_temp_y_d[-1])

plt.plot(c_temp_x_d, c_temp_y_d, label='suppose to be down points')
plt.plot(c_temp_x_u, c_temp_y_u, label='suppose to be upper points')
plt.plot(midLine[:,0], midLine[:,1], label='Chord')
plt.scatter(a[:,0],a[:,1], label='raw points')

plt.legend();plt.grid();plt.show()

我有:

我想要的:

非常感谢您的帮助和建议! 提前致谢!

您正在丢弃您的 STL 网格和切片中已经存在的宝贵连接信息!

我在 PyVista 中想不出更惯用的解决方案,但在最坏的情况下,您可以从切片中获取单元格(线)信息,然后从其左侧开始遍历您的形状(在拓扑上相当于一个圆)向右,反之亦然。这是一种方法:

import numpy as np
import matplotlib.pyplot as plt
import pyvista as pv

mesh = pv.read('../wing_fish.stl')
z_slice = [0, 0, 1]     # normal to cut at

single_slice = mesh.slice(normal=z_slice, origin=[0, 0, 200]) #  slicing

# find points with smallest and largest x coordinate
points = single_slice.points
left_ind = points[:, 0].argmin()
right_ind = points[:, 0].argmax()

# sanity check for what we're about to do:
# 1. all cells are lines
assert single_slice.n_cells == single_slice.n_points
assert (single_slice.lines[::3] == 2).all()

# 2. all points appear exactly once as segment start and end
lines = single_slice.lines.reshape(-1, 3)  # each row: [2, i_from, i_to]
assert len(set(lines[:, 1])) == lines.shape[0]

# create an auxiliary dict with from -> to index mappings
conn = dict(lines[:, 1:])

# and a function that walks this connectivity graph
def walk_connectivity(connectivity, start, end):
    this_ind = start
    path_inds = [this_ind]
    while True:
        next_ind = connectivity[this_ind]
        path_inds.append(next_ind)
        this_ind = next_ind
        if this_ind == end:
            # we're done
            return path_inds

# start walking at point left_ind, walk until right_ind
first_side_inds = walk_connectivity(conn, left_ind, right_ind)

# now walk forward for the other half curve
second_side_inds = walk_connectivity(conn, right_ind, left_ind)

# get the point coordinates for plotting
first_side_points = points[first_side_inds, :-1]
second_side_points = points[second_side_inds, :-1]

# plot the two sides
fig, ax = plt.subplots()
ax.plot(*first_side_points.T)
ax.plot(*second_side_points.T)
plt.show()

为了避免使用O(n^2)算法,我定义了一个辅助字典,将线段起始索引映射到结束索引。为了让它起作用,我们需要进行一些完整性检查,即单元格都是简单的线段,并且每个线段都有相同的方向(即每个起点都是唯一的,每个终点都是唯一的)。一旦我们有了这个,就很容易从你的机翼轮廓的左边缘开始,走每条线段,直到我们找到右边缘。

这种方法的本质意味着我们无法先验地知道从左到右的路径是在上路径还是下路径。这需要您进行实验;以您认为合适的方式命名这两条路径。

当然,总有微调的余地。例如,上面的实现创建了两条路径,它们都以网格的左右边界点开始和结束。如果您希望顶部和底部曲线不共享任何点,则必须相应地调整算法。如果在路径上找不到终点,那么当前的实现将为您提供一个无限循环,其中的列表会超出所有可用内存。考虑在实现中添加一些检查以避免这种情况。

无论如何,这就是我们从上面得到的: