2

我尝试这样做:

  1. 使用 PyVsita https://docs.pyvista.org/在 Z 坐标处剪切 STL 文件https://www.dropbox.com/s/pex20yqfgmxgt0w/wing_fish.stl?dl=0
  2. 在给定截面 Z 处提取点的坐标 X、Y
  3. 将点排序到上组和下组以进行进一步操作

这是我的代码:

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()

是)我有的:

在此处输入图像描述

我想要的是:

在此处输入图像描述

我将非常感谢任何帮助和建议!提前致谢!

4

1 回答 1

2

您正在丢弃 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)算法,我定义了一个辅助字典,将线段开始索引映射到结束索引。为了使它工作,我们需要一些完整性检查,即单元格都是简单的线段,并且每个线段具有相同的方向(即每个起点都是唯一的,每个终点都是唯一的)。一旦我们有了这个,就很容易从机翼轮廓的左边缘开始,遍历每个线段,直到找到右边缘。

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

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

无论如何,这就是我们从上面得到的: 带有清晰橙色顶部和蓝色底部轮廓的机翼数据图像

于 2021-09-17T14:19:28.473 回答