简体   繁体   English

除了冲压外,如何在Python龟图形中绘制椭圆?

[英]How to draw an ellipse in Python turtle graphics other than stamping?

I am trying to draw a letter "O" with Python turtle graphics. 我正在尝试用Python乌龟图形绘制字母“ O”。 To cue the drawing of the "O", the function for it is invoked with a key press. 要提示“ O”的图形,可通过按键调用其功能。 Here is what I have so far: 这是我到目前为止的内容:

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")

The letter_height & letter_width variables can be changed by the user from any value between 10-170 using a dialog box cued by another key press. 用户可以使用另一个按键提示的对话框,在10-170之间的任意值之间更改letter_heightletter_width变量。 Right now, the "O" comes out as shown below if letter_height = 170 & letter_width = 10 : 现在,如果letter_height = 170letter_width = 10 ,则显示“ O”,如下所示:

当letter_height = 170&letter_width = 10时为O

However, if you compare this to the "H" (another letter that can be drawn by my program), you can easily see that they are not in proportion whatsoever: 但是,如果将其与“ H”(可以由我的程序绘制的另一个字母)进行比较,则可以很容易地看出它们之间没有任何比例关系:

O旁边的O

What I want to do is draw an ellipse for the "O" where its vertical radius is equal to letter_height & its horizontal radius is equal to letter_width such that the "O" will get shorter as letter_width increases, and taller as letter_height increases. 我想做的是为“ O”绘制一个椭圆,其垂直半径等于letter_height ,其水平半径等于letter_width这样,当letter_width增加时,“ O”将变短,而在letter_height增加时将变高。 The problem is, I don't really know how to do that! 问题是,我真的不知道该怎么做! I heard that you can stamp one, but I really do not want to use the stamp method since its animation does not look as appealing. 我听说你可以邮票之一,但我真的希望使用邮票的方法,因为它的动画看起来并不吸引人。 Also, when I try to map my letter_height and letter_width values to it, it covers the entire screen for some reason! 另外,当我尝试将我的letter_heightletter_width值映射到它时,由于某种原因,它会覆盖整个屏幕!

In conclusion, I would like to know how to draw an ellipse in turtle graphics that can be manipulated like a circle (change radii lengths of the ellipse, change the ellipse's extent, etc). 总之,我想知道如何在乌龟图形绘制一个椭圆,该椭圆可以像一个圆一样进行操作 (更改椭圆的半径长度,更改椭圆的范围等)。 I don't want to use the turtle.stamp() method, so is there any way to draw an ellipse other than stamping one onto the canvas? 不想使用turtle.stamp()方法,那么有没有什么办法来绘制比冲压一个拖到画布以外的椭圆形? Any help is much appreciated! 任何帮助深表感谢!

After testing @moomoomoo309's ellipse code and finding problems (prints in wrong place, width and height don't match arguments, ignores turtle heading so can't print slanted ellipses, heading doesn't track drawing, doesn't leave pen in original state, etc.) I decide to try to write my own. 在测试@ moomoomoo309的椭圆代码并发现问题后(错误位置,宽度和高度上的打印与参数不匹配,忽略了乌龟标题,因此无法打印倾斜的椭圆,标题无法跟踪绘图,不会使笔处于原始状态等),我决定尝试编写自己的。

I chose to use turtle.circle() as a model with respect to where the ellipse is drawn relative to the existing turtle position and heading, allowing the user to change the steps (ie make other irregular polygons), leave the pen state and position where it started, etc. This is what I came up with (I used self instead of turtle or pen as I intended it to be installed as a method): 我选择使用turtle.circle()作为相对于相对于现有乌龟位置和方向绘制椭圆的位置的模型,从而允许用户更改步骤(即制作其他不规则多边形),保持笔的状态和位置它是从哪里开始的,等等。这就是我的想法(我使用self代替了turtlepen因为我打算将其作为一种方法安装):

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()

(Optionally) add this method to our turtle per Adding a Method to an Existing Object Instance : (可选) 通过向现有对象实例添加方法,将此方法添加到乌龟中:

from functools import partial

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

Demonstration code to show all the new shapes we can draw with turtle.ellipse() : 演示代码显示我们可以使用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()

EXAMPLE OUTPUT 示例输出

在此处输入图片说明

I tried to implement the extent a la turtle.circle() but wasn't able to get it to work for arbitrary extents properly (ie in such a way that you could invoke turtle.ellipse() twice with the same extent and have it continue the curve where it left off) so I've left that for another day. 我试图实现extent一拉turtle.circle()但无法得到它的任意程度的正常工作(即在这样的,你可以调用一个方法turtle.ellipse()两次以相同的程度,并把它继续从中断处开始的曲线),所以我又将其留了一天。

Bringing my answer back to the OP's original problem, we can now do: 将我的答案带回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()

To generate the skinny ellipse-based letter O that the OP desired: 要生成OP所需的基于椭圆的瘦字母O:

在此处输入图片说明

I'm pretty sure this will work, the 180 in width/180 and height/180 might be off though. 我很确定这是可行的,宽度/ 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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM