#
# A virtual tree generator.
#
#				Gershon Elber, June 1994.
#

iritstate( "RandomInit", 1964 );  # Seed-initiate the randomizer.

save_mat = view_mat;

#
# Factor of width reduction as we traverse the tree from its root to its
# leaves, WFactor, and length reduction factor, LFactor.
WFactor = 0.7;
LFactor = 0.8;

#
# Relative factor of branch rotation.
RFactor = 1.5;

#
# Colors of tree branches and leaves.
BRGB = "144,164,96";
LRGB = "10,255,10";
BColor = 8;
LColor = green;

#
# A function to compute a unit vector perpendicular to the given vector.
#
PerpVector = function( V ):V1:
    V1 = vector( coord( V, 1 ),
		 coord( V, 2 ),
		 coord( V, 0 ) ):
    return = normalize( V1 ^ V );

#
# Functions to change direction of V using a perpendicular to V.
#
RotateVector = function( V, Amount ):V1:
    V1 = normalize( PerpVector( V ) ^ V ) * sqrt( V * V ):
    if ( Amount > 0,
	 return = V + V1 * random( 0, Amount ),
	 return = V + V1 * random( Amount, 0 ) );
RotateVector2 = function( V, Amount ):V1:V2:
    V1 = normalize( PerpVector( V ) ):
    V2 = normalize( V1 ^ V ) * sqrt( V * V ):
    if ( Amount > 0,
	 return = V + V1 * random( 0, Amount ) + V2 * random( 0, Amount ),
	 return = V + V1 * random( Amount, 0 ) + V2 * random( Amount, 0 ) );

#
# This function should define a branch from P1 to P2 with an approximated
# radius of R.
#
TreeBranch = function(Pt1, Pt2, R):
    return = SwpSclSrf( circle( vector( 0, 0, 0 ), 1 ),
			coerce(Pt1, E3) + coerce(Pt2, E3),
			ctlpt(E2, 0, R) + ctlpt(E2, 1, R * WFactor),
			off,
			1.0);

#
# A recursive constructor of the tree2. Gets position of root of tree,
# Direction of branch, Size of branch, Level of Branches, and recursion Level.
#
VirtTree2 = function(): return = 0; # Dummy function for recursive def.
VirtTree2 = function( Pos, Dir, Size, BLevel, Level ) : NewPos:Tr1:Tr2:Tr:
#   printf("%pf, %vf, %f, %f\n", list( Pos, Dir, Size, Level ) ):
    return = nil():
    NewPos = Pos + Dir:
    if ( Level > 0,
         Tr = treeBranch( Pos, NewPos, Size ):
         if ( Level >= BLevel,
	      color( Tr, BColor ):
	      attrib( Tr, "ptexture", "trunk.rle" ):
	      attrib( Tr, "rgb", BRGB ),
	      color( Tr, LColor ):
	      attrib( Tr, "rgb", LRGB ):
	      attrib( Tr, "ptexture", "leaves.rle" ) ):
	 snoc( Tr, return ) ):
    if ( Level > 1,
	 Tr1 = VirtTree2( NewPos,
		          rotateVector( Dir, RFactor ) * LFactor,
			  Size * WFactor,
			  BLevel,
			  Level - 1 ):
	 Tr2 = VirtTree2( NewPos,
		          rotateVector( Dir, -RFactor ) * LFactor,
			  Size * WFactor,
			  BLevel,
			  Level - 1 ):
         return = return + Tr1 + Tr2 );
#
# A recursive constructor of the tree3. Gets position of root of tree,
# Direction of branch, Size of branch,, Level of Branches and recursion Level.
#
VirtTree3 = function(): return = 0; # Dummy function for recursive def.
VirtTree3 = function( Pos, Dir, Size, BLevel, Level ) : NewPos:Tr1:Tr2:Tr3:Tr:
#   printf("%pf, %vf, %f, %f, %f\n", list( Pos, Dir, Size, BLevel, Level ) ):
    return = nil():
    NewPos = Pos + Dir:
    if ( Level > 0,
         Tr = treeBranch( Pos, NewPos, Size ):
         if ( Level >= BLevel,
	      color( Tr, BColor ):
	      attrib( Tr, "ptexture", "trunk.rle" ):
	      attrib( Tr, "rgb", BRGB ),
	      color( Tr, LColor ):
	      attrib( Tr, "rgb", LRGB ):
	      attrib( Tr, "ptexture", "leaves.rle" ) ):
	 snoc( Tr, return ) ):
    if ( Level > 1,
	 Tr1 = VirtTree3( NewPos,
		          rotateVector2( Dir, RFactor ) * LFactor,
			  Size * WFactor,
			  BLevel,
			  Level - 1 ):
	 Tr2 = VirtTree3( NewPos,
		          rotateVector2( Dir, RFactor * random( -1, 1 ) ) * LFactor,
			  Size * WFactor,
			  BLevel,
			  Level - 1 ):
	 Tr3 = VirtTree3( NewPos,
		          rotateVector2( Dir, -RFactor ) * LFactor,
			  Size * WFactor,
			  BLevel,
			  Level - 1 ):
         return = return + Tr1 + Tr2 + Tr3 );

view_mat = rotx( -90 ) * roty( 135 ) * rotx( -30 )
	* scale( vector( 0.2, 0.2, 0.2 ) )
	* trans( vector( 0, -0.5, 0 ) );

tree1 = VirtTree2( point( 0, 0, 0 ), vector( 0, 0, 1 ), 0.3, 4, 7);
interact( list( view_mat, tree1 ) );
free( Tree1 );

tree2 = VirtTree3( point( 0, 0, 0 ), vector( 0, 0, 1 ), 0.5, 3, 5);
interact( tree2 );
free( Tree2 );

forest3 = function( n, m, BLevel, Level ):i:j:
    return = nil():
    for ( i = 0, 1, n,
	for ( j = 0, 1, m,
	    snoc( VirtTree3( point( i * 5, j * 5, 0 ), vector( 0, 0, 1 ),
			     0.3, BLevel, Level ),
		  return ) ) );

base = ruledsrf( ctlpt( E2, -20, -20 ) + ctlpt( E2, -20,  40 ),
		 ctlpt( E2,  40, -20 ) + ctlpt( E2,  40,  40 ) );
attrib( base, "rgb", "244,164,96" );
attrib( base, "ptexture", "ground.rle" );
viewobj( base );
save( "base", base );

frst = forest3( 1, 1, 2, 4 );
view( list( frst, base ), on );

#
# Be prepared, this one is quite large.
#
# frst = forest3( 4, 4, 3, 5 );
# viewstate( "PolyAprx", 0 );
# viewstate( "PolyAprx", 0 );
# viewstate( "PolyAprx", 0 );
# viewstate( "NumIsos", 0 );
# viewstate( "NumIsos", 0 );
# viewstate( "NumIsos", 0 );
# view( list( frst, base ), on );

save( "forrest.ibd", frst );

#############################################################################

view_mat = save_mat;

free( WFactor );
free( LFactor );
free( RFactor );
free( BRGB );
free( LRGB );
free( BColor );
free( LColor );
free( frst );
free( base );
