【问题标题】:How do I draw this shape in Turtle? [closed]我如何在 Turtle 中绘制这个形状? [关闭]
【发布时间】:2020-09-01 08:58:42
【问题描述】:

我今天在一个学习小组中遇到了一个挑战,在 python 海龟库中绘制这个形状。

我想不出一种方法来表达几何解决方案,以找到转弯的角度和我需要的线的大小。

你能告诉我如何单独绘制第一个多边形吗?我已经知道如何制作图案了。

我在五年级。所以请给我一个我能理解的解决方案。

【问题讨论】:

  • 欢迎来到 StackOverflow,Tes。您能否澄清“单独绘制第一个多边形”的意思?您是在问如何绘制例如这部分i.imgur.com/H2wX19n.png 或这部分i.imgur.com/KcdEpON.png 或其他部分?
  • 您可以在 Wikipedia 中找到有关此 kite polygon 形状的一些信息。

标签: python turtle-graphics python-turtle


【解决方案1】:

这是我想出的解决方案。它基于此图:

数学背景

我的解决方案使用“三角法”,这是一种从三角形的另一边的长度和三角形的角度计算三角形一边的长度的方法。这是我希望在 9 年级或 10 年级时教授的高等数学。我不希望五年级的人知道三角学。我也无法解释三角学的每一个细节,因为我必须写很多,而且我不认为我有讲清楚的教学技能。我建议您查看例如此视频以了解该方法:

https://www.youtube.com/watch?v=5tp74g4N8EY

您也可以向您的老师询问更多信息,或自行在互联网上进行研究。

第 1 步:计算角度

我们可以在没有三角函数的情况下做到这一点。

首先,我们看到中间有一个“五边形”(5 边多边形)。我想知道这个“五边形”中一个角的内角。我把这个角度称为X

我们如何计算角度X?我们首先记得三角形内角的和是180°。我们看到我们可以将一个 5 边的多边形分成5-2 三角形,如下所示:

每个5-2 三角形的内角之和为180°。所以对于整个 5 边多边形,内角之和为180° * (5-2)。由于所有角度都具有相同的大小,因此每个角度都是180°*(5-2) / 5 = 108°。所以我们有X = 108°

另一边的角度和X一样。这允许我们计算两个X 之间的角度。我将这个角度称为Y

因为一个完整的圆圈是360°,我们知道360° = 2*X + 2*Y。因此,Y = (360° - 2*X) / 2。我们知道X = 108°,所以我们得到Y = 72°

接下来,我们看到有一个包含Y 角的三角形。我想知道三角形另一个角Z的角度:

三角形的内角之和为180°*(3-2) = 180°。因此,我们知道180° = 2*Y + Z,所以Z = 180° - 2*Y。我们知道Y = 72°,所以我们得到Z = 36°

我们会经常使用角度Z。你可以看到绿色星星的每个角落都有角度Z。蓝星与绿星相同,只是它是旋转的,所以所有蓝色角也有角度Z。红星的角是绿星和蓝星角的两倍宽,所以红星的角是2*Z

第 2 步:计算长度

首先,我们观察到所有外角都在一个圆上。我们称这个圆的半径为R。我们不必计算R。相反,我们可以为R 取任何我们想要的值。我们总是会得到相同的形状,但尺寸不同。我们可以将R 称为形状的“参数”。

鉴于R 的一些价值,我想知道以下长度:

计算A:

我们从A 开始。我们可以看到下面的三角形:

三角形的长边是我们的半径R。另一边的长度为A/2,我们不关心第三边。最右边的角度是Z/2Z = 36° 是我们在上一节中计算的角度)。角度S 是直角,所以S = 90°。我们可以计算第三个角T,因为我们知道三角形的内角总和为180°。因此,180° = S + Z/2 + T。求解T,我们得到T = 180° - S - Z/2 = 180° - 90° - 36°/2 = 72°

接下来,我们使用三角函数来计算A/2。三角学告诉我们A/2 = R * sin(T)。代入T 的公式,我们得到A/2 = R * sin(72°)。求解A,我们得到A = 2*R*sin(72°)

如果您为R 选择一些值,例如R = 100,您现在可以使用此公式计算A。你需要一个sin(72°) 的计算器,因为在你的头脑中计算这个是非常困难的。将sin(72) 输入我的计算器会得到0.951056516。所以对于我们的选择R = 100,我们知道A = 2 * R * sin(72°) = 2 * 100 * 0.951056516 = 190.211303259

计算B:

我们使用相同的技术来找到B 的公式。我们看到下面的三角形:

所以底边是我们半径R的长度。右侧是B/2。我们不关心第三方。最右边的角度是Z/2 的三倍。角度S 是直角,所以我们有S = 90°。我们可以用180° = S + T + 3*Z/2 计算剩余的角度T。求解T,我们得到T = 180° - S - 3*Z/2 = 180° - 90° - 3*36°/2 = 36°。好的,所以T = Z,我们也可以从图片中看到这一点,但现在我们已经计算出来了。

使用三角函数,我们知道B/2 = R * sin(T),因此我们得到公式B = 2 * R * sin(36°) 来计算B 以选择R

计算C:

我们看到下面的三角形:

所以底边的长度为A/2,顶边的长度为B。我们已经有了这两个方面的公式。第三边是C,我们要为其找到一个公式。最右边的角度是Z。角度S 是直角,所以S = 90°。最顶角是Z/2的三倍。

使用三角函数,我们得到C = sin(Z) * B

计算D:

我们看到下面的三角形:

我们已经有了C 的公式。我们想找到D 的公式。最上面的角度是Z/2(我无法将文本放入三角形中)。左下角S 是直角。

使用三角函数,我们知道D = tan(Z/2) * Ctan 函数类似于前面公式中的 sin。您可以再次将其放入您的计算器中以计算该值,因此对于Z = 36°,我可以将tan(36/2) 放入我的计算器中,它给了我0.324919696

计算E:

好的,这很简单,E = 2*D

已经完成了一半!

计算F:

这类似于AB

我们想找到F 的公式。顶部的长度为F/2。底部的长度是我们的半径R。最右边的角有角度ZS 是直角。我们可以计算T = 180° - S - Z = 180° - 90° - Z = 90° - Z

使用三角函数,我们得到F/2 = R * sin(T)。代入T 的公式得到F/2 = R*sin(90° - Z)。求解F 得到F = 2*R*sin(90°-Z)

计算G:

我们看到下面的三角形:

顶部的长度为F,我们已经知道了它的公式。右边的长度为G,我们想为它找到一个公式。我们不关心底部。最左边的角有角度Z/2。最右边的角有角度2*Z。底角有角S,是直角,所以S = 90°。对我来说,红线和绿线完全垂直,所以S 确实是直角对我来说并不是很明显,但是您可以使用三角形内角的公式来验证这一点,这给了你180° = Z/2 + 2*Z + S。求解S 得到S = 180° - Z/2 - 2*Z。使用Z = 36°,我们得到S = 180° - 36°/2 - 2* 36° = 90°

使用三角函数,我们得到G = F * sin(Z/2)

计算H:

我们看到下面的三角形:

右侧的长度为G,我们已经有公式。底部的长度为H,我们想找到一个公式。我们不关心第三方。顶角有角度Z,右下角有角度S。我们已经知道S 是上一节的直角。

使用三角函数,我们得到H = G * tan(Z)

计算I:

这很简单,IA 在同一行。我们可以看到A可以分为A = I + H + E + H + I。我们可以将其简化为A = 2*I + 2*H + E。求解I 得到I = (A - 2*H - E)/2

计算J:

这很简单,JF 在同一行。我们可以看到F可以分为F = G + J + G。我们可以将其简化为F = 2*G + J。求解J 得到J = F - 2*G

编写 Python 程序

我们现在有所有我们感兴趣的行的公式!我们现在可以将它们放入 Python 程序中来绘制图片。

Python 为您提供计算 sintan 的辅助函数。它们包含在math 模块中。因此,您可以将import math 添加到程序顶部,然后您可以在程序中使用math.sin(...)math.tan(...)。但是,有一个问题:这些 Python 函数不使用度数来测量角度。相反,他们使用称为“弧度”的不同单位。幸运的是,度数和弧度之间的转换很容易:以度为单位,一个完整的圆是360°。在弧度中,一个完整的圆是2*pi,其中pi 是一个特殊常数,大约是3.14159265359...。因此,我们可以将以度为单位的角度转换为以弧度为单位的角度,方法是将角度除以360°,然后将其乘以2*pi。我们可以在 Python 中编写以下辅助函数:

import math

def degree_to_radians(angle_in_degrees):
  full_circle_in_degrees = 360
  full_circle_in_radians = 2 * math.pi

  angle_in_radians = angle_in_degrees / full_circle_in_degrees * full_circle_in_radians

  return angle_in_radians

def sin_from_degrees(angle_in_degrees):
  angle_in_radians = degree_to_radians(angle_in_degrees)
  return math.sin(angle_in_radians)

def tan_from_degrees(angle_in_degrees):
  angle_in_radians = degree_to_radians(angle_in_degrees)
  return math.tan(angle_in_radians)

我们现在可以使用我们的函数 sin_from_degreestan_from_degrees 从以度为单位的角度计算 sintan

把它们放在一起:

from turtle import *

import math

# Functions to calculate sin and tan ###########################################

def degree_to_radians(angle_in_degrees):
  full_circle_in_degrees = 360
  full_circle_in_radians = 2 * math.pi

  angle_in_radians = angle_in_degrees / full_circle_in_degrees * full_circle_in_radians

  return angle_in_radians

def sin_from_degrees(angle_in_degrees):
  angle_in_radians = degree_to_radians(angle_in_degrees)
  return math.sin(angle_in_radians)

def tan_from_degrees(angle_in_degrees):
  angle_in_radians = degree_to_radians(angle_in_degrees)
  return math.tan(angle_in_radians)

# Functions to calculate the angles ############################################

def get_X():
  num_corners = 5
  return (num_corners-2)*180 / num_corners

def get_Y():
  return (360 - 2*get_X()) / 2

def get_Z():
  return 180 - 2*get_Y()

# Functions to calculate the lengths ###########################################

def get_A(radius):
  Z = get_Z()
  return 2 * radius * sin_from_degrees(90 - Z/2)

def get_B(radius):
  Z = get_Z()
  return 2 * radius * sin_from_degrees(90 - 3*Z/2)

def get_C(radius):
  Z = get_Z()
  return sin_from_degrees(Z) * get_B(radius)

def get_D(radius):
  Z = get_Z()
  return tan_from_degrees(Z/2) * get_C(radius)

def get_E(radius):
  return 2 * get_D(radius)

def get_F(radius):
  Z = get_Z()
  return 2 * radius * sin_from_degrees(90 - Z)

def get_G(radius):
  Z = get_Z()
  return get_F(radius) * sin_from_degrees(Z/2)

def get_H(radius):
  Z = get_Z()
  return get_G(radius) * tan_from_degrees(Z)

def get_I(radius):
  A = get_A(radius)
  E = get_E(radius)
  H = get_H(radius)
  return (A - E - 2*H) / 2

def get_J(radius):
  F = get_F(radius)
  G = get_G(radius)
  return F - 2*G

# Functions to draw the stars ##################################################

def back_to_center():
  penup()
  goto(0, 0)
  setheading(0)
  pendown()

def draw_small_star(radius):
  penup()
  forward(radius)
  pendown()

  Z = get_Z()

  left(180)
  right(Z/2)

  E = get_E(radius)
  H = get_H(radius)
  I = get_I(radius)

  for i in range(0,5):
    penup()
    forward(I)

    pendown()
    forward(H)

    penup()
    forward(E)

    pendown()
    forward(H)

    penup()
    forward(I)

    left(180)
    right(Z)

  back_to_center()

def draw_green_star(radius):
  pencolor('green')
  draw_small_star(radius)

def draw_blue_star(radius):
  pencolor('blue')

  Z = get_Z()
  left(Z)

  draw_small_star(radius)

def draw_red_star(radius):
  pencolor('red')

  Z = get_Z()

  penup()
  forward(radius)
  pendown()

  left(180)
  right(Z)

  G = get_G(radius)
  J = get_J(radius)

  for i in range(0,10):
    pendown()
    forward(G)

    penup()
    forward(J)

    pendown()
    forward(G)

    left(180)
    right(2*Z)

  back_to_center()

def draw_shape(radius):
  draw_green_star(radius)
  draw_blue_star(radius)
  draw_red_star(radius)

radius = 400
draw_shape(radius)
done()

输出:

【讨论】:

  • 请告诉我,我怎样才能像你一样发布海龟动画?
  • 我没有找到自动的方法(可能有),所以我使用屏幕录制程序(Linux上的simplescreenrecorder)录制窗口的mp4,然后转换为mp4使用 ffmpeg 转成 gif。
  • @AnnZen,如果您使用的是 Mac,请查看 Make an animated GIF from Python turtle using Preview on OSX,但请注意最新的 OSX 会破坏一些步骤,因为这样做很有用。
  • 谢谢,但我不会使用 mac。
【解决方案2】:

这是一个不同的解决方案。它基于一个风筝多边形,其中上部是一对 3-4-5 直角三角形,下部是一对 8-15-17 直角三角形:

from turtle import Screen, Turtle

KITES = 10
RADIUS = 100

def kite(t):
    t.right(37)
    t.forward(100)
    t.right(81)
    t.forward(170)

    t.right(124)

    t.forward(170)
    t.right(81)
    t.forward(100)
    t.right(37)

turtle = Turtle()
turtle.penup()
turtle.sety(-RADIUS)

for _ in range(KITES):
    turtle.circle(RADIUS, extent=180/KITES)
    turtle.pendown()

    kite(turtle)

    turtle.penup()
    turtle.circle(RADIUS, extent=180/KITES)

turtle.hideturtle()

screen = Screen()
screen.exitonclick()

【讨论】:

    【解决方案3】:

    (是的,我对这个谜题很着迷。)@AnnZen 的简洁解决方案给我留下了深刻的印象,我决定看看是否能想出一个更短的解决方案。这个多边形中唯一独特的结构是风筝的侧面:

    所以问题变成了以循环方式绘制其中的十个,然后反转代码以相反的方向再次绘制它们:

    from turtle import *
    
    for _ in range(2):
        for _ in range(10):
            fd(105)
            lt(90)
            fd(76.5)
            pu()
            bk(153)
            rt(54)
            pd()
    
        lt(72)
        lt, rt = rt, lt
    
    done()
    

    【讨论】:

      【解决方案4】:

      解决方法如下:

      import turtle
      #turtle.tracer(0)
      a = turtle.Turtle()
      for _ in range(10):
          a.forward(100)
          a.right(90)
          a.forward(73)
          a.right(72)
          a.forward(73)
          a.backward(73)
          a.right(108)
          a.forward(73)
          a.right(90)
          a.penup()
          a.forward(100)
          a.pendown()
          a.forward(100)
          a.right(108)
      #turtle.update()
      

      【讨论】:

      • 您的解决方案比我的要短得多 :) 您是否手动计算了这些值?
      • 是的,测量了一些角度并进行了一些计算。
      • 你的三步序列:a.left(180); a.forward(73); a.left(72)可以缩短为两步序列:a.backward(73); a.right(108),转:a.left(252)也可以写成:a.right(108)
      • 你说得对,我没有意识到这一点,谢谢。
      【解决方案5】:

      让我们看一下绘制此形状的另一种方法。我们将从与@f9c69e9781fa194211448473495534 相同的图表开始

      在 OP 的原始图像上使用尺子 (1" = 100px) 和量角器,我们可以用非常简单的代码近似此图:

      from turtle import Screen, Turtle
      
      turtle = Turtle()
      turtle.hideturtle()
      turtle.penup()  # center on the screen
      turtle.setposition(-170, -125)
      turtle.pendown()
      
      for _ in range(10):
          turtle.forward(340)
          turtle.left(126)
          turtle.forward(400)
          turtle.left(126)
      
      screen = Screen()
      screen.exitonclick()
      

      这相当于用铅笔绘图,而不是抬起它,也没有在任何线上过度绘制(备份)。

      为了使我们想要的形状弹出,我们将上面绘制的两条线分成更细和更粗的。第一行分为三段,第二行分为五段:

      为此,我们只需将循环中的 forward() 调用分成宽段和窄段:

      for _ in range(10):
          turtle.width(4)
          turtle.forward(105)
          turtle.width(1)
          turtle.forward(130)
          turtle.width(4)
          turtle.forward(105)
      
          turtle.left(126)
      
          turtle.width(1)
          turtle.forward(76.5)
          turtle.width(4)
          turtle.forward(76.5)
          turtle.width(1)
          turtle.forward(94)
          turtle.width(4)
          turtle.forward(76.5)
          turtle.width(1)
          turtle.forward(76.5)
      
          turtle.left(126)
      

      最后我们用升降笔代替粗细变化:

      只需将width() 调用替换为penup()pendown()

      for _ in range(10):
          turtle.pendown()
          turtle.forward(105)
          turtle.penup()
          turtle.forward(130)
          turtle.pendown()
          turtle.forward(105)
      
          turtle.left(126)
      
          turtle.penup()
          turtle.forward(76.5)
          turtle.pendown()
          turtle.forward(76.5)
          turtle.penup()
          turtle.forward(94)
          turtle.pendown()
          turtle.forward(76.5)
          turtle.penup()
          turtle.forward(76.5)
      
          turtle.left(126)
      

      【讨论】:

        【解决方案6】:

        我的短代码:

        from turtle import *
        for f, t in [(0,-72),(71,108),(71,0)]*10+[(29,90),(73,72),(73,90),(29,72)]*10:fd(f),rt(t)
        

        【讨论】:

        • 安,我对这个漂亮解决方案的观察表明,如果您将第二个数组改为 [(28.9, 90), (72.6, 72), (72.6, 90), (28.9, 72)],您将获得更清晰的结果。
        • 酷,如果你用眼睛看的话。真的吗?
        • 是的,我将它分成两个循环用于两个数组,抬起笔并在每次迭代时打印点,两个循环使用两种颜色。然后我可以看到应该重叠的两个循环中的点子集在哪里没有重叠,以及重叠了多少。上下调整单个数字,我能够让它们对齐。我很少“做数学”。
        猜你喜欢
        • 1970-01-01
        • 2014-03-20
        • 1970-01-01
        • 1970-01-01
        • 2015-05-28
        • 1970-01-01
        • 1970-01-01
        • 2016-09-22
        • 2015-06-11
        相关资源
        最近更新 更多