分形树

作者: 大龙10 | 来源:发表于2022-07-30 05:50 被阅读0次

    书名:代码本色:用编程模拟自然系统
    作者:Daniel Shiffman
    译者:周晗彬
    ISBN:978-7-115-36947-5
    第8章目录

    8.5 树

    • 到目前为止,我们接触的分形都是确定性的,也就是说,这类分形没有任何随机因
      素,每次运行都会构建出相同的结果。
    • 对于传统分形和可视化编程技术的演示,它们是非常不错的素材;
    • 但在模拟方面,它们过于准确,不够贴近自然。
    • 接下来讨论随机(非确定性)分形的构建技术。

    1、确定性分形技术构建

    • 本节要模拟的是带有分支的树。

    • 首先,让我们用确定性分形技术构建一棵分形树。构建规则如下:


    • 再一次,我们用递归方式构建了一个分形:树枝是一个线段,线段末尾有两根小树枝。


    2、实现的思路

    • 这个分形的难点在于它的分形规则用到了旋转,每个新的树枝必须在原树枝上旋转一定的角度,而原树枝也是如此。幸运的是:Processing专门有一个机制用于管理旋转角度,即变换矩阵。
    • 让我们从主干开始。由于要涉及rotate()函数,我们必须在绘制过程中不断地沿着树枝平移。主干是从屏幕底部开始的(如上图所示),因此我们要做的第一件事就是平移到主干所在的位置。
      translate(width/2,height);
    • 紧接着我们要画一根向上延伸的线段
      line(0,0,0,-100);
    • 树干绘制完成后,为了画出它的树枝,我们需要平移到树干的末端,然后进行旋转。
      (最后,我们需要把这些操作实现为递归函数,但在此之前要先梳理出具体步骤。)
    • 记住,在Processing中,旋转始终都是绕着原点进行的。因此,我们必须把原点平移到当前树枝的末端。
    translate(0,-100);
    rotate(PI/6);
    line(0,0,0,-100);
    
    • 现在我们已经画好了右边的树枝,下面要添加左边的树枝。在向右旋转之前,我们可以调用pushMatrix()函数保存转换矩阵的当前状态,旋转完成后再调用popMatrix()函数恢复状态,接着在树干左边画树枝。以下是全部代码。
    translate(width/2, height);
    line(0,0,0,-100); 树干
    translate(0,-100);
    pushMatrix();
    rotate(PI/6);
    line(0,0,0,-100); 右边的树枝
    popMatrix();
    rotate(-PI/6);
    line(0,0,0,-100); 左边的树枝
    

    3、递归树的实现

    • 如果把每个line()函数调用都当成一根“树枝”,你会发现,树枝其实是一条线段,线段的末尾还连接着两条线段。
      我们可以直接调用line()函数产生更多树枝,但类似于康托尔集和科赫曲线,这么做会让代码变得复杂而笨重。
      相反地,我们可以把上述逻辑作为递归函数的实现思路,把其中的line()函数调用替换为递归的branch()函数。
      下面来看看这种实现。
    void branch() {
        line(0, 0, 0, -100); 绘制树枝
        translate(0, -100); 平移到末尾
        pushMatrix();
        rotate(PI/6); 向右旋转,再画新的树枝
        branch();
        popMatrix();
        pushMatrix();
        rotate(-PI/6); 向左旋转,再画新的树枝
        branch();
        popMatrix();
    }
    
    • 在上面的代码中,每个branch()函数调用的周围都有成对的pushMatrix()函数和popMatrix()函数调用。
      这种实现方式非常神奇。每次调用branch()函数之前,pushMatrix()函数都会事先记住当前树枝的位置。
      请把自己当作Processing,尝试着用笔和纸跟踪递归函数的执行,你会发现:程序首先画所有右边的树枝,当它达到终点时,popMatrix()函数会逐个恢复之前每个树枝的状态,然后再开始画左边的树枝。
    • 以上递归函数并不会画出一棵树,因为它没有退出条件,最终只是陷入无限的递归调用。你可能还会发现,图中树枝随着层数的增加而缩短。以下代码让树枝的长度不断缩短,一旦树枝长度小于某个值,就停止递归。
    void branch(float len) { branch()函数接受一个长度参数
        line(0, 0, 0, -len);
        translate(0, -len);
        len *= 0.66; 每个树枝的长度以2/3的倍数缩短
        if (len > 2) {
            pushMatrix();
            rotate(theta);
            branch(len); 后续的branch()调用必须传入长度参数
            popMatrix()
            pushMatrix();
            rotate(-theta);
            branch(len);
            popMatrix();
            }
    }
    

    4、随机分形树

    • 在分形树中加入一点点随机性就可以使它的外形更贴近自然。
      查看一棵树的外形,你会发现每根树枝的角度和长度都不相同,此外,每根树枝的分支数量也不相同。
      简单地改变树枝的角度和长度能带来什么样的结果?这很容易实现,只要在绘制时获取一个随机数即可。
    void branch(float len) {
        float theta = random(0, PI/3); 选择一个随机数作为树枝的角度
        line(0, 0, 0, -len);
        translate(0, -len);
        len *= 0.66;
        if (len > 2) {
            pushMatrix();
            rotate(theta);
            branch(len);
            popMatrix();
            pushMatrix();
            rotate(-theta);
            branch(len);
            popMatrix();
            }
    }
    

    相关文章

      网友评论

          本文标题:分形树

          本文链接:https://www.haomeiwen.com/subject/ikqoirtx.html