#!/usr/bin/perl

use Vector;
use Getopt::Std;

# Set these manually:

# Smaller is less wide.
$WIDTH=3;

# The smallest radius of branches we're interested in.
$FAST=1;

# The size below which a branch must be to have leaves on it.  Trunk is 1.
$LEAFBEARING=0.3;

# Leaves.  0 for none, anything else uses "leaf$LEAF" for the leaf
$LEAF="";

# 0 for linear, 1 for exponential.
$DECAY=1;

#extend of random twists and turns
$RANDOM=.1;

# rates for above
$LINDECAY=.02;
$EXPDECAY=0.90;

# the how much smaller is a branch than its trunk?  .8 gives trees
# which are very busy at the top; .6 gives spares trees.
$BRANCHDECAY=.7;

# how much smaller does a trunk get when it branches?
$TRUNKDECAY=.4;

# Probability of branching at a given iteration.  from 0 to 1.  
$BRANCHPROB=.4;

# How many trees?
$NUM=1;

# -1 to 1.  Positive values curve branches up, negative is opposite.
# positive is for columnar trees, negative for round fruit-bearing ones
$CURVATURE=.6;

# scale the leaves
$LEAFSCALE=1;

# By default, use real elements
$MESH=0;

# Array name
$NAME="tree";

getopt("wlebtpcnfFN",\%opt);
if ($opt{h})
{
    print <<EOF;
$0 options:

    -w n    width
    -l n    linear decay	\\ choose
    -e n    exponential decay	/ one
    -b n    branch decay
    -t n    trunk decay
    -p n    branch probability
    -c n    curvature
    -f str  leaf type
    -F n    leaf scale
    -n n    number of trees
    -N name name of array (default "tree")
    -m	    Use meshes of triangles instead of real shapes
EOF
    exit();
}

$WIDTH=$opt{w} if $opt{w};
if ($opt{l}) {
    $DECAY=0;
    $LINDECAY=$opt{l};
}
if ($opt{e}) {
    $DECAY=1;
    $EXPDECAY=$opt{e};
}
$BRANCHDECAY = $opt{b} if defined $opt{b};
$TRUNKDECAY = $opt{t} if defined $opt{t};
$BRANCHPROB = $opt{p} if defined $opt{p};
$CURVATURE = $opt{c} if defined $opt{c};
$LEAF = $opt{f} if defined $opt{f};
$NUM = $opt{n} if $opt{n};
$LEAFSCALE = $opt{F} if $opt{F};
$MESH = $opt{m}+0;
$NAME = $opt{N} if $opt{N};

# Some recommendations:
# Columnar:	-w 1 -l .02 -b .6 -t .6 -p .5 -c .2
#  Fuller:      -w 1 -l .02 -b .6 -t .6 -p .6 -c .2
#  Shorter:     -w 1 -l .02 -b .6 -t .4 -p .5 -c .2
#
# Triangle:	-w 1 -e .9 -b .7 -t .4 -p .4 -c .2
#  Fuller:	-w 1 -e .9 -b .7 -t .4 -p .5 -c .2
#
# Roundish:	-w 3 -e .9 -b .7 -t .4 -p .4 -c .6
#		-w 2 -l .02 -b .6 -t .5 -p .5 -c .4
#
# Stunted:      -w 2 -l .03 -b .8 -t .5 -p .5 -c .6


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

# Replace the display so that we can reduce precision
sub dispvector
{
    my ($v)=@_;
    sprintf "%.4g,%.4g,%.4g", $$v[0], $$v[1], $$v[2];
}

sub addleaf
{
    my ($location)=@_;
    return unless $LEAF;

    my $dir=rand(360);
    my $pitch=rand(40)+70;

    if ($MESH) {
	my @pt;
	$pt[0]=vector(0,0,0);
	$pt[1]=vector(.5,.5,0);
	$pt[2]=vector(-.5,.5,0);
	$pt[3]=vector(0,1.2,0);

	for (my $i=0; $i<4; $i++)
	{
	    $pt[$i]*=$LEAFSCALE;
	    $pt[$i]=Vector::rotx($pt[$i], $pitch+20);
	    $pt[$i]=Vector::roty($pt[$i], $dir);
	    $pt[$i]+=$location;
	}

	printf "  triangle { <%s>,<%s>,<%s> texture {leaftexture} }\n",
	    dispvector($pt[0]), dispvector($pt[1]), dispvector($pt[3]);
	printf "  triangle { <%s>,<%s>,<%s> texture {leaftexture} }\n",
	    dispvector($pt[0]), dispvector($pt[2]), dispvector($pt[3]);

    } else {
	printf "  object {leaf$LEAF rotate <%.4g,%.4g,0> scale %.4g translate <%s>}\n",
		$pitch, $dir, $LEAFSCALE, dispvector($location);
    }

}

sub addbranch
{
    my ($start, $radius1, $end, $radius2)=@_;
    if ($MESH) {
	# The triangle version is not pretty.
	my (@p1, @p2);
	my $dir=$end-$start;
	my $norm=cross($dir, vector(1,0,0));

	for (my $i=0; $i<4; $i++)
	{
	    $norm/=mag($norm);
	    push @p1, $norm*$radius1+$start;
	    push @p2, $norm*$radius2+$end;

	    $norm=cross($dir, $norm);
	}
#	print "Going from $start to $end, we have:\n";
#	print "  $p1[0]  $p1[1]  $p1[2]  $p1[3]\n";
#	print "  $p2[0]  $p2[1]  $p2[2]  $p2[3]\n";
	
	# Now print out eight triangles
	for (my $i=0; $i<4; $i++)
	{
	    my $j=$i+1;
	    $j=0 if $j==4;
	    printf "  triangle { <%s>,<%s>,<%s> }\n",
		dispvector($p1[$i]), dispvector($p1[$j]), dispvector($p2[$j]);
	    printf "  triangle { <%s>,<%s>,<%s> }\n",
		dispvector($p1[$i]), dispvector($p2[$i]), dispvector($p2[$j]);
	}
	    

    } elsif ($FAST) {
    # cylinders are faster to draw than cones
	my $radius=($radius1+$radius2)/2;
	printf "  cylinder { <%s>,<%s>,%.4g }\n",dispvector($start),
		dispvector($end), $radius;
    } else {
	printf "  cone { <%s>,%.4g,<%s>,%.4g }\n",dispvector($start),
		$radius1, dispvector($end), $radius2;
    }
}

sub branch
{
    my ($radius, $location, $direction) = @_;

    my $newradius;

    addleaf($location) if ($radius < $LEAFBEARING);

    if ($radius < .04) {
	return;
    }

    if ($DECAY) {
	$newradius=$radius*$EXPDECAY;
    } else {
	$newradius=$radius-$LINDECAY;
    }

    my $endloc=$location+$direction;
    addbranch($location, $radius, $endloc, $newradius);

    # branch around a point halfway between the current direciton
    # and vertical
    my $center=(vector(0,1,0)+$direction)/2;

    if (rand() < $BRANCHPROB)
    {
	my $branchdir=$center+vector(rand($WIDTH*2)-$WIDTH,0,
		rand($WIDTH*2)-$WIDTH);

	# branch radius is between 1/3 and 2/3 of current
#   	my $branchradius=$radius/3+rand($radius/3);
	my $branchradius=$radius/2;

	# If we can't branch, stop this branch too	
#	return if $branchradius < $SIZELIMIT;
	branch($radius*$BRANCHDECAY, $location, $branchdir, rand(6));

	$radius*=$TRUNKDECAY;
    }

    # occasional direction perturbation
    if (rand() < $RANDOM) {
	 $direction+=vector(rand(.2)-.1,rand(.2)-.1,rand(.2)-.1);
    }

    # draw towards center
    if (rand() < abs($CURVATURE))
    {
	my $diff=$direction-vector(0,1,0);
	if ($CURVATURE > 0)
	{
	    $direction-=$diff*.2;
	}
	else
	{
	    $direction+=$diff*.2;
	}
    }

    branch($newradius, $endloc, $direction);
}

sub start
{
    print "#declare num$NAME = $NUM;\n";
    print "#declare $NAME = array[$NUM]\n";
}

sub starttree
{
    my ($num)=@_;

    if ($MESH) {

    print <<EOF;
#declare ${NAME}[$num] =
mesh
{
EOF

    } else {
    
    print <<EOF;
#declare ${NAME}[$num] =
union
{
EOF

    }
}

sub endtree
{
    # the scale is entirely empirical based on about how tall I think
    # a tree of the style these look like should be
    print "scale .5\n}\n";
}

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

start;
for ($i=0; $i<$NUM; $i++)
{
    starttree($i);
    addbranch(vector(0,0,0),1,vector(0,8,0),1);
    branch(1, vector(0, 8, 0), vector(0, 1, 0));
    endtree;
}
