
package LaTeX::PGF::Diagram2D::Plot;

use 5.000000;
use strict;
use warnings;

use Carp;

our @ISA = qw();

our $VERSION = '1.00';

require LaTeX::PGF::Diagram2D::Xspline;

# Plot types, Output (t):
#  0  Dots
#  1  Curve
#  2  Lines

# Plot data types, input (d):
#  0  Function, optionally with derivative given
#     d1=function, d2=derivative
#  1  Points (optionally with a derivative)
#     d1=reference to array of points
#  2  Parametric function
#     d1=xfunction d2=yfunction, d3=xderivative (opt), d4=yderivative (opt)
#  3  X-spline points (open spline)




# Dot style:
#  0 = circle
#  1 = square
#  2 = diamond
#  3 = triangle
#  4 = crosshair
#  5 = pentagon


use PDL;



sub new
{
  my $self = undef;
  if($#_ < 0) {
    croak "Usage: LaTeX::PGF::Diagram2D::Plot->new()";
  } else {
    my $class = shift;
    $self = {
      't' => 1,	# Plot type: -1=unknown, 0=points, 1=curve, 2=lines
      'd' => -1,	# Data type: -1=unknown, 0=function, 1=points, 2=param
      'r' => undef,	# Range (for undef use entire x-axis or plot points)
      'd1' => undef,	# Data 1 (Function or array of points)
      'd2' => undef,	# Data 2 (Derivative function)
      'd3' => undef,	# Data 3
      'd4' => undef,	# Data 4
      'f' => undef,	# Flag: Finished.
      'ax' => undef,	# x axis
      'ay' => undef,	# y axis
      'color' => undef,	# Plot color
      'pp' => undef,	# Paper points (userspace coordinates)
      'i' => 20,	# Number of curve intervals
      'ds' => 0,	# Dot style
      'dsz' => 5.0,	# Dot size (diameter, multiple of line size)
      'debug' => 0,	# Flag: Debug
      'xspline' => 8,	# Bezier segments per X-spline segment
    };
    bless($self, $class);
  }
  return $self;
}


sub set_xy_fct
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xy_fct(function,[derivative-function])";
  } else {
    $self = shift;
    $self->{'d1'} = shift;
    if($#_ >= 0) {
      $self->{'d2'} = shift;
    }
    $self->{'d'} = 0;
  }
  return $self;
}



sub set_parametric_fct
{
  my $self = undef;
  if($#_ < 4) {
    croak "Usage: \$plot->set_parametric_fct(min, max, xfct, yfct[, xderiv, yderiv]);";
  } else {
    $self = shift;
    my $min = shift;
    my $max = shift;
    $self->{'r'} = [ $min, $max ];
    $self->{'d1'} = shift;
    $self->{'d2'} = shift;
    $self->{'d'} = 2;
    if($#_ >= 1) {
      $self->{'d3'} = shift;
      $self->{'d4'} = shift;
    }
  }
  return $self;
}



sub set_xy_points
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xy_points(pointsarrayref)";
  } else {
    $self = shift;
    $self->{'d1'} = shift;
    $self->{'d'} = 1;
  }
  return $self;
}



sub set_xy_points_text
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xy_points_text(text)";
  } else {
    $self = shift;
    my $text = shift;
    my @array;
    my $na = 0;
    my $x;
    my $y;
    my $d;
    foreach my $line (split(/\n/, $text)) {
      $x = 0.0; $y = 0.0; $d = 0.0;
      if($line =~ /\S/o) {
        if($line =~ /^\s*(\S+)\s+(\S+)\s+(\S+)/o) {
	  $x = $1; $y = $2; $d = $3;
	  $array[$na++] = [ $x, $y, $d ];
	} else {
	  if($line =~ /^\s*(\S+)\s+(\S+)/o) {
	    $x = $1; $y = $2;
	    $array[$na++] = [ $x, $y ];
	  } else {
	    croak "ERROR: Illegal line \"$line\" in text!";
	  }
	}
      }
    }
    $self->{'d1'} = \@array; $self->{'d'} = 1;
  }
  return $self;
}



sub set_xy_points_file
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xy_points_file(filename)";
  } else {
    $self = shift; my $fn = shift; my $line;
    my @array; my $na = 0;
    my $x; my $y; my $d;
    if(open(my $fh, '<', "$fn")) {
      while(<$fh>) {
        $line = $_; chomp $line;
	if($line =~ /\S/o) {
	  if($line =~ /^\s*(\S+)\s+(\S+)\s+(\S+)/o) {
	    $x = $1; $y = $2; $d = $3;
	    $array[$na++] = [ $x, $y, $d ];
	  } else {
	    if($line =~ /^\s*(\S+)\s+(\S+)/o) {
	      $x = $1; $y = $2;
	      $array[$na++] = [ $x, $y ];
	    }
	  }
	}
      }
      close($fh); $fh = undef;
      $self->{'d1'} = \@array; $self->{'d'} = 1;
    }
  }
  return $self;
}



sub set_xsplines_points
{
  my $self = undef;
  my @pp; my $npp = 0; my $pr; my $x; my $y; my $s;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xspline_points(arrayref [, s_default ])";
  } else {
    $self = shift;
    my $ar = shift;
    my $sdef = -1.0;
    if($#_ >= 0) {
      $sdef = shift;
      if($sdef < -1.0) { $sdef = -1.0; }
      if($sdef >  1.0) { $sdef =  1.0; }
    }
    for(my $i = 0; $i <= $#$ar; $i++) {
      $pr = $ar->[$i];
      if($#$pr > 0) {
        $x = $pr->[0]; $y = $pr->[1]; $s = $sdef;
	if($#$pr > 1) {
	  $s = $pr->[2];
	  if($s < -1.0) { $s = -1.0; }
	  if($s >  1.0) { $s =  1.0; }
	}
	$pp[$npp++] = [ $x, $y, $s ];
      }
    }
    $self->{'d1'} = \@pp; $self->{'d'} = 3;
  }
  return $self;
}



sub set_xsplines_points_text
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xsplines_points_text(text [, s_default ])";
  } else {
    $self = shift; my $t = shift; my $sdef = -1.0;
    my $x; my $y; my $s;
    my @pp; my $npp = 0;
    if($#_ >= 0) {
      $sdef = shift;
      if($sdef < -1.0) { $sdef = -1.0; }
      if($sdef >  1.0) { $sdef =  1.0; }
    }
    foreach my $line (split(/\n/, $t)) {
      $x = 0.0; $y = 0.0; $s = $sdef;
      if($line =~ /\S/o) {
        if($line =~ /^\s*(\S+)\s+(\S+)\s+(\S+)/o) {
	  $x = $1; $y = $2; $s = $3;
	  if($s < -1.0) { $s = -1.0; }
	  if($s >  1.0) { $s =  1.0; }
	  $pp[$npp++] = [ $x, $y, $s ];
	} else {
	  if($line =~ /^\s*(\S+)\s+(\S+)/o) {
	    $x = $1; $y = $2;
	    $pp[$npp++] = [ $x, $y, $sdef ];
	  }
	}
      }
    }
    $self->{'d1'} = \@pp; $self->{'d'} = 3;
  }
  return $self;
}



sub set_xspline_points_file
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_xspline_points_file(filename [, s_default ])";
  } else {
    $self = shift; my $fn = shift; my $sdef = -1; my $fh = undef;
    my $line; my $x; my $y; my $s;
    my @pp; my $npp = 0;
    if($#_ >= 0) {
      $sdef = shift;
      if($sdef < -1.0) { $sdef = -1.0; }
      if($sdef >  1.0) { $sdef =  1.0; }
    }
    if(open($fh, '<', $fn)) {
      while(<$fh>) {
        $line = $_; chomp $line;
	$x = 0.0; $y = 0.0; $s = $sdef;
	if($line =~ /\S/o) {
	  if($line =~/^\s*(\S+)\s+(\S+)\s+(\S+)/o) {
	    $x = $1; $y = $2; $s = $3;
	    if($s < -1.0) { $s = -1.0; }
	    if($s >  1.0) { $s =  1.0; }
	    $pp[$npp++] = [ $x, $y, $s ];
	  } else {
	    if($line =~/^\s*(\S+)\s+(\S+)/o) {
	      $x = $1; $y = $2;
	      $pp[$npp++] = [ $x, $y, $sdef ];
	    }
	  }
	}
      }
      close($fh);
      $self->{'d1'} = \@pp; $self->{'d'} = 3;
    }
  }
  return $self;
}



sub finish_points_points
{
  my $self = shift;
  $self->{'pp'} = $self->{'d1'};
  return $self;
}



sub finish_points_lines
{
  my $self = shift;
  $self->{'pp'} = $self->{'d1'};
  return $self;
}



sub finish_points_curve
{
  my $self = shift;
  $self->{'pp'} = $self->{'d1'};
  return $self;
}



sub finish_function_points
{
  my $self = shift;
  my @dp;		# data points
  my $ndp = 0;		# Number of data points
  my $xs = $self->{'ax'}->{'min'};
  my $xe = $self->{'ax'}->{'max'};
  my $rp = $self->{'range'};
  my $x = 0.0;
  my $y = 0.0;
  if(defined($rp)) {
    $xs = $rp->[0]; $xe = $rp->[1];
  }
  my $diff = $xe - $xs;
  my $i = $self->{'i'};
  if(defined($self->{'d1'})) {
    my $fp = $self->{'d1'};
    if($self->{'ax'}->{'t'}) {
      $diff = exp(log($xe/$xs)/(1.0 * $i));
      $x = $xs;
      $y = &$fp($x);
      $dp[0] = [ $x, $y ];
      for(my $j = 1; $j < $i; $j++) {
        $x = $x * $diff;
	$y = &$fp($x);
	$dp[$j] = [ $x, $y ];
      }
      $x = $xe;
      $y = &$fp($x);
      $dp[$i] = [ $x, $y ];
    } else {
      $x = $xs;
      $y = &$fp($x);
      $dp[0] = [ $x, $y ];
      for(my $j = 1; $j < $i; $j++) {
        $x = $xs + ((1.0 * $j) * $diff)/(1.0 * $i);
        $y = &$fp($x);
        $dp[$j] = [ $x, $y ];
      }
      $x = $xe;
      $y = &$fp($x);
      $dp[$i] = [ $x, $y ];
    }
    $self->{'pp'} = \@dp;
  }
  return $self;
}



sub finish_function_curve
{
  my $self = shift;
  if(defined($self->{'d2'})) {
    my @dp;		# data points
    my $ndp = 0;		# Number of data points
    my $xs = $self->{'ax'}->{'min'};
    my $xe = $self->{'ax'}->{'max'};
    my $rp = $self->{'range'};
    my $x = 0.0;
    my $y = 0.0;
    my $z = 0.0;
    if(defined($rp)) {
      $xs = $rp->[0]; $xe = $rp->[1];
    }
    my $diff = $xe - $xs;
    my $i = $self->{'i'};
    if(defined($self->{'d1'})) {
      my $fp = $self->{'d1'};
      my $dfp = $self->{'d2'};
      if($self->{'ax'}->{'t'}) {
        $diff = exp(log($xe/$xs)/(1.0 * $i));
        $x = $xs;
        $y = &$fp($x);
        $z = &$dfp($x);
        $dp[0] = [ $x, $y, $z ];
        for(my $j = 1; $j < $i; $j++) {
          $x = $x * $diff;
          $y = &$fp($x);
	  $z = &$dfp($x);
          $dp[$j] = [ $x, $y, $z ];
        }
        $x = $xe;
        $y = &$fp($x);
        $z = &$dfp($x);
        $dp[$i] = [ $x, $y, $z ];
      } else {
        $x = $xs;
        $y = &$fp($x);
        $z = &$dfp($x);
        $dp[0] = [ $x, $y, $z ];
        for(my $j = 1; $j < $i; $j++) {
          $x = $xs + ((1.0 * $j) * $diff)/(1.0 * $i);
          $y = &$fp($x);
	  $z = &$dfp($x);
          $dp[$j] = [ $x, $y, $z ];
        }
        $x = $xe;
        $y = &$fp($x);
        $z = &$dfp($x);
        $dp[$i] = [ $x, $y, $z ];
      }
      $self->{'pp'} = \@dp;
    }
  } else {
    $self->finish_function_points();
  }
  return $self;
}



sub finish_param_points
{
  my $self = shift;
  if(defined($self->{'r'}) && defined($self->{'d1'}) && defined($self->{'d2'}))
  {
    my @pp; my $npp = 0;
    my $range = $self->{'r'}; my $fx = $self->{'d1'}; my $fy = $self->{'d2'};
    my $mint = $range->[0]; my $maxt = $range->[1]; my $i = $self->{'i'};
    my $x; my $y;
    my $t;
    $t = $mint;
    $x = &$fx($t); $y = &$fy($t);
    $pp[$npp++] = [ $t, $x, $y, undef, undef ];
    for(my $j = 1; $j < $i; $j++) {
      $t = $mint + ((1.0 * $j) * ($maxt - $mint)) / (1.0 * $i);
      $x = &$fx($t); $y = &$fy($t);
      $pp[$npp++] = [ $t, $x, $y, undef, undef ];
    }
    $t = $maxt;
    $x = &$fx($t); $y = &$fy($t);
    $pp[$npp++] = [ $t, $x, $y, undef, undef ];
    $self->{'pp'} = \@pp;
  }
  return $self;
}



sub finish_param_curve
{
  my $self = shift;
  if(defined($self->{'r'}) && defined($self->{'d1'}) && defined($self->{'d2'}))
  {
    my @pp; my $npp = 0;
    my $range = $self->{'r'};
    my $fx = $self->{'d1'}; my $fy = $self->{'d2'};
    my $fxx = undef; my $fyy = undef;
    if(defined($self->{'d3'}) && defined($self->{'d4'})) {
      $fxx = $self->{'d3'}; $fyy = $self->{'d4'};
    }
    my $mint = $range->[0]; my $maxt = $range->[1]; my $i = $self->{'i'};
    my $x; my $y; my $xx; my $yy;
    my $t;
    $t = $mint;
    $x = &$fx($t); $y = &$fy($t); $xx = undef; $yy = undef;
    if(defined($fxx)) { $xx = &$fxx($t); $yy = &$fyy($t); }
    $pp[$npp++] = [ $t, $x, $y, $xx, $yy ];
    for(my $j = 1; $j < $i; $j++) {
      $t = $mint + ((1.0 * $j) * ($maxt - $mint)) / (1.0 * $i);
      $x = &$fx($t); $y = &$fy($t); $xx = undef; $yy = undef;
      if(defined($fxx)) { $xx = &$fxx($t); $yy = &$fyy($t); }
      $pp[$npp++] = [ $t, $x, $y, $xx, $yy ];
    }
    $t = $maxt;
    $x = &$fx($t); $y = &$fy($t); $xx = undef; $yy = undef;
    if(defined($fxx)) { $xx = &$fxx($t); $yy = &$fyy($t); }
    $pp[$npp++] = [ $t, $x, $y, $xx, $yy ];
    $self->{'pp'} = \@pp;
  }
  return $self;
}



sub finish
{
  my $self = undef;
  if($#_ < 0) {
    croak "Usage: \$plot->finish()";
  } else {
    $self = shift;
    if(!defined($self->{'f'})) {
      my $pt = $self->{'t'};
      my $dt = $self->{'d'};
      if($pt == 0) {
        if($dt == 0) {
          $self->finish_function_points();
        } else {
          if($dt == 1) {
	    $self->finish_points_points();
	  } else {
	    if($dt == 2) {
	      $self->finish_param_points();
	    } else {
	      if($dt == 3) {
	        $self->finish_points_points();
	      }
	    }
	  }
        }
      } else {
        if($pt == 1) {
          if($dt == 0) {
	    $self->finish_function_curve();
	  } else {
	    if($dt == 1) {
	      $self->finish_points_curve();
	    } else {
	      if($dt == 2) {
	        $self->finish_param_curve();
	      } else {
	        if($dt == 3) {
		  $self->finish_points_points();
		}
	      }
	    }
	  }
        } else {
          if($pt == 2) {
	    if($dt == 0) {
	      $self->finish_function_points();
	    } else {
	      if($dt == 1) {
	        $self->finish_points_lines();
	      } else {
	        if($dt == 2) {
	          $self->finish_param_points();
	        } else {
		  if($dt == 3) {
		    $self->finish_points_points();
		  }
		}
	      }
	    }
	  }
        }
      }
      $self->{'f'} = 1;
    }
  }
  return $self;
}



sub dot_circle
{
  my $self = shift;
  my $dg = shift;
  my $fh = shift;
  my $x = shift;
  my $y = shift;
  my $diameter = shift;
  print $fh "\\pgfpathcircle{";
  $dg->write_point($x, $y);
  print $fh "}{" . (0.5 * $diameter) . "bp}\\pgfusepath{fill}\n";
  return $self;
}



sub dot_square
{
  my $self = shift;
  my $dg = shift;
  my $fh = shift;
  my $x = shift;
  my $y = shift;
  my $diameter = shift;
  print $fh "\\pgfpathmoveto{";
  $dg->write_point(($x - 0.5 * $diameter), ($y - 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + 0.5 * $diameter), ($y - 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + 0.5 * $diameter), ($y + 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x - 0.5 * $diameter), ($y + 0.5 * $diameter));
  print $fh "}\n\\pgfpathclose\\pgfusepath{fill}\n";
  return $self;
}



sub dot_diamond
{
  my $self = shift;
  my $dg = shift;
  my $fh = shift;
  my $x = shift;
  my $y = shift;
  my $diameter = shift;
  my $d = $diameter / sqrt(2.0);
  print $fh "\\pgfpathmoveto{";
  $dg->write_point($x, ($y + $d));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x - $d), $y);
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point($x, ($y - $d));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + $d), $y);
  print $fh "}\n\\pgfpathclose\\pgfusepath{fill}\n";
  return $self;
}



sub dot_triangle
{
  my $self = shift;
  my $dg = shift;
  my $fh = shift;
  my $x = shift;
  my $y = shift;
  my $diameter = shift;
  my $a = 0.25 * $diameter;
  my $b = 0.5 * sqrt(0.75) * $diameter;
  print $fh "\\pgfpathmoveto{";
  $dg->write_point($x, ($y + 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x - $b), ($y - $a));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + $b), ($y - $a));
  print $fh "}\n\\pgfpathclose\\pgfusepath{fill}\n";
  return $self;
}



sub dot_crosshair
{
  my $self = shift;
  my $dg = shift;
  my $fh = shift;
  my $x = shift;
  my $y = shift;
  my $diameter = shift;
  print $fh "\\pgfpathmoveto{";
  $dg->write_point($x, ($y + 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point($x, ($y - 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfusepath{stroke}\n";
  print $fh "\\pgfpathmoveto{";
  $dg->write_point(($x - 0.5 * $diameter), $y);
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + 0.5 * $diameter), $y);
  print $fh "}\n\\pgfusepath{stroke}\n";
  return $self;
}



sub dot_pentagon
{
  my $self = shift;
  my $dg = shift;
  my $fh = shift;
  my $x = shift;
  my $y = shift;
  my $diameter = shift;
  my $a = 0.5 * $diameter * cos(2.0 * 3.1415926 / 5.0);
  my $b = 0.5 * $diameter * sin(2.0 * 3.1415926 / 5.0);
  my $c = 0.5 * $diameter * cos(4.0 * 3.1415926 / 5.0);
  my $d = 0.5 * $diameter * sin(4.0 * 3.1415926 / 5.0);
  print $fh "\\pgfpathmoveto{";
  $dg->write_point($x, ($y + 0.5 * $diameter));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x - $b), ($y + $a));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x - $d), ($y + $c));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + $d), ($y + $c));
  print $fh "}\n";
  print $fh "\\pgfpathlineto{";
  $dg->write_point(($x + $b), ($y + $a));
  print $fh "}\n\\pgfpathclose\\pgfusepath{fill}\n";
  return $self;
}



sub plot_points_points
{
  my $self = shift; my $dg = shift;
  my $fh = $dg->{'f1'};
  my $aref = $self->{'pp'};
  my $pref = undef; my $x = undef; my $y = undef;
  my $diameter = $self->{'dsz'} * 0.2 * 72.0 / 25.4;
  if($#$aref > 0) {
    for(my $i = 0; $i <= $#$aref; $i++) {
      $pref = $aref->[$i];
      $x = $pref->[0]; $y = $pref->[1];
      $x = $self->{'ax'}->value_to_coord($x);
      $y = $self->{'ay'}->value_to_coord($y);
      my $dt = $self->{'ds'};
      if($dt == 0) {
        $self->dot_circle($dg, $fh, $x, $y, $diameter);
      }
      if($dt == 1) {
        $self->dot_square($dg, $fh, $x, $y, $diameter);
      }
      if($dt == 2) {
        $self->dot_diamond($dg, $fh, $x, $y, $diameter);
      }
      if($dt == 3) {
        $self->dot_triangle($dg, $fh, $x, $y, $diameter);
      }
      if($dt == 4) {
        $self->dot_crosshair($dg, $fh, $x, $y, $diameter);
      }
      if($dt == 5) {
        $self->dot_pentagon($dg, $fh, $x, $y, $diameter);
      }
    }
  }
  return $self;
}



sub plot_points_lines
{
  my $self = shift; my $dg = shift;
  my $fh = $dg->{'f1'};
  my $aref = $self->{'pp'};
  my $pref = undef; my $x = undef; my $y = undef;
  if($#$aref > 0) {
    for(my $i = 0; $i <= $#$aref; $i++) {
      $pref = $aref->[$i];
      $x = $pref->[0]; $y = $pref->[1];
      $x = $self->{'ax'}->value_to_coord($x);
      $y = $self->{'ay'}->value_to_coord($y);
      if($i == 0) {
        print $fh "\\pgfpathmoveto{";
      } else {
        print $fh "\\pgfpathlineto{";
      }
      $dg->write_point($x, $y);
      print $fh "}\n";
    }
    print $fh "\\pgfusepath{stroke}\n";
  }
  return $self;
}



sub all_values_with_derivative
{
  my $self = shift; my $dg = shift; my $pp = shift; my $fh = $dg->{'f1'};
  my $pref = $pp->[0];
  my $x = $pref->[0];
  my $y = $pref->[1];
  my $z = $pref->[2];
  my $ox = $x; my $oy = $y; my $oz = $z; my $dxdt = undef; my $dydt = undef;
  print $fh "\\pgfpathmoveto{";
  $dg->write_point($x, $y);
  print $fh "}\n";
  for(my $i = 1; $i <= $#$pp; $i++) {
    $pref = $pp->[$i];
    $x = $pref->[0]; $y = $pref->[1]; $z = $pref->[2];
    $dxdt = $x - $ox;
    print $fh "\\pgfpathcurveto{";
    $dydt = $oz * $dxdt;
    $dg->write_point(($ox + ($dxdt/3.0)), ($oy + ($dydt/3.0)));
    print $fh "}{";
    $dydt = $z * $dxdt;
    $dg->write_point(($x - ($dxdt/3.0)), ($y - ($dydt/3.0)));
    print $fh "}{";
    $dg->write_point($x, $y);
    print $fh "}\n";
    $ox = $x; $oy = $y; $oz = $z;
  }
  print $fh "\\pgfusepath{stroke}\n";
  return $self;
}



sub some_values_without_derivative
{
  my $self = shift; my $dg = shift; my $pp = shift;
  my $N = $#$pp + 1;		# Number of points
  my $nsegs = $N - 1;		# Number of segments
  my $neqs = 4 * $nsegs;	# Number of equations and coefficients
  my $eqnno;			# Current equation
  my $pref;			# Reference to current point
  my $j;			# Number index
  my $i;			# Index of pref
  my $x;			# X coordinate
  my $y;			# Y coordinate
  my $z;			# first derivative (dy/dx)
  my $mtx = zeros($neqs, $neqs);	# Coefficients of eq sys
  my $rve = zeros(1, $neqs);		# Results vector of eq sys
  # nsegs: 0 ... nsegs-1
  for($j = 0; $j < $nsegs; $j++) {
    $eqnno = $j; $i = $j;
    $pref = $pp->[$i]; $x = $pref->[0]; $y = $pref->[1];
    $rve->set(0, $eqnno, $y);
    $mtx->set((4*$j), $eqnno, ($x * $x * $x));
    $mtx->set((4*$j+1), $eqnno, ($x * $x));
    $mtx->set((4*$j+2), $eqnno, $x);
    $mtx->set((4*$j+3), $eqnno, 1.0);
  }
  # nsegs: nsegs ... 2*nsegs-1
  for($j = 0; $j < $nsegs; $j++) {
    $eqnno = $nsegs + $j; $i = $j + 1;
    $pref = $pp->[$i]; $x = $pref->[0]; $y = $pref->[1];
    $rve->set(0, $eqnno, $y);
    $mtx->set((4*$j), $eqnno, ($x * $x * $x));
    $mtx->set((4*$j+1), $eqnno, ($x * $x));
    $mtx->set((4*$j+2), $eqnno, $x);
    $mtx->set((4*$j+3), $eqnno, 1.0);
  }
  # nsegs-1: 2*nsegs ... 3*nsegs-2
  for($j = 0; $j < ($nsegs - 1); $j++) {
    $eqnno = 2 * $nsegs + $j; $i = $j + 1;
    $pref = $pp->[$i]; $x = $pref->[0]; $y = $pref->[1];
    $mtx->set((4*$j), $eqnno, (3.0 * $x * $x));
    $mtx->set((4*$j+1), $eqnno, (2.0 * $x));
    $mtx->set((4*$j+2), $eqnno, 1.0);
    $mtx->set((4*$i), $eqnno, (-3.0 * $x * $x));
    $mtx->set((4*$i+1), $eqnno, (-2.0 * $x));
    $mtx->set((4*$i+2), $eqnno, -1.0);
  }
  # nsegs-1: 3*nsegs-1 ... 4*nsegs-3
  for($j = 0; $j < ($nsegs - 1); $j++) {
    $eqnno = 3 * $nsegs - 1 + $j; $i = $j + 1;
    $pref = $pp->[$i]; $x = $pref->[0]; $y = $pref->[1];
    if($#$pref > 1) {
      $z = $pref->[2];
      $mtx->set((4*$j), $eqnno, (3.0 * $x * $x));
      $mtx->set((4*$j+1), $eqnno, (2.0 * $x));
      $mtx->set((4*$j+2), $eqnno, 1.0);
      $rve->set(0, $eqnno, $z);
    } else {
      $mtx->set((4*$j), $eqnno, (6.0 * $x));
      $mtx->set((4*$j+1), $eqnno, 2.0);
      $mtx->set((4*$i), $eqnno, (-6.0 * $x));
      $mtx->set((4*$i+1), $eqnno, -2.0);
    }
  }
  # 4*nsegs-2
  $eqnno = 4 * $nsegs - 2; $j = 0; $i = 0;
  $pref = $pp->[$i]; $x = $pref->[0]; $y = $pref->[1];
  if($#$pref > 1) {
    $z = $pref->[2];
    $mtx->set((4*$j), $eqnno, (3.0 * $x * $x));
    $mtx->set((4*$j+1), $eqnno, (2.0 * $x));
    $mtx->set((4*$j+2), $eqnno, 1.0);
    $rve->set(0, $eqnno, $z);
  } else {
    $mtx->set((4*$j), $eqnno, (6.0 * $x));
    $mtx->set((4*$j+1), $eqnno, 2.0);
  }
  # 4*nsegs-1
  $eqnno = 4 * $nsegs - 1; $j = $nsegs - 1; $i = $j + 1;
  $pref = $pp->[$i]; $x = $pref->[0]; $y = $pref->[1];
  if($#$pref > 1) {
    $z = $pref->[2];
    $mtx->set((4*$j), $eqnno, (3.0 * $x * $x));
    $mtx->set((4*$j+1), $eqnno, (2.0 * $x));
    $mtx->set((4*$j+2), $eqnno, 1.0);
    $rve->set(0, $eqnno, $z);
  } else {
    $mtx->set((4*$j), $eqnno, (6.0 * $x));
    $mtx->set((4*$j+1), $eqnno, 2.0);
  }
  my $res = $mtx->inv() x $rve;
  if($self->{'debug'}) {
    print $res;
  }
  my @ppb; my $nppb = 0;
  for($i = 0; $i <= $#$pp; $i++) {
    $pref = $pp->[$i];
    $x = $pref->[0]; $y = $pref->[1]; $z = 0.0;
    if($#$pref > 1) {
      $z = $pref->[2];
    } else {
      if($i > 0) {
        $z = 3.0 * $res->at(0, 4*($i-1)) * $x * $x
	     + 2.0 * $res->at(0, 4*($i-1)+1) * $x
	     + $res->at(0, 4*($i-1)+2);
      } else {
        $z = 3.0 * $res->at(0, 0) * $x * $x
	     + 2.0 * $res->at(0, 1) * $x
	     + $res->at(0, 2);
      }
    }
    $ppb[$i] = [ $x, $y, $z ];
  }
  $self->all_values_with_derivative($dg, \@ppb);
  return $self;
}



sub plot_points_curve
{
  my $self = shift; my $dg = shift;
  my @ppa; my $nppa = 0;
  my $aref = $self->{'pp'};
  my $pref = undef;
  my $ax = $self->{'ax'}; my $ay = $self->{'ay'};
  my $x = undef;
  my $y = undef;
  my $z = undef;
  my $value_without_derivative = 0;
  for(my $i = 0; $i <= $#$aref; $i++) {
    $pref = $aref->[$i];
    $z = undef;
    $x = $pref->[0]; $y = $pref->[1];
    if($#$pref > 1) {
      $z = $pref->[2]
           * $ay->value_to_derivative($y) / $ax->value_to_derivative($x);
    } else {
      $value_without_derivative = 1;
    }
    $x = $self->{'ax'}->value_to_coord($x);
    $y = $self->{'ay'}->value_to_coord($y);
    if(defined($z)) {
      $ppa[$nppa++] = [ $x, $y, $z ];
    } else {
      $ppa[$nppa++] = [ $x, $y ];
    }
  }
  my @ppb = sort { $a->[0] <=> $b->[0]; } @ppa;
  if($value_without_derivative) {
    $self->some_values_without_derivative($dg, \@ppb);
  } else {
    $self->all_values_with_derivative($dg, \@ppb);
  }
  return $self;
}



sub plot_param_points
{
  my $self = shift; my $dg = shift; my $fh = $dg->{'f1'};
  my $ar = $self->{'pp'};
  my $pr = undef;
  my $x = undef;
  my $y = undef;
  my $dt = $self->{'ds'};
  my $diameter = $self->{'dsz'} * 0.2 * 72.0 / 25.4;
  for(my $i = 0; $i <= $#$ar; $i++) {
    $pr = $ar->[$i];
    $x = $pr->[1];
    $y = $pr->[2];
    $x = $self->{'ax'}->value_to_coord($x);
    $y = $self->{'ay'}->value_to_coord($y);
    if($dt == 0) {
        $self->dot_circle($dg, $fh, $x, $y, $diameter);
    }
    if($dt == 1) {
        $self->dot_square($dg, $fh, $x, $y, $diameter);
    }
    if($dt == 2) {
        $self->dot_diamond($dg, $fh, $x, $y, $diameter);
    }
    if($dt == 3) {
        $self->dot_triangle($dg, $fh, $x, $y, $diameter);
    }
    if($dt == 4) {
        $self->dot_crosshair($dg, $fh, $x, $y, $diameter);
    }
    if($dt == 5) {
        $self->dot_pentagon($dg, $fh, $x, $y, $diameter);
    }
  }
  return $self;
}



sub plot_param_lines
{
  my $self = shift; my $dg = shift; my $fh = $dg->{'f1'};
  my $ar = $self->{'pp'};
  my $pr = undef;
  my $x = undef;
  my $y = undef;
  if($#$ar >= 1) {
    $pr = $ar->[0];
    $x = $self->{'ax'}->value_to_coord($pr->[1]);
    $y = $self->{'ax'}->value_to_coord($pr->[2]);
    print $fh "\\pgfpathmoveto{";
    $dg->write_point($x, $y);
    print $fh "}\n";
    for(my $i = 1; $i <= $#$ar; $i++) {
      $pr = $ar->[$i];
      $x = $self->{'ax'}->value_to_coord($pr->[1]);
      $y = $self->{'ax'}->value_to_coord($pr->[2]);
      print $fh "\\pgfpathlineto{";
      $dg->write_point($x, $y);
      print $fh "}\n";
    }
    print $fh "\\pgfusepath{stroke}\n";
  }
  return $self;
}



sub check_param_derivatives
{
  my $self = shift;
  my $back = 1;
  my $ar; my $pr;
  $ar = $self->{'pp'};
  for(my $i = 0; $i <= $#$ar; $i++) {
    $pr = $ar->[$i];
    if(!(defined($pr->[3]) && defined($pr->[4]))) {
      $back = 0; $i = $#$ar + 1;
    }
  }
  return $back;
}



sub do_param_with_all_derivatives
{
  my $self = shift;
  my $dg = shift;
  my $pp = shift;
  my $fh = $dg->{'f1'};
  my $ox; my $oy; my $odxdt; my $odydt; my $x; my $y; my $dxdt; my $dydt;
  my $pr;
  $pr = $pp->[0];
  $x = $pr->[1]; $y = $pr->[2]; $dxdt = $pr->[3]; $dydt = $pr->[4];
  print $fh "\\pgfpathmoveto{";
  $dg->write_point($x, $y);
  print $fh "}\n";
  $ox = $x; $oy = $y; $odxdt = $dxdt; $odydt = $dydt;
  for(my $i = 1; $i <= $#$pp; $i++) {
    $pr = $pp->[$i];
    $x = $pr->[1]; $y = $pr->[2]; $dxdt = $pr->[3]; $dydt = $pr->[4];
    print $fh "\\pgfpathcurveto{";
    $dg->write_point(($ox + $odxdt/3.0), ($oy + $odydt/3.0));
    print $fh "}{";
    $dg->write_point(($x - $dxdt/3.0), ($y - $dydt/3.0));
    print $fh "}{";
    $dg->write_point($x, $y);
    print $fh "}\n";
    $ox = $x; $oy = $y; $odxdt = $dxdt; $odydt = $dydt;
  }
  print $fh "\\pgfusepath{stroke}\n";
  return $self;
}


sub param_with_all_derivatives
{
  my $self = shift; my $dg = shift;
  my $fh = $dg->{'f1'};
  my @npp;
  my $ar = $self->{'pp'};
  my $pr;
  my $t; my $x; my $y; my $xx; my $yy;
  my $r = $self->{'r'};
  my $mint = $r->[0]; my $maxt = $r->[1];
  my $dTdt = ($maxt - $mint) / (1.0 * $self->{'i'});
  my $ax = $self->{'ax'}; my $ay = $self->{'ay'};
  for(my $i = 0; $i <= $#$ar; $i++) {
    $pr = $ar->[$i];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2]; $xx = $pr->[3]; $yy = $pr->[4];
    $xx = $xx * $ax->value_to_derivative($x) * $dTdt;
    $yy = $yy * $ay->value_to_derivative($y) * $dTdt;
    $x  = $ax->value_to_coord($x);
    $y  = $ay->value_to_coord($y);
    $npp[$i] = [ $t, $x, $y, $xx, $yy ];
  }
  $self->do_param_with_all_derivatives($dg, \@npp);
  return $self;
}



sub find_missing_derivatives
{
  my $self = shift; my $dg = shift; my $pp = $self->{'pp'};
  my @npp;
  my $pr;
  my $t; my $x; my $y; my $xx; my $yy;
  my $N = $#$pp + 1;
  my $nsegs = $N - 1;
  my $ax = $self->{'ax'}; my $ay = $self->{'ay'};
  my $i;
  my $eqnno;
  for($i = 0; $i <= $#$pp; $i++) {
    $pr = $pp->[$i];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2]; $xx = undef; $yy = undef;
    $x = $ax->value_to_coord($x);
    $y = $ay->value_to_coord($y);
    $npp[$i] = [ (1.0 * $i) , $x, $y, $xx, $yy ];
  }
  my $xmtx = zeros((4 * $nsegs), (4 * $nsegs));
  my $ymtx = zeros((4 * $nsegs), (4 * $nsegs));
  my $xres = zeros(1, (4 * $nsegs));
  my $yres = zeros(1, (4 * $nsegs));
  for($i = 0; $i < $nsegs; $i++) {
    $eqnno = $i;
    $pr = $npp[$i];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
    $xres->set(0, $eqnno, $x);
    $xmtx->set((4 * $i), $eqnno, ($t*$t*$t));
    $xmtx->set((4 * $i + 1), $eqnno, ($t * $t));
    $xmtx->set((4 * $i + 2), $eqnno, $t);
    $xmtx->set((4 * $i + 3), $eqnno, 1.0);
    $yres->set(0, $eqnno, $y);
    $ymtx->set((4 * $i), $eqnno, ($t * $t * $t));
    $ymtx->set((4 * $i + 1), $eqnno, ($t * $t));
    $ymtx->set((4 * $i + 2), $eqnno, $t);
    $ymtx->set((4 * $i + 3), $eqnno, 1.0);
  }
  for($i = 0; $i < $nsegs; $i++) {
    $eqnno = $nsegs + $i;
    $pr = $npp[$i + 1];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
    $xres->set(0, $eqnno, $x);
    $xmtx->set((4 * $i), $eqnno, ($t * $t * $t));
    $xmtx->set((4 * $i + 1), $eqnno, ($t * $t));
    $xmtx->set((4 * $i + 2), $eqnno, $t);
    $xmtx->set((4 * $i + 3), $eqnno, 1.0);
    $yres->set(0, $eqnno, $y);
    $ymtx->set((4 * $i), $eqnno, ($t * $t * $t));
    $ymtx->set((4 * $i + 1), $eqnno, ($t * $t));
    $ymtx->set((4 * $i + 2), $eqnno, $t);
    $ymtx->set((4 * $i + 3), $eqnno, 1.0);
  }
  for($i = 0; $i < ($nsegs - 1); $i++) {
    $eqnno = 2 * $nsegs + $i;
    $pr = $npp[$i + 1];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
    $xmtx->set((4 * $i), $eqnno, (3.0 * $t * $t));
    $xmtx->set((4 * $i + 1), $eqnno, (2.0 * $t));
    $xmtx->set((4 * $i + 2), $eqnno, 1.0);
    $xmtx->set((4 * ($i + 1)), $eqnno, (-3.0 * $t * $t));
    $xmtx->set((4 * ($i + 1) + 1), $eqnno, (-2.0 * $t));
    $xmtx->set((4 * ($i + 1) + 2), $eqnno, -1.0);
    $ymtx->set((4 * $i), $eqnno, (3.0 * $t * $t));
    $ymtx->set((4 * $i + 1), $eqnno, (2.0 * $t));
    $ymtx->set((4 * $i + 2), $eqnno, 1.0);
    $ymtx->set((4 * ($i + 1)), $eqnno, (-3.0 * $t * $t));
    $ymtx->set((4 * ($i + 1) + 1), $eqnno, (-2.0 * $t));
    $ymtx->set((4 * ($i + 1) + 2), $eqnno, -1.0);
  }
  for($i = 0; $i < ($nsegs - 1); $i++) {
    $eqnno = 3 * $nsegs - 1 + $i;
    $pr = $npp[$i + 1];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
    $xmtx->set((4 * $i), $eqnno, (6.0 * $t));
    $xmtx->set((4 * $i + 1), $eqnno, 2.0);
    $xmtx->set((4 * ($i + 1)), $eqnno, (-6.0 * $t));
    $xmtx->set((4 * ($i + 1) + 1), $eqnno, -2.0);
    $ymtx->set((4 * $i), $eqnno, (6.0 * $t));
    $ymtx->set((4 * $i + 1), $eqnno, 2.0);
    $ymtx->set((4 * ($i + 1)), $eqnno, (-6.0 * $t));
    $ymtx->set((4 * ($i + 1) + 1), $eqnno, -2.0);
  }
  $i = 0; $eqnno = 4 * $nsegs - 2;
  $pr = $npp[0];
  $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
  $xmtx->set((4 * $i), $eqnno, (6.0 * $t));
  $xmtx->set((4 * $i + 1), $eqnno, 2.0);
  $ymtx->set((4 * $i), $eqnno, (6.0 * $t));
  $ymtx->set((4 * $i + 1), $eqnno, 2.0);
  $i = $nsegs - 1; $eqnno = 4 * $nsegs - 1;
  $pr = $npp[$nsegs];
  $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
  $xmtx->set((4 * $i), $eqnno, (6.0 * $t));
  $xmtx->set((4 * $i + 1), $eqnno, 2.0);
  $ymtx->set((4 * $i), $eqnno, (6.0 * $t));
  $ymtx->set((4 * $i + 1), $eqnno, 2.0);
  my $xco = $xmtx->inv() x $xres;
  my $yco = $ymtx->inv() x $yres;
  for($i = 0; $i < $#npp; $i++) {
    $pr = $npp[$i];
    $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
    $xx = 3.0 * $xco->at(0, (4 * $i)) * $t * $t
          + 2.0 * $xco->at(0, (4 * $i + 1)) * $t
	  + $xco->at(0, (4 * $i + 2));
    $yy = 3.0 * $yco->at(0, (4 * $i)) * $t * $t
          + 2.0 * $yco->at(0, (4 * $i + 1)) * $t
	  + $yco->at(0, (4 * $i + 2));
    $pr->[3] = $xx; $pr->[4] = $yy;
  }
  $i = $#npp - 1;
  $pr = $npp[ $#npp ];
  $t = $pr->[0]; $x = $pr->[1]; $y = $pr->[2];
  $xx = 3.0 * $xco->at(0, (4 * $i)) * $t * $t
        + 2.0 * $xco->at(0, (4 * $i + 1)) * $t
	+ $xco->at(0, (4 * $i + 2));
  $yy = 3.0 * $yco->at(0, (4 * $i)) * $t * $t
        + 2.0 * $yco->at(0, (4 * $i + 1)) * $t
	+ $yco->at(0, (4 * $i + 2));
  $pr->[3] = $xx; $pr->[4] = $yy;
  $self->do_param_with_all_derivatives($dg, \@npp);
}



sub plot_param_curve
{
  my $self = shift; my $dg = shift;
  if($self->check_param_derivatives($dg)) {
    $self->param_with_all_derivatives($dg);
  } else {
    $self->find_missing_derivatives($dg);
  }
  return $self;
}



sub plot_xspline_curve
{
  my $self = shift;
  my $dg = shift;
  my $fh = $dg->{'f1'};
  my $xs = LaTeX::PGF::Diagram2D::Xspline->new();
  my @pp; my $npp = 0;
  my $ar = $self->{'pp'};
  my $ax = $self->{'ax'};
  my $ay = $self->{'ay'};
  my $x;
  my $y;
  my $s;
  my $dxdt;
  my $dydt;
  my $odxdt;
  my $odydt;
  my $ox;
  my $oy;
  my $b;		# Current start point of segment
  my $a;		# Left neighbour
  my $c;		# Current end point of segment
  my $d;		# Right neighbour
  my $pa; my $pb; my $pc; my $pd;
  for($npp = 0; $npp <= $#$ar; $npp++) {
    $pa = $ar->[$npp];
    $x = $pa->[0]; $y = $pa->[1];
    $s = -1.0;
    if($#$pa > 1) {
      $s = $pa->[2];
    }
    $x = $ax->value_to_coord($x);
    $y = $ay->value_to_coord($y);
    $pp[$npp] = [ $x, $y, $s ];
  }
  $ar = \@pp;
  my $subs = $self->{'xspline'}; ; my $i; my $t;
  for($b = 0; $b < $#pp; $b++) {
    $a = $b - 1; $c = $b + 1; $d = $b + 2;
    $pa = undef; $pb = undef; $pc = undef; $pd = undef;
    if($a >= 0) { $pa = $pp[$a]; }
    $pb = $pp[$b];
    $pc = $pp[$c];
    if($d <= $#pp) { $pd = $pp[$d]; }
    $xs->set_points($pa, $pb, $pc, $pd);
    $xs->calculate(0.0);
    $odxdt = $xs->{'ddtx'} / (1.0 * $subs);
    $odydt = $xs->{'ddty'} / (1.0 * $subs);
    $ox = $xs->{'x'}; my $oy = $xs->{'y'};
    if($b == 0) {
      print $fh "\\pgfpathmoveto{";
      $dg->write_point($ox, $oy);
      print $fh "}\n";
    }
    for($i = 1; $i < $subs; $i++) {
      $t = (1.0 * $i) / (1.0 * $subs);
      $xs->calculate($t);
      $x = $xs->{'x'}; $y = $xs->{'y'};
      $dxdt = $xs->{'ddtx'} / (1.0 * $subs);
      $dydt = $xs->{'ddty'} / (1.0 * $subs);
      print $fh "\\pgfpathcurveto{\n";
      $dg->write_point(($ox + ($odxdt / 3.0)), ($oy + ($odydt / 3.0)));
      print $fh "}{";
      $dg->write_point(($x - ($dxdt / 3.0)), ($y - ($dydt / 3.0)));
      print $fh "}{";
      $dg->write_point($x, $y);
      print $fh "}\n";
      $ox = $x; $oy = $y; $odxdt = $dxdt; $odydt = $dydt;
    }
    $t = 1.0;
    $xs->calculate($t);
    $x = $xs->{'x'}; $y = $xs->{'y'};
    $dxdt = $xs->{'ddtx'} / (1.0 * $subs);
    $dydt = $xs->{'ddty'} / (1.0 * $subs);
    print $fh "\\pgfpathcurveto{\n";
    $dg->write_point(($ox + ($odxdt / 3.0)), ($oy + ($odydt / 3.0)));
    print $fh "}{";
    $dg->write_point(($x - ($dxdt / 3.0)), ($y - ($dydt / 3.0)));
    print $fh "}{";
    $dg->write_point($x, $y);
    print $fh "}\n";
    $ox = $x; $oy = $y; $odxdt = $dxdt; $odydt = $dydt;
  }
  print $fh "\\pgfusepath{stroke}\n";
}



sub plot_to
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->plot_to(diagram)";
  } else {
    $self = shift; my $dg = shift;
    $self->finish();
    my $color = 'black';
    if(defined($self->{'color'})) {
      $color = $self->{'color'};
    }
    $dg->set_color($color);
    my $pt = $self->{'t'};
    my $dt = $self->{'d'};
    if($pt == 0) {
      if($dt == 0) {
        $self->plot_points_points($dg);
      } else {
        if($dt == 1) {
	  $self->plot_points_points($dg);
	} else {
	  if($dt == 2) {
	    $self->plot_param_points($dg);
	  } else {
	    if($dt == 3) {
	      $self->plot_points_points($dg);
	    }
	  }
	}
      }
    } else {
      if($pt == 1) {
        if($dt == 0) {
	  $self->plot_points_curve($dg);
	} else {
	  if($dt == 1) {
	    $self->plot_points_curve($dg);
	  } else {
	    if($dt == 2) {
	      $self->plot_param_curve($dg);
	    } else {
	      if($dt == 3) {
	        $self->plot_xspline_curve($dg);
	      }
	    }
	  }
	}
      } else {
        if($pt == 2) {
	  if($dt == 0) {
	    $self->plot_points_lines($dg);
	  } else {
	    if($dt == 1) {
	      $self->plot_points_lines($dg);
	    } else {
	      if($dt == 2) {
	        $self->plot_param_lines($dg);
	      } else {
	        if($dt == 3) {
		  $self->plot_points_lines($dg);
		}
	      }
	    }
	  }
	}
      }
    }
  }
  return $self;
}


sub set_curve
{
  my $self = undef;
  if($#_ < 0) {
    croak "Usage: \$plot->set_lines()";
  } else {
    $self = shift; $self->{'t'} = 1;
  }
  return $self;
}


sub set_lines
{
  my $self = undef;
  if($#_ < 0) {
    croak "Usage: \$plot->set_lines()";
  } else {
    $self = shift; $self->{'t'} = 2;
  }
  return $self;
}



sub set_dots
{
  my $self = undef;
  if($#_ < 0) {
    croak "Usage: \$plot->set_dots([dotstyle])";
  } else {
    $self = shift; $self->{'t'} = 0; $self->{'ds'} = 0;
    if($#_ >= 0) {
      my $t = shift;
      if(("$t" eq "0") || ("$t" eq "c") || ("$t" eq "circle")) {
        $self->{'ds'} = 0;
      }
      if(("$t" eq "1") || ("$t" eq "s") || ("$t" eq "square")) {
        $self->{'ds'} = 1;
      }
      if(("$t" eq "2") || ("$t" eq "d") || ("$t" eq "diamond")) {
        $self->{'ds'} = 2;
      }
      if(("$t" eq "3") || ("$t" eq "t") || ("$t" eq "triangle")) {
        $self->{'ds'} = 3;
      }
      if(("$t" eq "4") || ("$t" eq "cr") || ("$t" eq "crosshair")) {
        $self->{'ds'} = 4;
      }
      if(("$t" eq "5") || ("$t" eq "p") || ("$t" eq "pentagon")) {
        $self->{'ds'} = 5;
      }
      if($#_ >= 0) {
        $self->{'dsz'} = shift;
      }
    }
  }
  return $self;
}



sub debug
{
  my $self = undef;
  if($#_ >= 0) {
    $self = shift;
    my $msg = ""; my $i = 0;
    while($#_ > -1) {
      if($i) {
        $msg = "$msg " . shift;
      } else {
        $msg = shift;
      }
      $i++;
    }
    if($self->{'debug'}) {
      print "DEBUG $msg\n";
    }
  }
  return $self;
}



sub set_intervals
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_intervals(number)";
  } else {
    $self = shift;
    my $n = shift;
    if($n < 1) {
      croak "ERROR: At least one interval is needed!";
    } else {
      $self->{'i'} = $n;
    }
  }
  return $self;
}



sub set_color
{
  my $self = undef;
  if($#_ < 1) {
    croak "Usage: \$plot->set_color(color)";
  } else {
    $self = shift; $self->{'color'} = shift;
  }
  return $self;
}



sub set_xsplines_segments
{
  my $self = undef;
  if($#_ >= 1) {
    $self = shift; $self->{'xspline'} = shift;
  } else {
    croak "Usage: \$plot->set_xsplines_segments(num)";
  }
  return $self;
}



sub set_range
{
  my $self = undef;
  if($#_ >= 2) {
    $self = shift; my $min = shift; my $max = shift;
    $self->{'r'} = [ $min, $max ];
  } else {
    croak "Usage: \$plot->set_range(min, max)";
  }
  return $self;
}


1;

__END__

