Chpater3 今天来学习画一棵树(II)以有规律的随机画出拟真的树枝 原来画一颗树不难嘛!

此篇接续第一篇:https://ithelp.ithome.com.tw/articles/10269980
接下来我们把造树的步骤拆分成骨干、画树枝、树叶,而到目前为止只完成了一个很阳春的骨干。

更加真实的骨干

在这个步骤中,可以加入一些随机元素,思考逻辑如下:

  1. 当树枝进行一节节生长时,需要不断向上输送营养,过程会有损耗
  2. 当树枝主干要进行分岔时,营养会分别被两个子树枝给瓜分

按照这个逻辑,我们依序设计以下变数并添加到昨天的建构式:

  1. shrink表示树枝随着一节节越来越短 (约0.55 - 0.65之间)
  2. diff表示两个子树枝瓜分营养後,导致的长短差异 (约正负0.15之间)
if(times > 0){
    let endX = x + r * Math.cos(theta / 180 * Math.PI);
    let endY = y + r * Math.sin(theta / 180 * Math.PI);
    let shrink = 0.65 + random(0.1); // from 0.55 to 0.65
    let diff = random(0.3) - 0.15;  // +-0.15
    this.son = 
         [new Tree(this, endX, endY, r * (shrink - diff), theta+30, times-1),
          new Tree(this, endX, endY, r * (shrink + diff), theta-30, times-1)];
}

diff 越少看起来越完美,越多则越容易有枝叶营养不良
shrink 越少整个树越小

这样一来,就可以看到现在树枝的分岔更加的写实跟蜿蜒曲折了:
https://ithelp.ithome.com.tw/upload/images/20210923/20135197dV98Y6DAZG.png

为了未来添加风力的效果,今天一边研究一边设计了两个参数a和b,原本的分枝角度各为正负30度,
a: 若让两个分枝都增加角度,就能让分枝逆时针绻曲,反之顺时针
b: 若让两个分枝的正负角度更大,能让分枝更加扩散,反之则是集中

并且为此制作一个函式,可以根据ab值计算座标:

let Tree = function(father, x, y, r, theta, times){
  // ......
  // 省略
  // ......
  this.Transform = function(index){
    if(this.father){
      this.theta = this.father.theta + 30 * ((index == 0)?(a+b+1):(a-b-1));
      this.startX = this.father.endX;
      this.startY = this.father.endY;
      this.endX = this.startX + this.r * Math.cos(this.theta / 180 * Math.PI);
      this.endY = this.startY + this.r * Math.sin(this.theta / 180 * Math.PI);
    }
    // 我是递回唷!还记得我吗?
    if(this.son) this.son.forEach((branch, index) => branch.Transform(index));
  };
}

都是用上一根树枝来一节节往下算,用this.father的末端座标当原点来计算(r, theta)极座标位置

也因此一开始造树的时候第一枝要传入undefined给father

canvas.addEventListener('click', Recreate);
function Recreate(e){
    let Rect = canvas.getBoundingClientRect();
    myTree = new Tree(undefined, WIDTH/2, HEIGHT, HEIGHT/6, -90, 10);
    // 或者,让它从滑鼠的位置长出来
    // let x = (e.pageX - Rect.left) * RATIO;
    // let y = (e.pageY - Rect.top) * RATIO;
    // myTree = new Tree(undefined, x, y, HEIGHT/6, -90, 10);
}

每次点击滑鼠时重新产生树

接着再加上滑鼠的移动事件监听,让ab值随着滑鼠移动改变:

canvas.addEventListener('mousemove', GetMouse);
function GetMouse(e) {
    let Rect = canvas.getBoundingClientRect();
    a = ((e.pageX - Rect.left) * RATIO - WIDTH/2) / (WIDTH/2);
    b = ((e.pageY - Rect.top) * RATIO - HEIGHT/2) / (HEIGHT/2);
    myTree.Transform(); // 呼叫改变树形状的方法
}

如此一来,便能看到树的形状丰富的改变,是很棒的研究材料呢!
(到这边本来有一个demo,不过我加了一些有的没得进去,就爆炸了ww,等我修好明天再一起po)
(因为没有备份,然後又想加入第二章的缓冲函式,制作风力效果,然後就出事了阿北,拍谢拉~)

树枝和树叶

有了真实的骨干终於能开始进一步罗!明天会讲到:Y型树枝、树枝的颜色、树的成长、树叶

我们离Classes又更近了

此时大家有没有发现一件事:Draw和Transform这两个函式不只一组!我们来算一下初始条件设10,总共会有2^10=1024个树枝,那麽,按照我们这个物件的写法,就会有1024组Draw、Transform函式,天哪!太多了吧,摁确实就是这麽多,翻开每一个树枝的物件,都可以看到里面紮紮实实的拥有这两组函式,虽然跟其他人都长得不一样。

那麽,其实有一个方式可以解决,用类别函式Classes来造树枝!(大约就在...第四章节一边做游戏会一边说明)


<<:  《瞬间爆击或者持续伤害》

>>:  视觉化的沟通框架 影响力地图impact mapping

Day 2:Golang 是什麽?

Golang 基本介绍: 是 Google 开发的静态编译程序语言,支援垃圾回收与并发,跟 C 的撰...

[Day 17] Leetcode 56. Merge Intervals (C++)

前言 连假结束加班加起来QQ 今天就来点轻松点,一样是top 100 liked的题目56. Mer...

Day 4 Matrix capsules with EM routing

前言 接续着昨天讲到的EM routing,今天来将EM routing做进一步的解释 EM rou...

Day 28-Unit Test 应用於使用重构与测试手法优化 C# Code-2 (情境及应用-8)

Unit Test 应用於使用重构与测试手法优化 C# Code-2-前言 昨天有提到我们在 Con...

Android Studio - AlertDialog - 列表选单

今天想介绍其他常用的dialog和之前介绍过一般的dialog很像 只是有了一些变化 但都还蛮实用的...