Mel Recursion & Branching

 

Mel
Recursion & Branching


return to main index



 
Introduction

This tutorial develops a practical application of recursion to generate shapes that resemble trees. The recursive procedure tree() (listing 1) presented in this tutorial produces two branches at each node. The procedure uses the following algorithm to find the end points of the branches that extend in a growthDir vector.


Finding the First Branch

Given growthDirbranchAngle and branchLen,

 

    1   Calculate a circle representing the branch radius - figure 1.
    2   Find a random vector on the circle - figure 2.
    3   Translate the vector up the y-axis by branch length units - figure 3.
    4   Rotate the vector by the branch angle to find the end of a branch - figure 4.
    5   Draw a line between the base of the branch and the (relative) end of the branch.
    6   Recurse


Figure 1


 


Figure 2

 


Figure 3

 


Figure 4


 
Finding the Second Branch

Given growthDirbranchAngle and a vector defining the first branch 
    1   Define the second branch as if it were a reflection of the first
          branch about the growthDir vector - figure 5,
    2   Move the end of the second branch relative to the base point,
    3   Draw the second branch,
    4   Recurse



Figure 5


 
Sample Trees
 


Figure 6

 


Figure 7


 

Listing 1 (tree.mel)


//==============================================
//    aimY
//==============================================
proc float[] aimY(vector $vec)
{
float $out[2];
float $xAngle;
float $zAngle;
float $xyLength;
float $vecLength;
  
$xyLength = sqrt(($vec.x) * ($vec.x) +
                ($vec.y) * ($vec.y));
$vecLength = sqrt(($vec.x) * ($vec.x) +
                ($vec.y) * ($vec.y) + 
                ($vec.z) * ($vec.z));
  
// $xyLength will be zero when $vec is pointing
// along the +z or -z axis
if($xyLength == 0)
    $zAngle = ($vec.x) > 0 ? deg_to_rad(90) : deg_to_rad(-90);
else
    $zAngle = acos(($vec.y)/$xyLength);
  
$xAngle = acos($xyLength/$vecLength);
  
$xAngle = ($vec.z) > 0 ? $xAngle : -$xAngle;
$out[0] = rad_to_deg($xAngle);
 
$zAngle = ($vec.x) > 0 ? -$zAngle : $zAngle;
$out[1] = rad_to_deg($zAngle);
return $out;
}
  
//==============================================
//    vectorOnCircle
//==============================================
proc vector vectorOnCircle(float $rad)
{
// find a random vector within a 2 * rad square
float $x = rand(-$rad, $rad);
float $y = 0;
float $z = rand(-$rad, $rad);
vector $vec = <<$x, $y, $z>>;
  
// convert to a unit vector - this will place
// head of the vector on a circle of radius 1
$vec = unit($vec);
  
// scale it by $rad
$vec = $vec * $rad;
return $vec;
}
  
//==============================================
//    reflect
//==============================================
proc vector reflect(vector $i, vector $n)
{
$i = unit($i);
$n = unit($n);
return $i - 2 * dot($i, $n) * $n;
}
  
//==============================================
//    tree
//==============================================
proc tree(int     $depth,
        vector    $base,
        vector    $growthDir, 
        float     $branchAngle,
        float     $branchLen)
{
if($depth <= 0)
    return;
    
// STEP 1 Find the size of a circle such that if it was
// located at the end of the branch it would define
// a cone whose apex angle equals twice the "branchAngle".
$rad = $branchLen * sin(deg_to_rad($branchAngle));
  
// STEP 1.1    Randomly choose a point on a circle
vector $vec = vectorOnCircle($rad);
  
// STEP 1.2 Transform the point on the circle so that 
// its "branchLen" units up the world y-axis
vector  $x_axis = <<1,0,0>>;
vector  $y_axis = <<0,1,0>>;
vector  $z_axis = <<0,0,1>>;
$vec = $vec + <<0, $branchLen, 0>>;
  
// STEP 1.3 Find the x-axis/z-axis rotations that will
// align the branch $vec to the "growthDir".
float $angle[2] = aimY($growthDir);
$vec = rot($vec, $x_axis, deg_to_rad($angle[0]));
$vec = rot($vec, $z_axis, deg_to_rad($angle[1]));
  
$vec = <<($vec.x), ($vec.y), ($vec.z)>>;
  
// STEP 1.4 Locate the end of the branch relative to the
// base of the branch.
vector $end1 = $base + $vec;
  
// STEP 1.5 Finally, draw the curve segment that "represents"
// the first branch.
curve -d 1  -p ($base.x) ($base.y) ($base.z)
            -p ($end1.x) ($end1.y) ($end1.z);
  
// Recurse to the next branch
tree($depth - 1, $end1, $vec, $branchAngle, $branchLen * 0.6);
  
// STEP 2 Define the second branch as if it were a 
// reflection of the first branch first about the "growthDir"
$vec = reflect(-$vec, $growthDir);
  
$vec = <<($vec.x), ($vec.y), ($vec.z)>>;
  
// STEP 2.1 Locate the end of the branch relative to the
// base of the branch.
vector $end2 = $base + $vec;
  
// STEP 2.2 Finally, draw the curve segment that "represents"
// the second branch.
curve -d 1  -p ($base.x) ($base.y) ($base.z)
            -p ($end2.x) ($end2.y) ($end2.z);
  
// Recurse to the next branch
tree($depth - 1, $end2, $vec, $branchAngle, $branchLen * 0.6);
}
  
select -all;
delete;
  
//seed(1);
vector $begin = <<0,0,0>>;
vector $dir = <<0,1,0>>;
float $angle = 13;
float $len = 2;
tree(7, $begin, $dir, $angle, $len);




© 2002- Malcolm Kesson. All rights reserved.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值