Как нарисовать эллипс в графике черепахи Python, кроме штамповки?

Я пытаюсь нарисовать букву «О» с графикой черепахи Python. Чтобы указать рисунок «О», функция для этого вызывается нажатием клавиши. Вот что у меня есть до сих пор:

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, используя диалоговое окно, вызываемое другим нажатием клавиши. Прямо сейчас «O» появляется, как показано ниже, если letter_height = 170 и letter_width = 10:

О, когда letter_height = 170 и letter_width = 10

Однако, если вы сравните это с буквой «H» (еще одна буква, которую можно нарисовать с помощью моей программы), вы легко увидите, что они совершенно не пропорциональны:

O рядом с H

Что я хочу сделать, так это нарисовать эллипс для «О», где его вертикальный радиус равен letter_height, а его горизонтальный радиус равен letter_width, так что «О» будет становиться короче по мере увеличения letter_width и выше по мере увеличения letter_height. Проблема в том, что я действительно не знаю, как это сделать! Я слышал, что вы можете штамповать один, но я действительно не не хочу использовать метод штампа, так как его анимация выглядит не так привлекательно. Кроме того, когда я пытаюсь сопоставить с ним свои значения letter_height и letter_width, оно по какой-то причине закрывает весь экран!

В заключение я хотел бы знать, как нарисовать эллипс в графике черепахи, которым можно манипулировать как кругом (изменить длину радиуса эллипса, изменить его размер и т. д.). Я не хочу использовать метод turtle.stamp(), так есть ли способ нарисовать эллипс, кроме как напечатать его на холсте? Любая помощь высоко ценится!


person R. Kap    schedule 15.12.2015    source источник
comment
Вы имеете в виду динамическое управление? Или просто параметризовано?   -  person Peter Wood    schedule 15.12.2015
comment
@ Питер Вуд Хорошо, круг параметризован или динамически манипулируется?   -  person R. Kap    schedule 15.12.2015
comment
Я думаю, вам придется аппроксимировать его множеством прямых линий.   -  person tobias_k    schedule 15.12.2015
comment
Кстати, на изображении показана капсула, а не эллипс.   -  person George Birbilis    schedule 04.09.2017
comment
Помимо ответа @cdlane, это выглядит интересно: mathopenref.com/coordcirclealgorithm.html   -  person George Birbilis    schedule 04.09.2017


Ответы (2)


После тестирования кода эллипса @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()

(Необязательно) добавьте этот метод в нашу черепаху на Добавление метода к существующему Экземпляр объекта:

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

Чтобы создать тонкую букву O на основе эллипса, которую хотел OP:

введите здесь описание изображения

person cdlane    schedule 26.12.2016

Я почти уверен, что это сработает, хотя 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()
person moomoomoo309    schedule 08.12.2016
comment
Этот код не рисует эллипс в точках x, y; он не рисует его width в ширину и height в высоту. 180 в выражениях width/180 и height/180, вероятно, должны быть ближе к 60 и на самом деле не должны быть там вообще, за исключением того, что вы сделали penX += вместо penX + (то же самое penY +=). Этот код игнорирует текущий заголовок черепахи, поэтому вы не можете рисовать диагональные эллипсы. Я попытался реализовать свою собственную реализацию ellipse() для решения этих проблем. - person cdlane; 26.12.2016