#!/usr/local/bin/perl5.6.0 -w
# -*- cperl -*-

# $Header: /nfs/pathsoft/repository/psu/genexec/perl/src/circular_diagram.pl,v 1.18 2002/07/03 08:51:05 kmr Exp $

# purpose: This script will generate a circular postscript diagram
# purpose: from any number of data files.

use strict;

use Bio::PSU::SeqFactory;
use Diagram::Circular;
use Diagram::Data;
use Pathogens::Artemis;

use Math::Complex;
use Carp;

my $page_width             = 72 * 8.25;
my $border_width           = 72 * 0.5;
my $page_height            = 72*11;
my $scale_label_separation = 10000;
my $scale_mark_separation  = 1000;
my $scale_mark_width       = 1;
my $scale_mark_height      = 1.2;
my $scale_font_size        = 3.0;
my $label_distance         = 6;
my $minimum_feature_width  = 1;
my $clockwise              = "yes";
my $end_gap                = 0;
my $outfile               = "";

sub usage
{
#    font_size       - the amount by which the font should be scaled (default:
#                      $font_size)

  die <<EOF;
usage $0 -h (for help)
   or $0 [-option=value ...] sequence_file
         [-gc <radius> <max_height> <window_size> <window_step>]
         [-gcdev <radius> <max_height> <window_size> <window_step>]
         data_file [-forward_only] <radius> <max_height>
         [data_file [-forward_only] <radius> <max_height>] ...

example:
   $0 -page_height=392 -label_distance=10 Cj.dna Cj.tab 80 5 -gcdev 60 8 1000 100
(this draws Cj.tab at radius 80 with maximum height of 5 and the gc deviation
plot at radius 60 with a maximum height of 8, window size of 1000 and window
step value of 100)

options:
  page_width      - the width of the page (default is 8.25 inches * 72 = 594)
  border_width    - the amount of space to leave at the edge of the page
                    (default is 36 = 0.5 inches)
  page_height     - the height of the page (default: 11 inches * 72 = 792)
  scale_font_size - the amount by which the font used to label the
                    scale should be scaled (default: $scale_font_size)
  label_distance  - the distance between the scale line/circle and the centre
                    of the scale labels
  scale_label_separation - the distance between the scale numbers (default
                    $scale_label_separation) (use 0 to disable)
  scale_mark_separation - the distance between the marks on the scale line
                    (default $scale_mark_separation) (use 0 to disable)
  scale_mark_width - the width of each scale mark (default $scale_mark_width)
  scale_mark_height - the height of each scale mark (default $scale_mark_height)
  minimum_feature_width - the minimum width (in bases) of each feature (default $minimum_feature_width)
  clockwise        - if yes the picture is drawn clockwise from north,
                     otherwise it is drawn anti-clockwise from east
                     (default $clockwise)
  end_gap          - the gap (in bases) between the last base of the sequence
                     and the first.  this option exists to allow non-circular
                     genomes to be drawn sensibly.  (default $end_gap)
  outfile         - the file to write the output to.  (default write to
                     screen)

arguments:
  <radius>         - after a data file name indicates where the features in
                     the data file should be drawn. 0 is the centre 100 is the
                     edge.
  <max_height>     - after <radius> indicates the maximum height to draw the
                     features in this data_file.  Use the same units as radius.
  <window_size>    - \

There are two types of data file - embl feature tables or the format described
below:

Each line after that should be a comment (starting with #) or a data
line with either 3 or 6 columns:

column 1:    start coordinate (between 1 and the value of the -divisions
             option)
column 2:    end coordinate (between 1 and the value of the -divisions option)
column 3:    distance from base line / height of feature measured in
             units from 0 (min) to 100 (max).  The actual height used
             is this value divided by the max_height given on the
             command line.  Negative values will cause the feature to go
             from the baseline towards the centre (rather than away
             from the centre)
columns 4-5: the red, green and blue values to use as the feature colour.  the
             values should be <= 1 and >= 0.  if the colour is omitted, black
             is used.
EOF
}

if (@ARGV == 0 || $ARGV[0] eq "-h") {
  usage ();
}

while ($ARGV[0] =~ /^-([\S=]*)=(\S*)/) {
  shift;
  my $var_name = $1;
  my $var_value = quotemeta $2;

  eval <<EOF;
no strict;

if (defined \$$var_name) {
  \$$var_name = "$var_value"
} else {
  die "$0: unknown option: $var_name\n";
}
EOF

  if (defined $@ && $@) {
    warn "\n$@\n";
    usage ();
  } else {
    next;
  }
}

if (lc $clockwise eq "yes" or lc $clockwise eq "y") {
  $clockwise = 1;
} else {
  $clockwise = 0;
}

if ($ARGV[0] =~ /^-(.+)$/) {
  print STDERR "$0: unknown option: -$1\n";
  usage ();
}

if (!@ARGV) {
  usage ();
}

if (defined $outfile and $outfile ne "") {
  print STDERR "trying to open $outfile for writing\n";

  open OUTFILE, ">$outfile";
  print STDERR "opened $outfile\n"
} else {
  open OUTFILE, ">&STDOUT";
}

my $max_width = $page_width - $border_width * 2;
my $half_width = $max_width / 2;
my $page_centre_x = $page_width / 2;
my $page_centre_y = $page_height / 2;
my $half_height = $page_height / 2;

my $seq_file = shift @ARGV;

my $seq_stream = Bio::PSU::SeqFactory->make(-file => $seq_file);

my $seq = $seq_stream->next_seq ();

if (!defined $seq) {
  die "couldn't read a sequence from $seq_file\n";
}

my @data = ();

my $arg;

while (defined ($arg = shift @ARGV)) {
  if (!@ARGV) {
    croak "$0: need a radius and a max_height argument after $arg\n";
  }

  my $forward_only = 0;

  if ($ARGV[0] =~ /^-(\S*)/) {
    if ($1 eq "forward_only") {
      shift;
      $forward_only = 1;
    } else {
      croak "unknown argument -$1\n";
    }
  }

  my $radius = shift;

  if ($radius !~ /^[\d\.]+$/) {
    croak "radius argument is not numeric: $radius\n";
  }

  if (!@ARGV) {
    croak "$0: need a max_height argument after $arg $radius\n";
  }

  my $max_height = shift;

  if ($max_height !~ /^[\d\.]+$/) {
    croak "max_height argument is not numeric: $max_height\n";
  }

  my $window_size;
  my $window_step;

  if ($arg eq "-gc" or $arg eq "-gcdev") {
    if (@ARGV >= 2) {
      $window_size = shift @ARGV;
      $window_step = shift @ARGV;
    } else {
      croak "$0: $arg flag needs four arguments:\n" .
            "     radius, max_height, window_size and, window_step\n";
    }
  }

  if ($arg eq "-gc") {
    push @data, Diagram::Data->make_gc_data ($seq, 0, $radius, $max_height,
                                             $window_size, $window_step);
    next;
  }

  if ($arg eq "-gcdev") {
    push @data, Diagram::Data->make_gc_data ($seq, 1, $radius, $max_height,
                                             $window_size, $window_step);
    next;
  }

  my $data_file = $arg;

  if (-z $data_file) {
    warn "warning: $data_file is empty (ignoring)\n";
    next;
  }

  push @data, Diagram::Data->read_file ($data_file, $radius, $max_height,
                                        $forward_only);
}

my $options =
{
 page_width             => $page_width,
 border_width           => $border_width,
 page_height            => $page_height,
 divisions              => $seq->length (),
 scale_label_separation => $scale_label_separation,
 scale_mark_separation  => $scale_mark_separation,
 scale_mark_width       => $scale_mark_width,
 scale_mark_height      => $scale_mark_height,
 scale_font_size        => $scale_font_size,
 label_distance         => $label_distance,
 minimum_feature_width  => $minimum_feature_width,
 clockwise              => $clockwise,
 end_gap                => $end_gap,
};

draw_picture \*OUTFILE, $options, @data;
