以特定顺序组合相邻的 3D 多边形
Combine adjacent 3D polygons that are in certain order
给定两个具有相同缠绕顺序(逆时针顺序或顺时针顺序)的三维多边形:
poly1 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120784.319999829, 491781.831000042, 5.96999979019165], [120784.319999829, 491781.831000042, -2.0699999332428], [120787.075999871, 491779.675000143, -2.0699999332428]])
poly2 = np.array([[120787.075999871, 491779.675000143, -2.03999996185303], [120787.075999871, 491779.675000143, 5.90999984741211], [120784.319999829, 491781.831000042, 5.96999979019165], [120787.075999871, 491779.675000143, -2.03999996185303]])
如何在保持顺序的同时将这些相邻的多边形组合/合并为一个多边形(在 Python 中)。
下面是绘制两个相邻多边形的代码:
import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(poly1[:,0],poly1[:,1],poly1[:,2])
ax.plot(poly2[:,0],poly2[:,1],poly2[:,2])
ax.view_init(45, 45)
plt.show()
我刚刚做了一个简单的解决方案,它允许将两个多边形与至少一个公共点组合起来,并可能创建 连续 线。
现在,没有容忍度(共同点必须相同),但现在,我会将其添加为函数的参数,因为我完全喜欢它。我将添加将多边形合并到几条公共线的可能性,但我需要时间。也许,我会把它推到 GitHub 上,然后在此处粘贴 link。
修改后的多边形(poly2
中的第一个和最后一个元素与poly1
中的相同):
poly1 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120784.319999829, 491781.831000042, 5.96999979019165], [120784.319999829, 491781.831000042, -2.0699999332428], [120787.075999871, 491779.675000143, -2.0699999332428]])
poly2 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120787.075999871, 491779.675000143, 5.90999984741211], [120784.319999829, 491781.831000042, 5.96999979019165], [120787.075999871, 491779.675000143, -2.0699999332428]])
有点陈腐的解决方案,由于这种情况,代码并不漂亮,但它有效:
def merge_polygons(p1, p2):
"""
Simple function that allows to combine two polygons (as numpy arrays) with at least 1 common point
and potential created continuous lines.
:return: polygon (merged p1 and p2) as numpy array
"""
poly1_l = list(p1)[1:]
poly2_l = list(p2)[1:]
common_i1 = []
common_i2 = []
# looking for common points
for i, j in ((i, j) for i in range(len(poly1_l)) for j in range(len(poly2_l))):
if np.all(poly1_l[i] == poly2_l[j]):
common_i1.append(i)
common_i2.append(j)
if not common_i1:
raise Exception("Can't merge the polygons - no common point!")
# merging polygons with 1 common point
if len(common_i1) == 1:
poly1_l[common_i1[0]:common_i1[0]] = poly2_l[common_i2[0]:] + poly2_l[:common_i2[0]][::-1]
poly1_l.append(poly1_l[0])
return np.array(poly1_l)
else: # merging polygons with 2+ common points
start = [None, None]
end = [None, None]
# checking, if the common points are creating continuous line
for iterator, common_l in enumerate((common_i1, common_i2)):
for i in common_l:
if not (i - 1) % len(poly1_l) in common_l and not (i + 1) % len(poly1_l) in common_l:
raise Exception("Can't merge the polygons - the common part has to be continuous!")
elif not (i - 1) % len(poly1_l) in common_l: # marking the start and the end of common part
start[iterator] = i
elif not (i + 1) % len(poly1_l) in common_l:
end[iterator] = i
# merging polygons due to location of common part
if isinstance(start[0], int) and isinstance(end[0], int):
poly3_l = []
if start[0] < end[0]: # if the common part in the first polygon is not interrupted by the beginning and the end of list
if start[1] < end[1]: # if the common part in the second polygon is not interrupted by the beginning and the end of list
poly3_l.extend(poly1_l[:start[0]])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]): # if start of chain in first polygon corresponds to start of chain in second polygon
poly3_l.extend(poly2_l[:start[1]+1][::-1])
poly3_l.extend(poly2_l[end[1]:][::-1])
else:
poly3_l.extend(poly2_l[end[1]:])
poly3_l.extend(poly2_l[:start[1]+1])
poly3_l.extend(poly1_l[end[0]+1:])
poly3_l.append(poly3_l[0])
else:
poly3_l.extend(poly1_l[:start[0]])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]):
poly3_l.extend(poly2_l[end[1]:start[1]+1][::-1])
else:
poly3_l.extend(poly2_l[end[1]:start[1]+1])
poly3_l.extend(poly1_l[end[0]+1:])
poly3_l.append(poly3_l[0])
else:
if start[1] < end[1]:
poly3_l.extend(poly2_l[:start[1]+1])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]):
poly3_l.extend(poly1_l[end[0]+1:start[0]][::-1])
else:
poly3_l.extend(poly1_l[end[0]+1:start[0]])
poly3_l.extend(poly2_l[end[1]:])
poly3_l.append(poly3_l[0])
else:
poly3_l.extend(poly1_l[end[0]+1:start[0]])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]):
poly3_l.extend(poly2_l[end[1]:start[1]+1][::-1])
else:
poly3_l.extend(poly2_l[end[1]:start[1]+1])
poly3_l.append(poly3_l[0])
return np.array(poly3_l)
else:
raise Exception("Polygons are the same - there is no need to merge them.")
此功能已针对所有可能性进行了两次测试,但我将为此进行更多测试。
它的样子:
如果你只想将多边形合并成一个形状,你必须只使用列表:
poly3 = np.array(list(poly1) + list(poly2))
你的情况如何:
希望对您有所帮助!
poly1
和 poly2
中点的坐标不完全匹配(尽管它们在图上看起来像是同一点)。
我想你希望算法使用“足够接近”的值(即如果点之间的距离小于给定的公差,例如 0.1
,它们被认为是同一点)。
您可以通过找到公共边并将其移除来连接两个多边形。
为了找到公共边,我们首先确定多边形的哪些点对于两个多边形是公共的。
我看过这个 post 并选择了 cKDTree 方法来做到这一点。
它的工作原理是
- 为一个多边形的每个点寻找其在另一个多边形中最近的相邻点
- 比较这两点之间的距离。如果距离小于我们设定的公差,我们认为它们是相同的点并且是两个多边形的共同点。
一旦确定了哪些点是公共的,就可以验证它们是否相邻。如果是,则您已找到要移除的边缘。
生成的多边形将由
组成
- 来自
poly1
的所有点数
poly2
中不构成公共边的那些点
代码如下:
import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial import cKDTree
poly1 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120784.319999829, 491781.831000042, 5.96999979019165], [120784.319999829, 491781.831000042, -2.0699999332428], [120787.075999871, 491779.675000143, -2.0699999332428]])
poly2 = np.array([[120787.075999871, 491779.675000143, -2.03999996185303], [120787.075999871, 491779.675000143, 5.90999984741211], [120784.319999829, 491781.831000042, 5.96999979019165], [120787.075999871, 491779.675000143, -2.03999996185303]])
def is_close(a, b, tolerance):
# Get closest distances for each pt in a
dist = cKDTree(b).query(a, k=1)[0] # k=1 selects closest one neighbor
# Check the distances against the given tolerance value
return dist <= tolerance
def find_consecutive_true_values(arr):
i = 0
while i < len(arr) - 1:
if arr[i] and arr[i+1]:
return i
i+=1
raise Exception('No common edge found')
# Find points in poly1, which are closer than given tolerance to any point in poly2
# and vice versa
tolerance = 0.1
points_in_poly1_close_to_poly2 = is_close(poly1, poly2, tolerance)
points_in_poly2_close_to_poly1 = is_close(poly2, poly1, tolerance)
# Scan each array for two adjacent true values (points at those two indices
# form an edge which is common to both polygons and which we want to remove).
# Idx1 (resp. idx2) will contain the index of the first point of that common edge in poly1 (resp. poly2)
idx1 = find_consecutive_true_values(points_in_poly1_close_to_poly2)
idx2 = find_consecutive_true_values(points_in_poly2_close_to_poly1)
#Split poly1 into two parts:
# first part contains points from the start up to the first point of the common edge (inclusive)
# second part contains points from the second point of the common edge to the end
poly1_part1 = poly1[:idx1+1]
poly1_part2 = poly1[idx1+1:]
#Remove common edge from poly2, depending on where it is located, we end up with one or two parts
if idx2 == len(poly2) - 2:
poly2_part1 = poly2[1:len(poly2) - 2]
poly2_part2 = None
elif idx2 == 0:
poly2_part1 = poly2[2:len(poly2) - 1]
poly2_part2 = None
else:
poly2_part1 = poly2[idx2+2:]
poly2_part2 = poly2[1:idx2]
#Create the resulting polygon by concatenating the individual parts (poly2_part2 may be empty)
if(poly2_part2 is None):
poly = np.concatenate((poly1_part1, poly2_part1, poly1_part2))
else:
poly = np.concatenate((poly1_part1, poly2_part1, poly2_part2, poly1_part2))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(poly[:,0], poly[:,1], poly[:,2])
ax.view_init(45, 45)
plt.show()
(代码远非惯用语,如果您知道您的 Python,请随时编辑它:))
给定两个具有相同缠绕顺序(逆时针顺序或顺时针顺序)的三维多边形:
poly1 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120784.319999829, 491781.831000042, 5.96999979019165], [120784.319999829, 491781.831000042, -2.0699999332428], [120787.075999871, 491779.675000143, -2.0699999332428]])
poly2 = np.array([[120787.075999871, 491779.675000143, -2.03999996185303], [120787.075999871, 491779.675000143, 5.90999984741211], [120784.319999829, 491781.831000042, 5.96999979019165], [120787.075999871, 491779.675000143, -2.03999996185303]])
如何在保持顺序的同时将这些相邻的多边形组合/合并为一个多边形(在 Python 中)。
下面是绘制两个相邻多边形的代码:
import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(poly1[:,0],poly1[:,1],poly1[:,2])
ax.plot(poly2[:,0],poly2[:,1],poly2[:,2])
ax.view_init(45, 45)
plt.show()
我刚刚做了一个简单的解决方案,它允许将两个多边形与至少一个公共点组合起来,并可能创建 连续 线。
现在,没有容忍度(共同点必须相同),但现在,我会将其添加为函数的参数,因为我完全喜欢它。我将添加将多边形合并到几条公共线的可能性,但我需要时间。也许,我会把它推到 GitHub 上,然后在此处粘贴 link。
修改后的多边形(poly2
中的第一个和最后一个元素与poly1
中的相同):
poly1 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120784.319999829, 491781.831000042, 5.96999979019165], [120784.319999829, 491781.831000042, -2.0699999332428], [120787.075999871, 491779.675000143, -2.0699999332428]])
poly2 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120787.075999871, 491779.675000143, 5.90999984741211], [120784.319999829, 491781.831000042, 5.96999979019165], [120787.075999871, 491779.675000143, -2.0699999332428]])
有点陈腐的解决方案,由于这种情况,代码并不漂亮,但它有效:
def merge_polygons(p1, p2):
"""
Simple function that allows to combine two polygons (as numpy arrays) with at least 1 common point
and potential created continuous lines.
:return: polygon (merged p1 and p2) as numpy array
"""
poly1_l = list(p1)[1:]
poly2_l = list(p2)[1:]
common_i1 = []
common_i2 = []
# looking for common points
for i, j in ((i, j) for i in range(len(poly1_l)) for j in range(len(poly2_l))):
if np.all(poly1_l[i] == poly2_l[j]):
common_i1.append(i)
common_i2.append(j)
if not common_i1:
raise Exception("Can't merge the polygons - no common point!")
# merging polygons with 1 common point
if len(common_i1) == 1:
poly1_l[common_i1[0]:common_i1[0]] = poly2_l[common_i2[0]:] + poly2_l[:common_i2[0]][::-1]
poly1_l.append(poly1_l[0])
return np.array(poly1_l)
else: # merging polygons with 2+ common points
start = [None, None]
end = [None, None]
# checking, if the common points are creating continuous line
for iterator, common_l in enumerate((common_i1, common_i2)):
for i in common_l:
if not (i - 1) % len(poly1_l) in common_l and not (i + 1) % len(poly1_l) in common_l:
raise Exception("Can't merge the polygons - the common part has to be continuous!")
elif not (i - 1) % len(poly1_l) in common_l: # marking the start and the end of common part
start[iterator] = i
elif not (i + 1) % len(poly1_l) in common_l:
end[iterator] = i
# merging polygons due to location of common part
if isinstance(start[0], int) and isinstance(end[0], int):
poly3_l = []
if start[0] < end[0]: # if the common part in the first polygon is not interrupted by the beginning and the end of list
if start[1] < end[1]: # if the common part in the second polygon is not interrupted by the beginning and the end of list
poly3_l.extend(poly1_l[:start[0]])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]): # if start of chain in first polygon corresponds to start of chain in second polygon
poly3_l.extend(poly2_l[:start[1]+1][::-1])
poly3_l.extend(poly2_l[end[1]:][::-1])
else:
poly3_l.extend(poly2_l[end[1]:])
poly3_l.extend(poly2_l[:start[1]+1])
poly3_l.extend(poly1_l[end[0]+1:])
poly3_l.append(poly3_l[0])
else:
poly3_l.extend(poly1_l[:start[0]])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]):
poly3_l.extend(poly2_l[end[1]:start[1]+1][::-1])
else:
poly3_l.extend(poly2_l[end[1]:start[1]+1])
poly3_l.extend(poly1_l[end[0]+1:])
poly3_l.append(poly3_l[0])
else:
if start[1] < end[1]:
poly3_l.extend(poly2_l[:start[1]+1])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]):
poly3_l.extend(poly1_l[end[0]+1:start[0]][::-1])
else:
poly3_l.extend(poly1_l[end[0]+1:start[0]])
poly3_l.extend(poly2_l[end[1]:])
poly3_l.append(poly3_l[0])
else:
poly3_l.extend(poly1_l[end[0]+1:start[0]])
if np.all(poly1_l[start[0]] == poly2_l[start[1]]):
poly3_l.extend(poly2_l[end[1]:start[1]+1][::-1])
else:
poly3_l.extend(poly2_l[end[1]:start[1]+1])
poly3_l.append(poly3_l[0])
return np.array(poly3_l)
else:
raise Exception("Polygons are the same - there is no need to merge them.")
此功能已针对所有可能性进行了两次测试,但我将为此进行更多测试。
它的样子:
如果你只想将多边形合并成一个形状,你必须只使用列表:
poly3 = np.array(list(poly1) + list(poly2))
你的情况如何:
希望对您有所帮助!
poly1
和 poly2
中点的坐标不完全匹配(尽管它们在图上看起来像是同一点)。
我想你希望算法使用“足够接近”的值(即如果点之间的距离小于给定的公差,例如 0.1
,它们被认为是同一点)。
您可以通过找到公共边并将其移除来连接两个多边形。
为了找到公共边,我们首先确定多边形的哪些点对于两个多边形是公共的。
我看过这个 post 并选择了 cKDTree 方法来做到这一点。
它的工作原理是
- 为一个多边形的每个点寻找其在另一个多边形中最近的相邻点
- 比较这两点之间的距离。如果距离小于我们设定的公差,我们认为它们是相同的点并且是两个多边形的共同点。
一旦确定了哪些点是公共的,就可以验证它们是否相邻。如果是,则您已找到要移除的边缘。
生成的多边形将由
组成- 来自
poly1
的所有点数
poly2
中不构成公共边的那些点
代码如下:
import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial import cKDTree
poly1 = np.array([[120787.075999871, 491779.675000143, -2.0699999332428], [120784.319999829, 491781.831000042, 5.96999979019165], [120784.319999829, 491781.831000042, -2.0699999332428], [120787.075999871, 491779.675000143, -2.0699999332428]])
poly2 = np.array([[120787.075999871, 491779.675000143, -2.03999996185303], [120787.075999871, 491779.675000143, 5.90999984741211], [120784.319999829, 491781.831000042, 5.96999979019165], [120787.075999871, 491779.675000143, -2.03999996185303]])
def is_close(a, b, tolerance):
# Get closest distances for each pt in a
dist = cKDTree(b).query(a, k=1)[0] # k=1 selects closest one neighbor
# Check the distances against the given tolerance value
return dist <= tolerance
def find_consecutive_true_values(arr):
i = 0
while i < len(arr) - 1:
if arr[i] and arr[i+1]:
return i
i+=1
raise Exception('No common edge found')
# Find points in poly1, which are closer than given tolerance to any point in poly2
# and vice versa
tolerance = 0.1
points_in_poly1_close_to_poly2 = is_close(poly1, poly2, tolerance)
points_in_poly2_close_to_poly1 = is_close(poly2, poly1, tolerance)
# Scan each array for two adjacent true values (points at those two indices
# form an edge which is common to both polygons and which we want to remove).
# Idx1 (resp. idx2) will contain the index of the first point of that common edge in poly1 (resp. poly2)
idx1 = find_consecutive_true_values(points_in_poly1_close_to_poly2)
idx2 = find_consecutive_true_values(points_in_poly2_close_to_poly1)
#Split poly1 into two parts:
# first part contains points from the start up to the first point of the common edge (inclusive)
# second part contains points from the second point of the common edge to the end
poly1_part1 = poly1[:idx1+1]
poly1_part2 = poly1[idx1+1:]
#Remove common edge from poly2, depending on where it is located, we end up with one or two parts
if idx2 == len(poly2) - 2:
poly2_part1 = poly2[1:len(poly2) - 2]
poly2_part2 = None
elif idx2 == 0:
poly2_part1 = poly2[2:len(poly2) - 1]
poly2_part2 = None
else:
poly2_part1 = poly2[idx2+2:]
poly2_part2 = poly2[1:idx2]
#Create the resulting polygon by concatenating the individual parts (poly2_part2 may be empty)
if(poly2_part2 is None):
poly = np.concatenate((poly1_part1, poly2_part1, poly1_part2))
else:
poly = np.concatenate((poly1_part1, poly2_part1, poly2_part2, poly1_part2))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(poly[:,0], poly[:,1], poly[:,2])
ax.view_init(45, 45)
plt.show()
(代码远非惯用语,如果您知道您的 Python,请随时编辑它:))