[英]Filling a polygon with lines parallel to its longest side
給定形成多邊形的點列表,我如何在該多邊形內創建與其最長邊平行的均勻間隔線?
我能夠旋轉線條並獲得均勻的間距,但我似乎無法將它們放置在多邊形內。 在獲得多邊形內的線后,我的意圖是找到它們截取線的位置。
這是我現在陷入困境的地方:
import matplotlib.pyplot as plt
import numpy as np
import math
def longest_side(points):
"""
Returns the points of the longest side
"""
max_length = 0
for i in range(len(points)-1):
cur_length = np.linalg.norm(np.array(points[i])-np.array(points[i+1]))
if cur_length > max_length:
max_length = cur_length
cur_longest = [points[i], points[i+1]]
return cur_longest
def rotate(origin, point, angle):
"""
Rotate point around origin
"""
ox, oy = origin
px, py = point
qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
return qx, qy
def create_lines(points, spacing):
"""
Fill polygon with lines
"""
# Get the longest side
longest_lines = longest_side(points)
x1,y1 = longest_lines[0]
x2,y2 = longest_lines[1]
# Arrange the points in acending x-value
if x2 < x1:
tmp = (x1, y1)
x1 = x2
y1 = y2
x2 = tmp[0]
y2 = tmp[1]
# Get the angle between the longest line and the horizontal axis
angle = math.atan2(y2 - y1, x2 - x1)
# Create lines parallel to the longest line with given spacing
for y in np.arange(min(y1, y2), max(y1, y2), spacing):
xr, yr = rotate(origin=[min(x), y], point=[max(x), y], angle=angle)
plt.plot([min(x), xr], [y, yr])
if __name__ == "__main__":
points = ([0, 8], [2, 10], [10, 4], [10, 0], [0, 8])
x = [p[0] for p in points]
y = [p[1] for p in points]
create_lines(points=points, spacing=1)
plt.plot(x, y, 'ro-')
plt.axis('scaled')
plt.show()
給定任何點列表,是否有解決此問題的通用方法?
簡短的回答:你需要做一些幾何學。
長答案:
創建一個線段 class 可以輕松計算線的交點和可接受的截距范圍,固定斜率 m 的線仍然與線段相交。
將您的點轉換為線段,找到最長的線,找到填充多邊形所需的截距范圍,然后找到每個截距生成的每條線的交點。
Class定義:
import matplotlib.pyplot as plt
import numpy as np
import math
# Represent a non-vertical line segment from start_pt to end_pt
# as y = mx + b and minv <= x <= maxv.
# For vertical lines x = b, m = None and minv <= y <= maxv
class LineSeg():
def __init__(self, start_pt, end_pt):
self.x, self.y = start_pt
self.x2, self.y2 = end_pt
if self.x != self.x2:
self.m = (self.y2 - self.y) / (self.x2 - self.x)
self.b = self.y - self.m*self.x
self.minv = min(self.x, self.x2)
self.maxv = max(self.x, self.x2)
else:
self.m = None
self.b = self.x
self.minv = min(self.y, self.y2)
self.maxv = max(self.y, self.y2)
def length(self):
return np.linalg.norm([self.x2-self.x, self.y2-self.y])
# Find intersection (x, y) with line y = mx + b
def intersect_w_line(self, m, b):
# Parallel lines
if m == self.m:
return (None, None)
# Line is vertical but line segment is not
elif m == None:
if self.minv <= b <= self.maxv:
return (b, self.m*b + self.b)
else:
return (None, None)
# Line segment is vertical, but line is not
elif self.m == None:
y = m*self.b + b
if self.minv <= y <= self.maxv:
return (self.b, y)
else:
return (None, None)
else:
x = (b - self.b) / (self.m - m)
y = self.m*x + self.b
if self.minv <= x <= self.maxv:
return (x, y)
else:
return (None, None)
# Find intercept range with line y = mx + b
def intercept_range(self, m):
if self.m == m:
return (self.b, self.b)
# Line is vertical, but segment is not
elif m == None:
return sorted([self.x, self.x2])
# Line is not vertical
else:
b = self.y - m*self.x
b2 = self.y2 - m*self.x2
return sorted([b, b2])
繪圖:
points = ([0, 8], [2, 10], [10, 4], [10, 0])
linesegs = [LineSeg(points[i], points[i+1]) if i+1 < len(points) else LineSeg(points[i], points[0]) for i in range(len(points))]
lengths = [lineseg.length() for lineseg in linesegs]
longest_seg = [lineseg for lineseg in linesegs if lineseg.length() == max(lengths)]
m = longest_seg[0].m
b = longest_seg[0].b
intercept_ranges = [lineseg.intercept_range(m) for lineseg in linesegs]
max_intercept = np.max(intercept_ranges)
min_intercept = np.min(intercept_ranges)
num_lines = 10
spacing = (max_intercept - min_intercept) / (num_lines+1)
intercepts = np.arange(min_intercept + spacing, max_intercept, spacing)
line_pts = [[lineseg.intersect_w_line(m, intercept) for lineseg in linesegs if lineseg.intersect_w_line(m, intercept)[0] is not None] for intercept in intercepts]
plt.close('all')
fig, ax = plt.subplots(1, 1)
polygon = mpl.patches.Polygon(points, closed = True, fill = False)
ax.add_artist(polygon)
for start, end in line_pts:
line = mpl.lines.Line2D([start[0], end[0]], [start[1], end[1]])
ax.add_artist(line)
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.