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

如何为Turtle图形多边形程序添加文本文件输入功能?

解决Turtle多边形程序读取文本文件坐标的问题

问题描述

现有基于Turtle图形库的多边形绘制程序,目前需手动输入坐标,操作繁琐耗时。希望添加读取文本文件获取坐标的功能,但尝试后未成功。

示例需求

绘制顶点为(0,0),(0,100),(100,100),(100,0)的正方形,内部包含顶点为(0,0),(0,20),(20,20),(20,0)的孔洞,偏移量为-50。

错误情况

读取JSON文件时出现报错,常见原因包括文件不存在、格式错误或键名不匹配。


现有代码

import turtle
turtle.setup(640, 380)
import math
import json

import turtle

def get_vertex():
    """Get a vertex by clicking on the screen."""
    turtle.onscreenclick(turtle.goto)

def get_polygon():
    """Get a polygon by clicking on the screen."""
    turtle.speed(0)  # Set turtle speed to fastest
    get_vertex()
    vertices = []
    while True:
        vertex = turtle.pos()
        if vertex in vertices:
            break  # Polygon is complete
        vertices.append(vertex)
        turtle.dot(5)  # Draw a dot at each vertex
    turtle.penup()
    return vertices


def getPolygonVertices(numVertices): #get polygon vertices function
    vertices = []
    for i in range(numVertices):
        try:
            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))
        except ValueError:
            print("Error: Coordinates must be integers.")
            return None
    return vertices


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


def getHoles(numHoles, polygonVertices): #get holes function
    holes = []
    for i in range(numHoles):
        try:
            x = int(input(f"Enter x coordinate of hole {i+1}: "))
            y = int(input(f"Enter y coordinate of hole {i+1}: "))
            if not pointInsidePolygon((x, y), polygonVertices):
                print("Error: Hole must be inside original polygon.")
                return None
            holes.append((x, y))
        except ValueError:
            print("Error: Coordinates must be integers.")
            return None
    return holes


def pointInsidePolygon(point, vertices, epsilon=1e-9):
    x, y = point
    inside = False
    for i in range(len(vertices)):
        j = (i + 1) % len(vertices)
        if abs((vertices[j][1] - vertices[i][1])*(x - vertices[i][0]) - (vertices[j][0] - vertices[i][0])*(y - vertices[i][1])) < epsilon:
            # point is on the edge of the polygon
            return False
        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 offsetPolygon(vertices, offset):
    newVertices = []
    for i in range(len(vertices)):
        j = (i + 1) % len(vertices)
        v1 = vertices[i]
        v2 = vertices[j]
        dx = v2[0] - v1[0]
        dy = v2[1] - v1[1]
        length = math.sqrt(dx*dx + dy*dy)
        if length > 0:
            dx /= length
            dy /= length
            dx *= offset
            dy *= offset
        newVertices.append((v1[0] + dy, v1[1] - dx))
    return newVertices


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

    #draw polygon
    polygon(vertices)

    #compute and draw hatch lines
    min_x = min(v[1] 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[0] 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 = float((min_x + line_spacing_x/2) / line_spacing_x) 
    start_y = float((min_y + line_spacing_y/2) / line_spacing_y)
    #compute end point
    end_x = float((max_x - line_spacing_x/2) / line_spacing_x) * line_spacing_x
    end_y = float((max_y - line_spacing_y/2) / line_spacing_y) * line_spacing_y

    #draw hatch lines
    turtle.pu()
    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):
                if not any(pointInsidePolygon((x, y), holeVertices) for holeVertices in holesVertices):
                    turtle.goto(x, y)
                    turtle.pd()
                    turtle.goto(x - line_spacing_x * math.tan(radAngle), y + line_spacing_y)
                    turtle.pu()

    turtle.done()


#Main
   
# Read the JSON file
with open('polygon.json') as f:
    data = json.load(f)

# Extract the polygon vertices from the data dictionary
polygonVertices = data['polygon']

# Extract the hole verticles from the data dictionary
holesVertices = data['hole']

# draw the polygon
polygon(polygonVertices)

# draw the holes
for hole in holesVertices:
    polygon(holesVertices)

# offset the polygon and draw the resulting polygon
offset = float(input("Enter offset distance: "))
offsetVertices = offsetPolygon(polygonVertices, offset)
polygon(offsetVertices)

# hatch the polygon
hatchPolygon(polygonVertices, 10, 45)

解决方案

1. 准备正确格式的JSON文件

在脚本同一目录下创建polygon.json,内容如下(匹配示例需求):

{
    "polygon": [[0, 0], [0, 100], [100, 100], [100, 0]],
    "hole": [[0, 0], [0, 20], [20, 20], [20, 0]]
}
  • 确保文件路径正确,若不在同一目录需填写绝对路径。

2. 修复代码中的关键错误

你的代码存在多处逻辑和语法问题,修复后即可正常读取文件:

  • 移除重复的import turtle语句
  • 绘制孔洞时的循环错误:for hole in holesVertices: polygon(holesVertices) 改为polygon(hole),避免重复绘制整个孔洞列表
  • 为hatchPolygon函数添加holesVertices参数,消除全局变量依赖
  • 修正边界计算错误:min_x = min(v[1] for v in vertices) 改为min(v[0] for v in vertices),同理修正max_x、min_y、max_y的取值逻辑
  • 添加异常处理,捕获文件不存在、JSON格式错误等常见问题

修复后的完整代码

import turtle
import math
import json

turtle.setup(640, 380)

def get_vertex():
    """通过点击屏幕获取顶点"""
    turtle.onscreenclick(turtle.goto)

def get_polygon():
    """通过点击屏幕获取多边形"""
    turtle.speed(0)  # 设置画笔最快速度
    get_vertex()
    vertices = []
    while True:
        vertex = turtle.pos()
        if vertex in vertices:
            break  # 多边形绘制完成
        vertices.append(vertex)
        turtle.dot(5)  # 在每个顶点绘制圆点
    turtle.penup()
    return vertices

def getPolygonVertices(numVertices):
    """手动输入多边形顶点"""
    vertices = []
    for i in range(numVertices):
        try:
            x = int(input(f"请输入第{i+1}个顶点的x坐标: "))
            y = int(input(f"请输入第{i+1}个顶点的y坐标: "))
            vertices.append((x, y))
        except ValueError:
            print("错误:坐标必须为整数。")
            return None
    return vertices

def polygon(vertices):
    """绘制多边形"""
    turtle.pu()
    turtle.goto(vertices[0])
    turtle.pd()
    for vertex in vertices:
        turtle.goto(vertex)
    turtle.goto(vertices[0])

def getHoles(numHoles, polygonVertices):
    """手动输入孔洞位置"""
    holes = []
    for i in range(numHoles):
        try:
            x = int(input(f"请输入第{i+1}个孔洞的x坐标: "))
            y = int(input(f"请输入第{i+1}个孔洞的y坐标: "))
            if not pointInsidePolygon((x, y), polygonVertices):
                print("错误:孔洞必须在原始多边形内部。")
                return None
            holes.append((x, y))
        except ValueError:
            print("错误:坐标必须为整数。")
            return None
    return holes

def pointInsidePolygon(point, vertices, epsilon=1e-9):
    """判断点是否在多边形内部(不在边缘)"""
    x, y = point
    inside = False
    for i in range(len(vertices)):
        j = (i + 1) % len(vertices)
        if abs((vertices[j][1] - vertices[i][1])*(x - vertices[i][0]) - (vertices[j][0] - vertices[i][0])*(y - vertices[i][1])) < epsilon:
            # 点在多边形边缘,返回False
            return False
        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 offsetPolygon(vertices, offset):
    """生成偏移后的多边形顶点"""
    newVertices = []
    for i in range(len(vertices)):
        j = (i + 1) % len(vertices)
        v1 = vertices[i]
        v2 = vertices[j]
        dx = v2[0] - v1[0]
        dy = v2[1] - v1[1]
        length = math.sqrt(dx*dx + dy*dy)
        if length > 0:
            dx /= length
            dy /= length
            dx *= offset
            dy *= offset
        newVertices.append((v1[0] + dy, v1[1] - dx))
    return newVertices

def hatchPolygon(vertices, holesVertices, lineSpacing=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)

    # 计算线间距的x、y分量
    line_spacing_x = lineSpacing * math.cos(radAngle)
    line_spacing_y = lineSpacing * math.sin(radAngle)

    # 计算起始点
    start_x = min_x + line_spacing_x/2
    start_y = min_y + line_spacing_y/2

    # 绘制填充线
    turtle.pu()
    current_y = start_y
    while current_y <= max_y:
        current_x = start_x
        while current_x <= max_x:
            point = (current_x, current_y)
            if pointInsidePolygon(point, vertices):
                # 检查是否在孔洞内
                in_hole = False
                for hole in holesVertices:
                    if pointInsidePolygon(point, hole):
                        in_hole = True
                        break
                if not in_hole:
                    turtle.goto(point)
                    turtle.pd()
                    # 绘制一条贯穿边界的线段
                    end_x = current_x + line_spacing_x * ((max_x - current_x) / abs(line_spacing_x)) if line_spacing_x != 0 else current_x
                    end_y = current_y + line_spacing_y * ((max_y - current_y) / abs(line_spacing_y)) if line_spacing_y != 0 else current_y
                    turtle.goto(end_x, end_y)
                    turtle.pu()
            current_x += abs(line_spacing_x)
        current_y += abs(line_spacing_y)

    turtle.done()

# 主程序
if __name__ == "__main__":
    try:
        # 读取JSON文件
        with open('polygon.json', 'r', encoding='utf-8') as f:
            data = json.load(f)

        # 提取多边形和孔洞顶点
        polygonVertices = data['polygon']
        holesVertices = data['hole']

        # 绘制原始多边形
        polygon(polygonVertices)

        # 绘制孔洞
        for hole in holesVertices:
            polygon(hole)

        # 输入偏移量并绘制偏移后的多边形
        offset = float(input("请输入偏移距离: "))
        offsetVertices = offsetPolygon(polygonVertices, offset)
        polygon(offsetVertices)

        # 绘制填充线
        hatchPolygon(polygonVertices, holesVertices, 10, 45)
    except FileNotFoundError:
        print("错误:未找到polygon.json文件,请确保文件在脚本同一目录下。")
    except json.JSONDecodeError:
        print("错误:polygon.json文件格式不正确,请检查JSON语法。")
    except KeyError as e:
        print(f"错误:JSON文件中缺少键{e}。")

3. 运行说明

  1. 将polygon.json放在Python脚本同一文件夹
  2. 运行脚本,输入偏移量(如-50)
  3. 程序会自动绘制原始正方形、内部孔洞、偏移后的图形,以及填充线

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:22:06