You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Turtle绘制多边形剖面线时线条超出边界问题求助

解决Turtle绘制多边形剖面线超出边界的问题

我用Python Turtle编写了一段代码,实现平行剖面线填充多边形,但运行后剖面线会超出多边形边界。

  • 当前效果:剖面线从多边形内部延伸到外部,超出了多边形的轮廓
  • 预期效果:剖面线完全限制在多边形内部,只显示轮廓内的部分

原始代码

import turtle
import math

def getPolygonVertices(numVertices): #get polygon vertices function
    vertices = []
    for i in range(numVertices):
        x = int(input(f"Enter x coordinate of vertex {i+1}: "))
        y = int(input(f"Enter y coordinate of vertex {i+1}: "))
        vertices.append((x, y))
    return vertices

def polygon(vertices): #draw polygon function
    turtle.up()
    turtle.goto(vertices[0])
    turtle.down()
    for vertex in vertices:
        turtle.goto(vertex)
    turtle.goto(vertices[0])

def pointInsidePolygon(point, vertices): #function to check if a point is inside polygon
    x, y = point
    inside = False
    for i in range(len(vertices)):
        j = (i + 1) % len(vertices)
        if ((vertices[i][1] > y) != (vertices[j][1] > y)) and \
           (x < (vertices[j][0] - vertices[i][0]) * (y - vertices[i][1]) / (vertices[j][1] - vertices[i][1]) + vertices[i][0]):
            inside = not inside
    return inside

def hatchPolygon(vertices, line_spacing=10, angle=0):
    turtle.speed(0)
    turtle.hideturtle()

    #draw polygon
    polygon(vertices)

    #compute and draw hatch lines
    min_x = min(v[0] for v in vertices)
    max_x = max(v[0] for v in vertices)
    min_y = min(v[1] for v in vertices)
    max_y = max(v[1] for v in vertices)

    #convert angle to radians
    radAngle = math.radians(angle)

    #compute line spacing in x and y direction
    line_spacing_x = lineSpacing * math.cos(radAngle)
    line_spacing_y = lineSpacing * math.sin(radAngle)

    #compute start point
    start_x = math.floor((min_x + line_spacing_x/2) / line_spacing_x) * line_spacing_x
    start_y = math.floor((min_y + line_spacing_y/2) / line_spacing_y) * line_spacing_y

    #compute end point
    end_x = math.ceil((max_x - line_spacing_x/2) / line_spacing_x) * line_spacing_x
    end_y = math.ceil((max_y - line_spacing_y/2) / line_spacing_y) * line_spacing_y

    #draw hatch lines
    turtle.penup()
    for y in range(int(start_y), int(end_y), int(line_spacing_y)):
        for x in range(int(start_x), int(end_x), int(line_spacing_x)):
            if pointInsidePolygon((x, y), vertices):
                turtle.goto(x, y)
                turtle.pendown()
                turtle.goto(x - line_spacing_x * math.tan(radAngle), y + line_spacing_y)
                turtle.penup()

    turtle.done()


#Main
n = int(input("Enter number of polygon vertices: ")) #get number of vertices 
polygonVertices = getPolygonVertices(n) #get polygon vertices

#hatch line 
lineSpacing = 10
angle = 45
hatchPolygon(polygonVertices, lineSpacing, angle)

错误原因分析

  1. 剖面线绘制逻辑错误:当前代码判断一个点在多边形内后,直接从该点绘制一段固定长度的线,这段线的终点大概率在多边形外部,导致剖面线超出边界。
  2. 变量名不一致:函数hatchPolygon的参数是line_spacing,但内部使用了全局变量lineSpacing,属于不良编码习惯,容易引发NameError。
  3. 遍历方式不严谨:用整数步长遍历x/y坐标,会因为角度导致剖面线间距不准确,且没有考虑剖面线与多边形边界的交点,无法精准控制线段的起止位置。

修正后的代码

import turtle
import math

def getPolygonVertices(numVertices):
    vertices = []
    for i in range(numVertices):
        x = int(input(f"Enter x coordinate of vertex {i+1}: "))
        y = int(input(f"Enter y coordinate of vertex {i+1}: "))
        vertices.append((x, y))
    return vertices

def polygon(vertices):
    turtle.up()
    turtle.goto(vertices[0])
    turtle.down()
    for vertex in vertices:
        turtle.goto(vertex)
    turtle.goto(vertices[0])

def pointInsidePolygon(point, vertices):
    x, y = point
    inside = False
    for i in range(len(vertices)):
        j = (i + 1) % len(vertices)
        if ((vertices[i][1] > y) != (vertices[j][1] > y)) and \
           (x < (vertices[j][0] - vertices[i][0]) * (y - vertices[i][1]) / (vertices[j][1] - vertices[i][1]) + vertices[i][0]):
            inside = not inside
    return inside

def lineIntersection(line1, line2):
    # 计算两条线段的交点,line1和line2格式为((x1,y1), (x2,y2))
    x1, y1 = line1[0]
    x2, y2 = line1[1]
    x3, y3 = line2[0]
    x4, y4 = line2[1]

    denom = (x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)
    if denom == 0:
        return None  # 线段平行或重合,无交点

    t_numer = (x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4)
    u_numer = (x1 - x3)*(y1 - y2) - (y1 - y3)*(x1 - x2)
    t = t_numer / denom
    u = u_numer / denom

    if 0 <= t <= 1 and 0 <= u <= 1:
        x = x1 + t*(x2 - x1)
        y = y1 + t*(y2 - y1)
        return (x, y)
    return None

def hatchPolygon(vertices, line_spacing=10, angle=0):
    turtle.speed(0)
    turtle.hideturtle()

    polygon(vertices)

    min_x = min(v[0] for v in vertices)
    max_x = max(v[0] for v in vertices)
    min_y = min(v[1] for v in vertices)
    max_y = max(v[1] for v in vertices)

    radAngle = math.radians(angle)
    cos_angle = math.cos(radAngle)
    sin_angle = math.sin(radAngle)

    # 计算包围盒内所有平行剖面线的偏移值
    offsets = []
    for x, y in [(min_x, min_y), (min_x, max_y), (max_x, min_y), (max_x, max_y)]:
        offsets.append(cos_angle * x + sin_angle * y)
    min_offset = min(offsets) - line_spacing
    max_offset = max(offsets) + line_spacing

    current_offset = min_offset
    while current_offset <= max_offset:
        intersections = []
        # 遍历多边形每条边,计算与当前剖面线的交点
        for i in range(len(vertices)):
            j = (i + 1) % len(vertices)
            edge = (vertices[i], vertices[j])
            # 构造足够长的剖面线,确保穿过整个包围盒
            if abs(cos_angle) > abs(sin_angle):
                x1 = min_x - 100
                y1 = (current_offset - cos_angle * x1) / sin_angle if sin_angle != 0 else 0
                x2 = max_x + 100
                y2 = (current_offset - cos_angle * x2) / sin_angle if sin_angle != 0 else 0
            else:
                y1 = min_y - 100
                x1 = (current_offset - sin_angle * y1) / cos_angle if cos_angle != 0 else 0
                y2 = max_y + 100
                x2 = (current_offset - sin_angle * y2) / cos_angle if cos_angle != 0 else 0
            hatch_line = ((x1, y1), (x2, y2))
            intersect = lineIntersection(edge, hatch_line)
            if intersect is not None:
                intersections.append(intersect)
        
        # 对交点按剖面线方向排序,确保连接顺序正确
        if len(intersections) >= 2:
            def param(point):
                return cos_angle * point[0] - sin_angle * point[1]
            intersections.sort(key=param)
            # 两两连接交点,只保留多边形内部的线段
            turtle.penup()
            for k in range(0, len(intersections), 2):
                if k+1 >= len(intersections):
                    break
                p1 = intersections[k]
                p2 = intersections[k+1]
                # 验证线段中点是否在多边形内,避免错误连接外部交点
                mid_x = (p1[0] + p2[0])/2
                mid_y = (p1[1] + p2[1])/2
                if pointInsidePolygon((mid_x, mid_y), vertices):
                    turtle.goto(p1)
                    turtle.pendown()
                    turtle.goto(p2)
                    turtle.penup()
        
        current_offset += line_spacing

    turtle.done()

# Main
n = int(input("Enter number of polygon vertices: "))
polygonVertices = getPolygonVertices(n)

lineSpacing = 10
angle = 45
hatchPolygon(polygonVertices, lineSpacing, angle)

关键修正点

  • 新增线段交点计算:通过lineIntersection函数精准计算剖面线与多边形边的交点,确保只绘制多边形内部的线段部分。
  • 重构剖面线生成逻辑:基于偏移值生成所有平行剖面线,对每条线的交点排序后,连接内部的交点对,完全贴合多边形边界。
  • 修复变量名问题:统一使用函数参数line_spacing,避免全局变量依赖。
  • 添加中点验证:确保连接的交点对之间的线段确实在多边形内部,排除外部交点的错误连接。

内容的提问来源于stack exchange,提问作者krit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:07:02