【问题标题】:How to draw an ellipse in Python turtle graphics other than stamping?Python海龟图形除了冲压外如何画椭圆?
【发布时间】:2015-12-15 09:06:17
【问题描述】:

我正在尝试用 Python 海龟图形绘制一个字母“O”。为了提示绘制“O”,通过按键调用它的函数。这是我目前所拥有的:

def draw_O():
# Draw an O

penup()
forward(letter_height/4)
pendown()
forward(letter_width/2)
circle(letter_height/4, 90)
forward(letter_height/2)
circle(letter_height/4, 90)
forward(letter_width/2)
circle(letter_height/4, 90)
forward(letter_height/2)
circle(letter_height/4, 90)
forward(letter_width/2)
penup()
forward(space_width + letter_height/4)
pendown()

onkey(draw_O, "o")

letter_height 和 letter_width 变量可以由用户使用另一个按键提示的对话框从 10-170 之间的任何值更改。现在,如果letter_height = 170 & letter_width = 10,则出现如下所示的“O”:

但是,如果您将其与“H”(我的程序可以绘制的另一个字母)进行比较,您可以很容易地看到它们根本不成比例:

我想做的是为“O”画一个椭圆,其垂直半径等于letter_height,水平半径等于letter_width,这样“O”就会变短为letter_width增加,并且随着letter_height 的增加而升高。问题是,我真的不知道该怎么做!我听说你可以戳一个,但我真的确实 不想使用戳方法,因为它的动画看起来不那么吸引人。此外,当我尝试将我的 letter_height 和 letter_width 值映射到它时,由于某种原因它会覆盖整个屏幕!

最后,我想知道如何在海龟图形中绘制一个椭圆可以像圆一样操作(改变椭圆的半径长度,改变椭圆的范围等)。我不不想使用turtle.stamp() 方法,那么除了在画布上印一个椭圆之外,还有什么方法可以绘制椭圆?非常感谢任何帮助!

【问题讨论】:

  • 你的意思是动态操作?还是只是参数化?
  • @Peter Wood 嗯,圆是参数化的还是动态操作的?
  • 我认为你必须用很多直线来近似它。
  • 图片显示的是胶囊,而不是椭圆
  • 除了@cdlane 的回答,这看起来很有趣:mathopenref.com/coordcirclealgorithm.html

标签: python python-3.x turtle-graphics ellipse


【解决方案1】:

在测试@moomoomoo309 的椭圆代码并发现问题后(打印位置错误,宽度和高度与参数不匹配,忽略海龟标题因此无法打印倾斜椭圆,标题不跟踪绘图,不离开笔在原始状态等)我决定尝试自己写。

我选择使用turtle.circle() 作为模型,以相对于现有海龟位置和航向绘制椭圆,允许用户更改步骤(即制作其他不规则多边形)、离开笔状态并它开始的位置等。这就是我想出的(我使用self而不是turtle或pen,因为我打算将它作为一种方法安装):

import turtle
import math

def ellipse(self, x_radius, y_radius, steps=60):

    down = self.isdown()  # record pen state for restoration later

    if not down:
        self.pendown()

    heading_radians = math.radians(self.heading())
    theta_radians = -math.pi / 2
    extent_radians = 2 * math.pi
    step_radians = extent_radians / steps
    extent_radians += theta_radians
    x_center, y_start = self.position()
    y_center = y_start + y_radius

    cos_heading, sin_heading = math.cos(heading_radians), math.sin(heading_radians)

    while True:
        x, y = x_center + math.cos(theta_radians) * x_radius, y_center + math.sin(theta_radians) * y_radius
        # readjust x & y to set the angle of the ellipse based on the original heading of the turtle
        x, y = x - x_center, y - y_start
        x, y = x * cos_heading - y * sin_heading, x * sin_heading + y * cos_heading
        x, y = x + x_center, y + y_start

        self.setheading(self.towards(x, y))  # turtle faces direction in which ellipse is drawn
        self.goto(x, y)

        if theta_radians == extent_radians:
            break

        theta_radians = min(theta_radians + step_radians, extent_radians)  # don't overshoot our starting point

    self.setheading(self.towards(x_center, y_start))  # set correct heading for the next thing we draw

    if not down:  # restore pen state on return
        self.penup()

(可选)根据Adding a Method to an Existing Object Instance将此方法添加到我们的海龟:

from functools import partial

yertle = turtle.Turtle()
yertle.ellipse = partial(ellipse, yertle)

展示我们可以用turtle.ellipse()绘制的所有新形状的演示代码:

if __name__ == "__main__":

    from functools import partial

    yertle = turtle.Turtle()
    yertle.ellipse = partial(ellipse, yertle)

    import random

    yertle.speed("fastest")
    yertle.hideturtle()
    yertle.penup()

    screen = turtle.Screen()

    for _ in range(75):

        radius = random.randint(10, 50)

        yertle.setheading(random.randint(0, 360))
        yertle.setx(random.randint(-screen.window_width()/2 + radius * 2, screen.window_width()/2 - radius * 2))
        yertle.sety(random.randint(-screen.window_height()/2 + radius + 2, screen.window_height()/2 - radius * 2))
        yertle.color((random.random(), random.random(), random.random()), (random.random(), random.random(), random.random()))

        flag = random.choice([True, False, False])

        if flag:
            yertle.begin_fill()

        yertle.ellipse(radius, radius / 0.5 + random.random() * 3, steps=random.choice([3, 4, 5, 6, 7, 8, 60, 60, 60]))

        if flag:
            yertle.end_fill()

    screen.exitonclick()

示例输出

我尝试实现 extent 和 turtle.circle(),但无法使其在任意范围内正常工作(即,您可以在相同范围内调用 turtle.ellipse() 两次并具有它继续它停止的曲线)所以我把它留了一天。

将我的答案带回到 OP 的原始问题,我们现在可以这样做:

import turtle
import math

def ellipse(self, x_radius, y_radius, steps=60):

    # ...

def draw_O():
    # Draw an O

    turtle.penup()
    turtle.forward(letter_height/4)
    turtle.pendown()

    ellipse(turtle, letter_width, letter_height)

    turtle.penup()
    turtle.forward(space_width + letter_height/4)
    turtle.pendown()

letter_width = 10
letter_height = 170

space_width = 5

turtle.onkey(draw_O, "o")

turtle.listen()
turtle.done()

生成 OP 所需的基于细椭圆的字母 O:

【讨论】:

    【解决方案2】:

    我很确定这会起作用,但宽度 180/180 和高度/180 可能会关闭。

    from math import sin,cos,pi
    def ellipse(pen, x, y, width, height):
        pen.penup()
        pen.goto(x + width / 2, height)
        pen.pendown()
        penX, penY = pen.pos()
        for i in range(0, 360):
            penX += cos(i*pi/180)*width/180
            penY += sin(i*pi/180)*height/180
            pen.goto(penX, penY)
        pen.penup()
    

    【讨论】:

    • 此代码不在 x,y 处绘制椭圆;它不会画width 宽或height 高。 width/180 和 height/180 表达式中的 180 可能应该更接近 60,并且实际上根本不应该存在,除非您使用 penX += 而不是 penX +(同上 penY +=)。此代码忽略海龟的当前航向,因此您无法绘制对角椭圆。我已经尝试自己实现ellipse() 来解决这些问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 2017-08-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    相关资源
    最近更新 更多