购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3 代码

为绘制科赫雪花,需要定义一个递归函数drawKochSF()。这个函数根据点 A 和点 B 的坐标计算点 P 1 P 2 P 3 (见图1.2)的坐标;再递归地调用自己,为越来越短的线段执行同样的计算,直到满足基线条件;最后使用模块turtle绘制片段。要查看完整的项目代码,请参阅1.7节“完整代码”,也可见本书配套源代码中的“/koch/koch.py”。

1.3.1 计算点的坐标

在函数drawKochSF()中,首先计算为绘制图1.2所示基本片段图案所需的所有点的坐标。

def drawKochSF(x1, y1, x2, y2, t):
    d = math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
    r = d/3.0
    h = r*math.sqrt(3)/2.0
    p3 = ((x1 + 2*x2)/3.0, (y1 + 2*y2)/3.0)
    p1 = ((2*x1 + x2)/3.0, (2*y1 + y2)/3.0)
    c = (0.5*(x1+x2), 0.5*(y1+y2))
    n = ((y1-y2)/d, (x2-x1)/d)
    p2 = (c[0]+h*n[0], c[1]+h*n[1])

函数drawKochSF()被定义为将线段 AB 的端点的 x y 坐标作为参数,该线段定义了图1.4所示雪花中的一个片段。该函数还将turtle对象t作为参数,该参数用于完成实际的绘图工作。接下来,这个函数将计算图1.2所示的所有参数,这些参数在1.1.2小节“构建科赫雪花”中讨论过。首先计算的是d—— A B 的距离。将这个距离除以3得到r,这是构成片段的各条线段(总共4条)的长度。然后根据r计算h,这是片段中间的三角形的高。

接下来计算其他参数,它们都是元组,包含 x 坐标和 y 坐标。元组p3和p1定义了片段中间的三角形底边的两个端点,元组c表示p1和p3的中点,而n是与线段 AB 垂直的单位向量。通过结合使用c、h和n,计算出了p2——片段中间的三角形的顶点坐标。

1.3.2 递归

在函数drawKochSF()的下一部分中,使用递归将第1个片段分解成越来越小的片段。

  ❶ if d > 10:
        # 第1个片段
      ❷ drawKochSF(x1, y1, p1[0], p1[1], t)
        # 第2个片段
        drawKochSF(p1[0], p1[1], p2[0], p2[1], t)
        # 第3个片段
        drawKochSF(p2[0], p2[1], p3[0], p3[1], t)
        # 第4个片段
        drawKochSF(p3[0], p3[1], x2, y2, t)

首先确定了递归停止条件❶。如果d(线段 AB 的长度)大于10像素,就继续递归,这是通过调用函数drawKochSF() 4次实现的。每次调用drawKochSF()时,都传入了一组不同的参数,这些参数是根据构成片段的4条线段的端点坐标确定的,而这些端点坐标已在函数开头计算得到。例如,在❷处,为线段 AP 1 调用了函数drawKochSF(),而此后几次函数调用分别针对的是线段 P 1 P 2 P 2 P 3 P 3 B 。在这些递归调用中,都将根据新的点 A 和点 B 坐标重复之前的计算,并判断d是否依然大于10像素,如果是就再次递归调用drawKochSF() 4次,以此类推。

1.3.3 绘制片段

下面来看看线段 AB 小于10像素时的情况,这是此递归算法的基线条件。小于这个阈值后将不再递归,而是将构成单个片段的4条线段返回,为此使用了模块turtle中的方法up()、down()和setpos(),这些方法在1.1.3小节“使用海龟绘图法绘图”中介绍过。

    else:
        # 绘制中间的角
        t.up()
      ❶ t.setpos(p1[0], p1[1])
        t.down()
        t.setpos(p2[0], p2[1])
        t.setpos(p3[0], p3[1])
        # 绘制两侧的边
        t.up()
      ❷ t.setpos(x1, y1)
        t.down()
        t.setpos(p1[0], p1[1])
        t.up()
      ❸ t.setpos(p3[0], p3[1])
        t.down()
        t.setpos(x2, y2)

首先绘制了由点 P 1 P 2 P 3 构成的角❶,然后绘制了线段 AP 1 ❷和 P 3 B ❸。由于在函数drawKochSF()的开头完成了所有必要的计算,因此实际绘图工作很简单,将合适的坐标传递给方法setpos()即可。

1.3.4 编写函数main()

函数main()创建并设置一个turtle对象,再调用drawKochSF()。

def main():
    print('Drawing the Koch Snowflake...')
 
    t = turtle.Turtle()
    t.hideturtle()
 
    # 绘制科赫雪花
    try:
      ❶ drawKochSF(-100, 0, 100, 0, t)
      ❷ drawKochSF(0, -173.2, -100, 0, t)
      ❸ drawKochSF(100, 0, 0, -173.2, t)
  ❹ except:
         print("Exception, exiting.")
         exit(0)
 
    # 等用户在屏幕上单击后退出
  ❺ turtle.Screen().exitonclick()

从图1.4可知,要绘制3个片段,确保最终输出为六角对称的雪花图形。为此,调用了drawKochSF() 3次:对于第1个片段,点 A 和点 B 的坐标分别为(−100, 0)和(100, 0)❶;对于第2个片段,坐标为(0, −173.2)和(−100, 0)❷;对于第3个片段,坐标为(100, 0)和(0, −173.2)❸。请注意,这些坐标与前面在程序test_turtle.py中绘制三角形时使用的坐标相同。请尝试确定这些坐标是如何计算出来的。(提示: pg22a

为捕获绘图期间可能发生的异常,将对函数drawKochSF()的调用放在一个Python try块中。例如,用户在绘图期间关闭了窗口,将引发异常,可在except块中捕获此异常❹,然后输出一条消息并退出程序。如果用户没有终止绘图过程,将执行代码turtle.Screen().exitonclick()❺,等待用户单击将窗口关闭。 I/nY7oCLw54HX/FfB2jz6ocSSBnB4ZfzPsUKoOW6KlzhSM6FfNzhA5xp08CKpzfV

点击中间区域
呼出菜单
上一章
目录
下一章
×