1.1.2 构建科赫雪花
下面来看看如何构建科赫雪花。图 1.2 展示了用于绘制科赫雪花的基本图案,以下称之为片段(flake)。这个片段基于长度为d的线段AB,该线段被分为等长的3部分(AP1、P1P3和P3B),其中每部分的长度都为r。P1并非直接连接到P3,而是经由P2连接到P3。P2是这样确定的:P1、P2和P3构成一个边长为r、高为h的等边三角形。点C为P1和P3之间的中点,它位于P2的正下方,因此AB和CP2是垂直的。
明白如何确定图 1.2 所示的各点后,就可递归地绘制越来越小的片段,从而构建出科赫雪花。大致而言,目标如下:给定点A和点B,需要计算点P1、P2和P3的坐标,并像图1.2那样将它们连接起来。要计算这些坐标,需要使用一些线性代数知识,线性代数是数学的一个分支,能够根据向量计算距离以及确定点的坐标。所谓向量,指的是有长度和方向的量。
图1.2 用于绘制科赫雪花的基本图案
下面介绍一个简单的线性代数公式,后面将用到它。给定三维空间中的点A和单位向量n(单位向量是长度为1的向量),要计算从点A出发沿这个单位向量移动距离d到达的点B的坐标,可使用下面的公式:
B = A + d × n
可通过示例轻松地验证这一点。假设点A的坐标为(5, 0, 0),单位向量n为(0, 1, 0),从点A出发沿这个单位向量移动10个单位到达点B,那么点B的坐标是多少呢?使用刚才的公式,可得到如下结果:
B = (5, 0, 0) + 10 × (0, 1, 0) = (5, 10, 0)
换而言之,要从点A到点B,需要沿y轴的正方向移动10个单位。
下面介绍另一个技巧,即垂直向量计算技巧(perpendicular vector trick)。假设有向量A= (a, b),如果有另一个向量B,它与向量A垂直,那么可将它表示为向量B= (−b, a)。要验证这个关系,可计算A和B的点积。要计算两个二维向量的点积,可将两个向量的第一个分量相乘,并将第二个分量相乘,再将这两个乘积相加。在这里,A和B的点积如下:
两个向量垂直时,它们的点积为0,因此B确实垂直于A。
掌握这些线性代数知识后,回过头来看图1.2所示的片段。给定点A和点B的坐标,如何计算点P2的坐标呢?要到达点P2,可从点C出发,沿单位向量n移动距离h。根据前面的第一个线性代数公式可知:
下面来将变量代入公式。点C为线段AB的中点,因此C = (A + B) / 2。其次,h为边长为r的等边三角形的高。根据勾股定理可知:
在这里,r为点A到点B的距离的三分之一。如果点A的坐标为(x1, y1),点B的坐标为(x2, y2),可使用下面的公式计算它们之间的距离:
只需将这个公式的结果除以3,就可得到r的值。
最后,需要将向量 n 表示出来。假设 n 与向量垂直,而向量可表示为点B的坐标减去点A的坐标:
向量的长度。现在利用前面的垂直向量计算技巧,使用A和B来表示n:
接下来需要计算P1和P3的坐标,为此需要用到另一个线性代数公式。假设有线段AB,而点C位于这条线段上;同时假设a为A到C的距离,而b为B到C的距离。可使用下面的公式来计算点C的坐标:
要明白这个公式,可假设点C为线段AB的中点,此时a和b相等。在这种情况下,凭直觉就能知道C的坐标应该为(A + B) / 2。在上面的公式中,将所有的b都替换为a,结果如下:
有了这个新公式后,就可计算P1和P3的坐标了。这两个点将线段AB三等分,这意味着P1到B的距离为P1到A的距离的两倍(即b1 = 2a1),P3到A的距离为P3到B的距离的两倍(即a3 = 2b3)。将这些值代入前面的公式,可得到如下计算P1和P3坐标的公式:
至此,就获得了绘制科赫雪花分形的第1层级所需的一切:给定点A和点B,然后通过计算确定点P1、P2和P3。在这个分形的第2层级,将第1层级中片段内的每条线段(如图1.2所示)替换为更小的片段,结果如图1.3所示。
图1.3 构建科赫雪花的第2步
注意,对于图1.2所示的4条线段(AP1、P1P2、P2P3和P3B),以每条线段为基础构建了一个新片段。在绘制科赫雪花的程序中,将把每条线段的端点(如A和P1)作为新片段的A和B的值,并递归地执行生成图1.2所示点的计算。
在分形的每个层级,都将再次对片段进行替换,从而绘制出越来越小的自相似图形。这就是科赫雪花绘制算法中的递归步骤,此后不断地重复这个步骤,直到满足基线条件。基线条件应该为线段AB的长度小于特定的阈值,如10像素。到达这个阈值后,将只绘制线段,而不再递归。
为让最终绘制出的科赫雪花更漂亮,可在分形的第1步绘制3个相连的片段,这样结果将更像雪花,呈六角对称,如图1.4所示。
图1.4 合并3个片段
知道如何计算绘制科赫雪花所需的坐标后,下面来看看如何在Python中使用这些坐标来绘制图形。