Fractal tree

From Rosetta Code
Revision as of 12:41, 13 May 2010 by 200.144.37.3 (talk) (added perl)
Task
Fractal tree
You are encouraged to solve this task according to the task description, using any language you may know.

Generate and draw a fractal tree.

To draw a fractal tree is simple:

  1. Draw the trunk
  2. At the end of the trunk, split by some angle and draw two branches
  3. Repeat at the end of each branch until a sufficient level of branching is reached

C

Library: SDL
Library: SGE

or

Library: cairo

<lang c>#include <SDL/SDL.h>

  1. ifdef WITH_CAIRO
  2. include <cairo.h>
  3. else
  4. include <SDL/sge.h>
  5. endif
  6. include <cairo.h>
  7. include <stdlib.h>
  8. include <time.h>
  9. include <math.h>
  1. ifdef WITH_CAIRO
  2. define PI 3.1415926535
  3. endif
  1. define SIZE 800 // determines size of window
  2. define SCALE 5 // determines how quickly branches shrink (higher value means faster shrinking)
  3. define BRANCHES 14 // number of branches
  4. define ROTATION_SCALE 0.75 // determines how slowly the angle between branches shrinks (higher value means slower shrinking)
  5. define INITIAL_LENGTH 50 // length of first branch

double rand_fl(){

 return (double)rand() / (double)RAND_MAX;

}

void draw_tree(SDL_Surface * surface, double offsetx, double offsety,

              double directionx, double directiony, double size,
              double rotation, int depth) {
  1. ifdef WITH_CAIRO
 cairo_surface_t *surf = cairo_image_surface_create_for_data( surface->pixels,
                                                              CAIRO_FORMAT_RGB24,

surface->w, surface->h, surface->pitch );

 cairo_t *ct = cairo_create(surf);
 cairo_set_line_width(ct, 1);
 cairo_set_source_rgba(ct, 0,0,0,1);
 cairo_move_to(ct, (int)offsetx, (int)offsety);
 cairo_line_to(ct, (int)(offsetx + directionx * size), (int)(offsety + directiony * size));
 cairo_stroke(ct);
  1. else
 sge_AALine(surface,
     (int)offsetx, (int)offsety,
     (int)(offsetx + directionx * size), (int)(offsety + directiony * size),
     SDL_MapRGB(surface->format, 0, 0, 0));
  1. endif
 if (depth > 0){
   // draw left branch
   draw_tree(surface,
       offsetx + directionx * size,
       offsety + directiony * size,
       directionx * cos(rotation) + directiony * sin(rotation),
       directionx * -sin(rotation) + directiony * cos(rotation),
       size * rand_fl() / SCALE + size * (SCALE - 1) / SCALE,
       rotation * ROTATION_SCALE,
       depth - 1);

   // draw right branch
   draw_tree(surface,
       offsetx + directionx * size,
       offsety + directiony * size,
       directionx * cos(-rotation) + directiony * sin(-rotation),
       directionx * -sin(-rotation) + directiony * cos(-rotation),
       size * rand_fl() / SCALE + size * (SCALE - 1) / SCALE,
       rotation * ROTATION_SCALE,
       depth - 1);
 }

}

void render(SDL_Surface * surface){

 SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255));
 draw_tree(surface,
     surface->w / 2.0,
     surface->h - 10.0,
     0.0, -1.0,
     INITIAL_LENGTH,
     PI / 8,
     BRANCHES);
 SDL_UpdateRect(surface, 0, 0, 0, 0);

}

int main(){

 SDL_Surface * screen;
 SDL_Event evt;

 SDL_Init(SDL_INIT_VIDEO);

 srand((unsigned)time(NULL));

 screen = SDL_SetVideoMode(SIZE, SIZE, 32, SDL_HWSURFACE);

 render(screen);
 while(1){
   if (SDL_PollEvent(&evt)){
     if(evt.type == SDL_QUIT) break;
   }
   SDL_Delay(1);
 }
 SDL_Quit();
 return 0;

}</lang>

OCaml

Library: ocaml-cairo

<lang ocaml>#directory "+cairo"

  1. load "bigarray.cma"
  2. load "cairo.cma"

let img_name = "/tmp/fractree.png" let width = 480 let height = 640

let level = 9 let line_width = 4.0

let color = (1.0, 0.5, 0.0)

let pi = 4.0 *. atan 1.0

let angle_split = pi *. 0.12 let angle_rand = pi *. 0.12

let () =

 Random.self_init();
 let surf = Cairo.image_surface_create Cairo.FORMAT_RGB24 ~width ~height in
 let ctx = Cairo.create surf in
 Cairo.set_antialias ctx Cairo.ANTIALIAS_SUBPIXEL;
 Cairo.set_line_cap ctx Cairo.LINE_CAP_ROUND;
 let draw_line (x,y) (dx,dy) =
   Cairo.move_to ctx x  (float height -. y);
   Cairo.line_to ctx dx (float height -. dy);
   Cairo.stroke ctx;
 in
 let set_color (r,g,b) v =
   Cairo.set_source_rgb ctx ~red:(r *. v) ~green:(g *. v) ~blue:(b *. v);
 in
 let trans_pos (x,y) len angle =
   let _x = cos angle
   and _y = sin angle in
   (x +. (_x *. len),
    y +. (_y *. len))
 in
 let rec loop ~level ~pos ~line_width ~line_len
              ~angle ~angle_split ~angle_rand ~intc =
   if level > 0 then begin
     (* draw the current segment *)
     Cairo.set_line_width ctx line_width;
     set_color color intc;
     let pos_to = trans_pos pos line_len angle in
     draw_line pos pos_to;
     (* evolution of the parameters *)
     let line_width = line_width *. 0.8
     and line_len   = line_len   *. 0.62
     and angle_split = angle_split *. 1.02
     and angle_rand  = angle_rand  *. 1.02
     and intc = intc *. 0.9
     in
     let next_loop =
       loop ~level:(pred level) ~pos:pos_to ~intc
            ~line_width ~line_len ~angle_split ~angle_rand
     in
     (* split *)
     let angle_left  = angle +. angle_split +. Random.float angle_rand
     and angle_right = angle -. angle_split -. Random.float angle_rand
     in
     next_loop ~angle:angle_left;
     next_loop ~angle:angle_right
   end
 in
 let pos = (float width *. 0.5, float height *. 0.1)
 and line_len = float height *. 0.3
 in
 loop ~level ~pos ~angle:(pi /. 2.0)
      ~angle_split ~angle_rand
      ~line_width ~line_len ~intc:1.0;
 Cairo_png.surface_write_to_file surf img_name
 (*Cairo_png.surface_write_to_channel surf stdout*)</lang>

Perl

using the GD::Simple module. <lang perl>use GD::Simple;

my ($width, $height) = (1000,1000); # image dimension my $scale = 6/10; # branch scale relative to trunk my $length = 400; # trunk size

my $img = GD::Simple->new($width,$height); $img->fgcolor('black'); $img->penSize(1,1);

tree($width/2, $height, $length, 270);

print $img->png;


sub tree {

       my ($x, $y, $len, $angle) = @_;
       return if $len < 1;
       $img->moveTo($x,$y);
       $img->angle($angle);
       $img->line($len);
       ($x, $y) = $img->curPos();
       tree($x, $y, $len*$scale, $angle+35);
       tree($x, $y, $len*$scale, $angle-35);

}</lang>

PostScript

<lang postscript>%!PS %%BoundingBox: 0 0 300 300 %%EndComments /origstate save def /ld {load def} bind def /m /moveto ld /g /setgray ld /t /translate ld /r /rotate ld /l /lineto ld /rl /rlineto ld /s /scale ld %%EndProlog /PerturbateAngle {} def /PerturbateLength {} def % ** To add perturbations, define properly PerturbateAngle and PerturbateLength, e.g. % /PerturbateAngle {realtime 20 mod realtime 2 mod 1 eq {add} {sub} ifelse} def % /PerturbateLength {realtime 10 mod 100 div realtime 2 mod 1 eq {add} {sub} ifelse} def /fractree { % [INITLENGTH, SPLIT, SFACTOR, BRANCHES]

 dup 3 get 0 gt
 {
   0 0 m dup 0 get 0 exch l
   gsave
     dup 0 get 0 exch t
     dup 1 get PerturbateAngle r
     dup 2 get dup PerturbateLength s
     dup aload pop 1 sub 4 array astore fractree stroke
   grestore
   gsave
     dup 0 get 0 exch t
     dup 1 get neg PerturbateAngle r
     dup 2 get dup PerturbateLength s
     dup aload pop 1 sub 4 array astore fractree stroke
   grestore
 } if pop

} def % /BRANCHES 14 def /INITLENGTH 50 def /SPLIT 35 def /SFACTOR .75 def % % BB check %0 0 m 300 0 rl 0 300 rl -300 0 rl closepath stroke % 0 g 150 0 t [INITLENGTH SPLIT SFACTOR BRANCHES] fractree stroke % showpage origstate restore %%EOF</lang>

SVG

In the same style as Dragon curve#SVG. SVG has no parameterized definitions, so the recursion must be unrolled.

<lang xml><?xml version="1.0" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"

"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">

<svg xmlns="http://www.w3.org/2000/svg"

    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="400" height="320">
 <style type="text/css"><![CDATA[
   line { stroke: black; stroke-width: .05; }
   circle { fill: black; }
 ]]></style>

<defs>

 <g id="stem"> <line x1="0" y1="0" x2="0" y2="-1"/> </g>

 <g id="l0"><use xlink:href="#stem"/></g>
 <g id="l1"> <use xlink:href="#l0" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l0" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l2"> <use xlink:href="#l1" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l1" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l3"> <use xlink:href="#l2" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l2" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l4"> <use xlink:href="#l3" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l3" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l5"> <use xlink:href="#l4" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l4" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l6"> <use xlink:href="#l5" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l5" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l7"> <use xlink:href="#l6" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l6" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l8"> <use xlink:href="#l7" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l7" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>
 <g id="l9"> <use xlink:href="#l8" transform="translate(0, -1) rotate(-35) scale(.7)"/>
             <use xlink:href="#l8" transform="translate(0, -1) rotate(+35) scale(.7)"/>
             <use xlink:href="#stem"/></g>

</defs>

<g transform="translate(200, 320) scale(100)">

 <use xlink:href="#l9"/>

</g>

</svg></lang>

Tcl

Library: Tk

<lang tcl>package require Tk

set SIZE 800 set SCALE 4.0 set BRANCHES 14 set ROTATION_SCALE 0.85 set INITIAL_LENGTH 50.0

proc draw_tree {w x y dx dy size theta depth} {

   global SCALE ROTATION_SCALE
   $w create line $x $y [expr {$x + $dx*$size}] [expr {$y + $dy*$size}]
   if {[incr depth -1] >= 0} {

set x [expr {$x + $dx*$size}] set y [expr {$y + $dy*$size}] set ntheta [expr {$theta * $ROTATION_SCALE}]

# Draw left branch draw_tree $w $x $y \ [expr {$dx*cos($theta) + $dy*sin($theta)}] \ [expr {$dy*cos($theta) - $dx*sin($theta)}] \ [expr {$size * (rand() + $SCALE - 1) / $SCALE}] $ntheta $depth # Draw right branch draw_tree $w $x $y \ [expr {$dx*cos(-$theta) + $dy*sin(-$theta)}] \ [expr {$dy*cos(-$theta) - $dx*sin(-$theta)}] \ [expr {$size * (rand() + $SCALE - 1) / $SCALE}] $ntheta $depth

   }

}

pack [canvas .c -width $SIZE -height $SIZE] draw_tree .c [expr {$SIZE/2}] [expr {$SIZE-10}] 0.0 -1.0 $INITIAL_LENGTH \

   [expr {3.1415927 / 8}] $BRANCHES</lang>