[comp.lang.postscript] Bezier trees

rob@siesoft (rob) (03/09/89)

I've been reading this group with interest for a few weeks now and I thought 
some of you might be interested in a little program to draw trees - I start
with a "curveto" curve and then recurse at a number of points on the curve.
WARNING - don't try this unless  your printer is otherwise unoccupied for a
half hour or so!! I've also written a similar program in C to generate "flat"
Postscript output. This allows me to "solve" the curve on the fly and tamper
with each "branch" to my heart's content - I found the prospect of solving
cubic equations in postfix rather daunting! If you've got any suggestions
for improvements (this is my first Postscript program) or you'd like the "C"
version please let me know. You can of course hack this to draw single
trees - the largest sensible recursion depth is 5 (about a half hour). A
depth of 3 takes a several minutes.

Rob Henley
Siemens SDG

8-< -------------------------------------------------------------------------


% ------- Variables & Procedures --------
/depth    0 def
/maxdepth 5 def
/down {/depth depth 1 add def} def
/up   {/depth depth 1 sub def} def

/tree
{
  gsave
  translate       % new user space
  rotate          % rotate
  .3  .3  scale   % reduce scale
  0 0 moveto      % move to origin
  6 copy          % copy 6 top items
  curveto         % Bezier cubic
  stroke          % mark the path

  % -- recurse at points calculated to be on the curve --
  down
  depth maxdepth le
  {
    60 12.5 262.5 tree
    -60 28.8 331.2 tree
    60 25.9 401.1 tree
    -40 -6.4 470.4 tree
    90 -78.3 537.3 tree
    -40 -132.3 569.3 tree
    70 -200 600 tree
    -15 -200 600 tree
  } if
  up
  grestore
} def


/season
{
gsave
translate
/maxdepth exch def
200 100 translate
-200 100    300 400    -200 600    0 0 0 tree
grestore
} def


/printbanner
{180 20 moveto (Winter Spring Summer) show} def


% ------- Begin Program --------
newpath

/Times-Italic findfont 30 scalefont setfont
.95 -.05 0 {setgray printbanner -1 .5 translate} for
1 setgray 
printbanner

0 setgray
0 0 moveto 

2 0   350 season
3 300 350 season
4 0   0   season
5 300 0   season

showpage

sanders%yale-zoo-suned@CS.YALE.EDU (Malcolm Sanders) (03/18/89)

   Rob Henley posted a PostScript code which drew fractal trees from
   Bezier curves. By way of introduction he wrote--

>I've been reading this group with interest for a few weeks now and I thought
>some of you might be interested in a little program to draw trees - I start
>with a "curveto" curve and then recurse at a number of points on the curve.
>WARNING - don't try this unless your printer is otherwise unoccupied for a
>half hour or so!! I've also written a similar program in C to generate "flat"
>Postscript output. This allows me to "solve" the curve on the fly and tamper
>with each "branch" to my heart's content - I found the prospect of solving
>cubic equations in postfix rather daunting! If you've got any suggestions
>for improvements (this is my first Postscript program) ....
>version please let me know.....


   Ah yes, where oh where does the path go?  In your program, you generate
   Bezier curves by issuing-

             x0 y0 moveto x1 y1 x2 y2 x3 y3 curveto

   where x0 and y0 are both given values of zero by virtue of a translate
   command.  The problem is to know if the point that you translate to
   is on the curve or not.

   Here is an attempt at a solution:

   The Red Book notes that each Bezier curve is parameterized by a parameter
   t ( in the interval [0,1]) in the following fashion.

              x(t) = Ax*t^3 + Bx*t^2 + Cx*t + x0
              y(t) = Ay*t^3 + By*t^2 + Cy*t + y0

   where

        x1 = x0 + Cx/3               y1 = y0 + Cy/3
        x2 = x1 + (Cx + Bx)/3        y2 = y1 + (Cy + By)/3
        x3 = x0 + Ax + Bx + Cx       y3 = y0 + Ay + By + Cy

   and by inversion of these linear relations, one obtains explicit
   expressions for the coefficients in the parametric polynomials in
   terms of the Bezier points, i.e.,
 
     Ax = x3 - 3*x2 + 3*x1 - x0           Ay = y3 - 3*y2 + 3*y1 - y0
     Bx = 3*(x2 - 2*x1 + x0)              By = 3*(y2 - 2*y1 + y0)
     Cx = 3*(x1 -x0)                      Cy = 3*(y1 - y0)
    
   Thus, the choice of a Bezier curve by picking x0,x1,x2,x3,y0,y1,y2,y3
   determines the values Ax, Bx, Cx, Ay, By, Cy.  Points on the curve
   are found by evaluating x(t),y(t) for various values of t.  Note
   that the position on the curve is a monotonically increasing function
   of t as t goes from 0 to 1, although the relationship between t and
   actual arclength along the curve is not simple(it involves the 
   evaluation of a particularly nasty integral.)

   I incorporated these ideas into Rob's code and I am posting the 
   result.  You can play with different Bezier points and the code
   will insure that all the recursions begin on the curves that were
   drawn during the previous recursion level(PRL).  Playing with the
   parameter t will put branches in different places.   

   I also monkeyed around with the angles so that the angle variable
   which tree eats will be the angle between the tree and the branch
   (branch and twig,...) rather than an absolute angle in the PRL
   graphics state.

   Anyway here follows another stage in the evolution of PostScript
   fractal trees.                            

------------------------------- Cut Here --------------------------------------


%! ------- Bezier Trees --------
% Code by Rob Henley at Siemens SDG 
% Mutation by Malcolm Sanders at Yale         
%
% ---------  Variables and Procedures -------------
% define the Bezier points...
/x0  0   def
/y0  0   def
/x1 -200 def
/y1  100 def
/x2  300 def
/y2  400 def
/x3 -200 def
/y3  600 def

% ...then use them to determine the polynomial coefficients
/Ax x3 x2 x1 sub 3 mul sub x0 sub def
/Ay y3 y2 y1 sub 3 mul sub y0 sub def
/Bx x2 x1 2 mul sub x0 add 3 mul def
/By y2 y1 2 mul sub y0 add 3 mul def
/Cx x1 x0 sub 3 mul def
/Cy y1 y0 sub 3 mul def

% Procedures to evaluate coordinates on Bezier curve-- t on top of stack
/xt {dup dup Ax mul Bx add mul Cx add mul x0 add} def
/yt {dup dup Ay mul By add mul Cy add mul y0 add} def

% Procedures for setting branching angles
/dxt {dup Ax 3 mul mul Bx 2 mul add mul Cx add} def
/dyt {dup Ay 3 mul mul By 2 mul add mul Cy add} def
/baseangle Cy Cx atan def
/curveangle {dup dyt exch dxt atan} def

/depth    0 def
/maxdepth 5 def
/down {/depth depth 1 add def} def
/up   {/depth depth 1 sub def} def

/tree
{
  gsave
  dup dup xt exch yt 
  translate       % new user space
  curveangle baseangle sub exch add
  rotate          % rotate
  .3  .3  scale   % reduce scale
  0 0 moveto      % move to origin
  6 copy          % copy 6 top items
  curveto         % Bezier cubic
  stroke          % mark the path

  % -- recurse at points calculated to be on the curve --
  % -- with specified branching angles --
  down
  depth maxdepth le
  {
    140 .5 tree
    -30 .625 tree
    120 .69 tree
    -40 .8 tree
    100 .9 tree
    -60 .95 tree
    60 1 tree
    -30 1 tree
  } if
  up
  grestore
} def


/season
{
gsave
translate
/maxdepth exch def
200 100 translate
x1 y1 x2 y2 x3 y3    0 0 tree
grestore
} def

/printbanner
{180 20 moveto (Winter Spring Summer) show} def

% ------- Begin Program --------
newpath

/Times-Italic findfont 30 scalefont setfont
.95 -.05 0 {setgray printbanner -1 .5 translate} for
1 setgray 
printbanner

0 setgray
0 0 moveto 

2 0   350 season
3 300 350 season
4 0   0   season
5 300 0   season

showpage  

------------------------------- Cut Here --------------------------------------
 
   __________                                             ____  ____
   ____  ____                                  ____       __________
   __________      |\      /|   |\      /|    /    \      ____  ____
   ____  ____      | \    / |   | \    / |    \____       __________
   __________      |  \  /  |   |  \  /  |         \      ____  ____
   ____  ____      |   \/   |o  |   \/   |o   \___ /o     __________
           
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@ Malcolm M. Sanders        BITNET:  sanders@yalevms              @@@
@@@ Applied Physics           ARPANET: sanders@venus.ycc.yale.edu   @@@
@@@ Yale Station Box 2159                                           @@@
@@@ New Haven, CT 06520       bellnet: (203) 432-4324               @@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@