#!/usr/bin/perl -w
#
# Take a 3D function and render it to vertical 2D slices.  Output SVG of these slices.
#
# (c) Matt Evans, 18th April 2009
#

use SVG;
use strict;

my $PI = 3.1415926;

sub myFunc
{
    my $x = shift;
    my $y = shift;
    my $r = sqrt(($x*$x)+($y*$y));

    return cos($r**1.6)*(1+(cos($PI*($r/sqrt(50)))));
}

# inkscape seems to assume 90dpi.. so 90 clicks per inch.

# 210 * X
# 297 * Y
# A4: X=1 Y=1
# A3: X=2 Y=1
# A2: X=2 Y=2

# Page setup
my $pgWidth = 210*2;
my $pgHeight = 297*2;

my $dpi = 90; # Inkscape assumes 90dpi
my $dotsPerMm = $dpi / 25.4; # Scale by this, and one unit equals 1mm...

# Graph slice dimensions
my $graphWidth = 95;
my $graphHeight = 50; # Total
my $graphHeightBase = 15; # Base guaranteed free from cutouts
my $graphHeightTop = $graphHeight - $graphHeightBase;
my $gapX = 2;
my $gapY = 10;
my $graphXres = 180;

# Notch spacing
my $notchY = 10;
my $notchX = 10;
my $notchLen = 5;

# Support guides
my $supportLen   = $graphWidth + 10;
my $supportWidth = 10;
my $graphDepth = $graphWidth;

# Graph coordinates
my $xmin = -5;
my $xmax = 5;
my $ymin = -5;
my $ymax = 5;
my $funcMin = -2;
my $funcMax = 2;

# Slices
my $slices = 30;


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

# create an SVG object
my $svg= SVG->new(width=>$pgWidth."mm",height=>$pgHeight."mm");

sub drawGraph
{
    my $gNum = shift;
    my $ypos = shift;
    my $originX = shift;
    my $originY = shift;

    my @xv = ();
    my @yv = ();

    my $point = 0;

    my $yscale = ($graphHeightTop/($funcMax - $funcMin));

    my $maxZ = 0;

    $xv[$point] = $originX;
    $yv[$point] = $originY;
    $point++;
    my $pointZ;
    for (my $i = 0; $i <= $graphWidth; $i += ($graphWidth/$graphXres))
    {
        my $x = (($i/$graphWidth)*($xmax-$xmin))+$xmin;
        

        $pointZ = $graphHeightBase + (($graphHeightTop/(-$funcMin/$funcMax-$funcMin))+(myFunc($x, $ypos) * $yscale));
        $xv[$point] = $originX + $i;
        $yv[$point] = $originY + $pointZ;
        $point++;
        if ($pointZ > $maxZ)
        {
            $maxZ = $pointZ;
        }
    }
    # Connect back to start
    $xv[$point] = $originX+$graphWidth;
    $yv[$point] = $originY+$pointZ;
    $point++;
    $xv[$point] = $originX+$graphWidth;
    $yv[$point] = $originY;
    $point++;
    $xv[$point] = $originX;
    $yv[$point] = $originY;
    $point++;


    my $mGroup=$svg->group(
        id    => "group_$gNum",
        style => { stroke=>'red', 'fill-opacity'=>0 },
        transform => "scale($dotsPerMm)"
        );

    my $points = $mGroup->get_path(
        x => \@xv,
        y => \@yv,
        -type   => 'polyline',
        -closed => 'false'  #specify that the polyline is closed
        );

    my $tag = $mGroup->polyline(
        %$points,
        id    => "pline_$gNum"
        );    
    $gNum++;

    my $rtagh = $mGroup->line(id=>"rh".$gNum,
                              x1=>$originX, y1=>($originY+$notchY),
                              x2=>($originX+$notchLen), y2=>($originY+$notchY)
        );
    my $rtagv = $mGroup->line(id=>"rv".$gNum,
                              x1=>($originX+$notchX), y1=>($originY),
                              x2=>($originX+$notchX), y2=>($originY+$notchLen)
        );

    my $ltagh = $mGroup->line(id=>"lh".$gNum,
                              x1=>($originX+$graphWidth), y1=>($originY+$notchY),
                              x2=>($originX+$graphWidth-$notchLen), y2=>($originY+$notchY)
        );
    my $ltagv = $mGroup->line(id=>"lv".$gNum,
                              x1=>($originX+$graphWidth-$notchX), y1=>($originY),
                              x2=>($originX+$graphWidth-$notchX), y2=>($originY+$notchLen)
        );
                              
    return $maxZ;
}

sub drawSupport
{
    my $originX = shift;
    my $originY = shift;
    my $sNum    = shift;
    # Draw a rectangle with $slices notches, each $notchLen deep.  Total height of rectangle $supportWidth, and 
    # total length $supportLen.

    my @xv = ($originX, $originX+$supportLen, $originX+$supportLen,   $originX,               $originX );
    my @yv = ($originY, $originY,             $originY+$supportWidth, $originY+$supportWidth, $originY );
    
    my $mGroup=$svg->group(
        id    => "group_s_$sNum",
        style => { stroke=>'red', 'fill-opacity'=>0 },
        transform => "scale($dotsPerMm)"
        );

    my $points = $mGroup->get_path(
        x => \@xv,
        y => \@yv,
        -type   => 'polyline',
        -closed => 'false'  #specify that the polyline is closed
        );
    
    my $tag = $mGroup->polyline(%$points, id => "support_$sNum");

    my $notch;
    for ($notch = 0; $notch < $slices; $notch++)
    {
        my $x = $originX + ((($graphDepth / ($slices-1)) * $notch) + (($supportLen - $graphDepth) / 2));
        $mGroup->line(id=>"notch".$sNum.$notch,
                      x1=>$x, y1=>($originY),
                      x2=>$x, y2=>($originY+$notchLen)
        );
    }
}

my $px;
my $py;
my $grNum = 0;
my $y = $ymin;
my $ystep = ($ymax - $ymin)/($slices-1);

my $maxZ = 0;

for ($py = 0; ($py < $pgHeight) && ($grNum < $slices); $py += ($maxZ + $gapY))
{
    $maxZ = 0;
    for ($px = 0; ($px < $pgWidth) && ($grNum < $slices); $px += ($graphWidth + $gapX))
    {
        my $z;
#            print "$y \n";
        $z = drawGraph($grNum, $y, $px, $py);
        if ($z > $maxZ)
        {
            $maxZ = $z;
        }
        $grNum++;
        $y += $ystep;
    }
}

my $pxInit = $px;
my $support = 0;
for (; $py < $pgHeight; $py += ($supportWidth + $gapY))
{    
    for ($px = $pxInit; $px < $pgWidth; $px += ($supportLen + $gapX))
    {
        if ($support < 4)
        {
            drawSupport($px, $py, $support);
            $support++;
        }
    }
    $pxInit = 0;
}

# now render the SVG object, implicitly use svg namespace
print $svg->xmlify;

