簡體   English   中英

用平行於其最長邊的線填充多邊形

[英]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()

給定任何點列表,是否有解決此問題的通用方法?

簡短的回答:你需要做一些幾何學。

長答案:

  1. 創建一個線段 class 可以輕松計算線的交點和可接受的截距范圍,固定斜率 m 的線仍然與線段相交。

  2. 將您的點轉換為線段,找到最長的線,找到填充多邊形所需的截距范圍,然后找到每個截距生成的每條線的交點。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM