如何为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. 运行说明
- 将
polygon.json放在Python脚本同一文件夹 - 运行脚本,输入偏移量(如-50)
- 程序会自动绘制原始正方形、内部孔洞、偏移后的图形,以及填充线
内容的提问来源于stack exchange,提问作者bald eagle
相关产品推荐
相关产品推荐

