#! /usr/local/bin/perl
################################################################################
#
#  Image Representations of Data Plots
#
#  copyright (c)
#    Fritz Lipmann Institute Jena, CF Bioinformatics, 2018
#    Fritz Lipmann Institute Jena, Genome Analysis Group, 2006, 2013
#    Karol Szafranski at UPenn Philadelphia, Center for Bioinformatics, 2004
#    Institute of Molecular Biotechnology Jena, Dept. Genome Analysis, 1999-2004
#  author
#    Karol Szafranski, karol.szafranski@leibniz-fli.de
#
################################################################################
#
#  DESCRIPTION
#
# - See function &usage for description of command line syntax
#
# - each function comes along with a description at the beginning of the code
#   block
#
#  DEBUG, CHANGES, ADDITIONS
#
# - see block or functions
#   sub ProgPlot {  # smoothening parameter setup
#
################################################################################
#
#  FUNCTIONS, DATA
#
# - MAIN
#   %GlobStore
#   $ProgFile,$ProgFstump
#   %ProgParam
#   $ProgMode,%ProgOpt,@ProgArg
#
# - usage help, command line arguments, basic I/O
#   &usage
#   &AddSwitch
#   &PrepOstump
#
# - batch calculation
#   &ProgFunc
#
# - process table data to graph
#   &ProgPlot
#   &ProgFeature
#
# - process plot data structures to graph
#   &ProgPlotStruct
#
################################################################################


# global constants and variables
# also used by modules as a unique global data anchor
our %GlobStore;

# include path(s), includes
use strict; #use warnings;  # OK 20060316
use Cwd qw(realpath);
BEGIN {
  unshift @INC, grep{$_} split(/:+/,$ENV{KPERLPATH}||$ENV{PERLPATH}||'');
  my ($mypath) = realpath(__FILE__)=~m/(^.*)\//;
  push @INC, $mypath;
}
use MainLib::Data;
use MainLib::Cmdline qw(&GetoptsNArgs &QueryConfirm);
use MainLib::Path;
use MainLib::File qw(&Unbuffer);
use MainLib::Misc;
use Math::kCalc;
use Math::Range;
use Math::Plot2D;
use Math::PlotImg;
use database::Table;
use database::DbPlain qw(&PlainToTable);


# script ID
# - program name as specified on the command line
our $ProgFile = ( split('/',__FILE__) )[-1];
our $ProgFstump=$ProgFile; $ProgFstump=~s/\.\w{1,4}$//;

# global constants (esp. default values)
our %ProgParam;
$ProgParam{default}{ProgMode} = 'plot';
$ProgParam{default}{OutImgWidth} = 640;
$ProgParam{default}{OutImgRelhigh} = 0.5;

# working desk
$ProgParam{store} = undef;


# manage I/O #####################################

# organiz I/O handles
&Unbuffer();


# command line interface #########################
# NOTE:
# - &GetoptsNArgs (via &AddSwitch) modifies global variables:
#   $ProgMode @ProgArg %ProgOpt
# - @ProgArg may be pre-filled in &GetoptsNArgs via &AddSwitch (option -fofn)

# arguments, switches, default subprogram
our $ProgMode = undef;
our @ProgArg = ();
our %ProgOpt = ();
@ProgArg = &GetoptsNArgs();
$ProgMode ||= $ProgParam{default}{ProgMode};


# work flow manifold #############################

# chain to program mode without input argument(s)
if (0) { }
elsif (!@ARGV or $ProgMode=~m/^h(elp)?$/i) { &usage() }

# ensure input argument(s)
unless (@ProgArg) {
  die "ERROR: input arguments missing\n";
}

# chain to program mode (with input argument(s))
my $arg;
if (0) { }
elsif ($ProgMode =~ m/^combine$/i) {  # old-fashioned program mode
  foreach $arg (@ProgArg) {
    unless ( $arg eq '-' or -s &PathExpand($arg) ) {
      printf STDERR "WARNING: input file %s does not exist or has zero length\n", $arg||"''";
    }
  }
  &ProgPlotStruct (@ProgArg);
}
elsif ($ProgMode =~ m/^FeatureMap$/i) {
  foreach $arg (@ProgArg) {
    unless ( $arg eq '-' or -s &PathExpand($arg) ) {
      printf STDERR "WARNING: input file %s does not exist or has zero length\n", $arg||"''";
    }
  }
  &ProgFeature ($ProgArg[0]);
}
elsif ($ProgMode =~ m/^func$/i) {
  &ProgFunc (@ProgArg);
}
elsif ($ProgMode =~ m/^plot$/i) {
  foreach $arg (@ProgArg) {
    unless ( $arg eq '-' or -s &PathExpand($arg) ) {
      printf STDERR "WARNING: input file %s does not exist or has zero length\n", $arg||"''";
    }
  }
  &ProgPlot ($ProgArg[0]);
}
elsif ($ProgMode =~ m/^plotd$/i) {
  foreach $arg (@ProgArg) {
    unless ( $arg eq '-' or -s &PathExpand($arg) ) {
      printf STDERR "WARNING: input file %s does not exist or has zero length\n", $arg||"''";
    }
  }
  &ProgPlotStruct (@ProgArg);
}
else {
  die "ERROR: unknown program mode or switch '$ProgMode'\n";
}

# exit script successfully
# cf. END blocks!
exit 0;


################################################################################
# usage help, command line arguments, basic I/O
################################################################################


sub usage {
  print "\n";
  print <<END_USAGE;
DESCRIPTION
 $ProgFile is desired for sequencing result folder and file handling.

COMMAND LINE SYNTAX
 $ProgFile  -<ModeSwitch> [-<OptionalSwitch> ...] <Arg1> [<Arg2> ...]

Arguments
---------
 See function descriptions.

path arguments:
 Path "-" resolves to STDIN or STDOUT, depending on the context.

ModeSwitch (case-insensitive)
-----------------------------
<none>            default ModeSwitch -$ProgParam{default}{ProgMode} if program arguments are given.
                  Otherwise like ModeSwitch -help.
-FeatureMap       create map of features from tabular data
                  *** building place ***
                  Arg1        path of data table file
-func             create table of data pixels according to specified function
                  Arg1        function, stated in single quotes, use \$x
                              to refer to function variable.
                  Arg2        range and step size for x values, syntax:
                              'xoff..xend,xstep'
-h(elp)           output command line syntax description and exit
-plot             create graphical plot from tabular data
                  Arg1        path of data table file containing linies with
                              pairs of x/y values.
                  -WinSize    smoothen sample plot in image
-plotd            create graphical plot from given graph data structure
                  Arg1+       path(s) of data file(s)
                  -WinSize    smoothen sample plot in image
                  --update    update graph data structure

OptionalSwitch (case-insensitive)
switch argument types: B:=boolean, F:=floating point/scientific, N:=integer,
S:=string, X:=varying type.

-debug(=N)        print debug protocol to STDERR (sometimes STDOUT). Keep
                  temporary files.
                  N           debug depth value
-OutDir=S         directory for multi-file output. This switch overrides any
                  directory statement provided with switch -OutStump.
-OutImg=S         file path for image output
-OutImgRelhigh=F  relative height of image in relation to its width,
                  default: $ProgParam{default}{OutImgRelhigh}.
-OutImgTransp     turn image background transparent
-OutImgWidth=N    image pixel width (plot data field), default: $ProgParam{default}{OutImgWidth}
-OutStump=S       path stump for multi-file output. A default is derived from
                  input file names in most cases.
-RangeX=F1..(F2)  x value range
-RangeY=F1..(F2)  y value range
-ReprColor=S      data pixel representation color. RGB hex code or one of pre-
                  defined: green, red etc.
-ReprSizeData=F   data pixel representation size (data scale)
-ReprSizePixel=N  data pixel representation size (pixel scale)
-ReprType=S       data pixel representation type, one of: circle, column, line,
                  pixel (default), plus.
-WinSize=N|F      specify window size for smoothening etc.
--*               function-specific switches. See the descriptions there.
                  Case-sensitive!

Environment Variables
---------------------
 \$PERLPATH        primary search path for Perl package look-up
 \$TEMPPATH        directory for storage of temporary files, default /tmp

Temporary Files
---------------
 The program may generate temporary files. These will be placed either in a
 directory specified by \$ENV{TEMPPATH} or in /tmp.
END_USAGE
  print "\n";
  exit 0;
}


# add program switches to global table (hash)
#
# INTERFACE
# - argument 1:  switch argument without leading '-'
#
# - global data:
#   $ProgMode
#   %ProgOpt  switch data which gets processed here
#
# DESCRIPTION
# - this function gets called by &MainLib::Misc::GetoptsNArgs
# - switch arguments are tested for validity. Arguments are parsed with highest
#   possible tolerance. This way, syntax errors can reported in accordance to
#   the actual switch, rather than reporting ANY syntax error.
#
sub AddSwitch {
  my $switch = shift;
  my ($SwitchArg);

  # optional switches
  if ($switch =~ m/^debug(=(\d+))?$/i) {
    $ProgOpt{-debug} = defined($2) ? int($2) : 1;
    return;
  }
  if ($switch =~ m/^OutDir=(.+)$/i) {
    $ProgOpt{-OutDir} = &PathExpand ($1);
    unless (-d $ProgOpt{-OutDir}) {
      if (-e $ProgOpt{-OutDir}) {
        die sprintf "ERROR: output destination exists, but is not a directory: %s\n", $ProgOpt{-OutDir}||"''";
      }
      
      # this dialogue will also appear if there's no output do be done to any
      # file/directory
      else {
        if (int(grep{ $_ eq '-' }@ARGV)) {
          printf STDERR "creating non-existent output directory (skip dialogue in STDIN input mode)\n";
          mkdir ($ProgOpt{-OutDir});
        } else {
          printf STDERR "output directory does not exist, create?";
          if (&QueryConfirm()) {
            mkdir ($ProgOpt{-OutDir});
          } else { exit 1 }
        }
      }
    }
    return;
  }
  if ($switch =~ m/^OutImg(=(.+))?$/i) {
    $ProgOpt{-OutImg} = $2 ? &PathExpand($2) : '';
    return;
  }
  if ($switch =~ m/^OutImgRelhigh=([0-9\.eE+-]+)$/i) {
    $ProgOpt{-OutImg} ||= '';
    $ProgOpt{-OutImgRelhigh} = $1;
    return;
  }
  if ($switch =~ m/^OutImgTransp(ar)?$/i) {
    $ProgOpt{-OutImg} ||= '';
    $ProgOpt{-OutImgTransp} = 1;
    return;
  }
  if ($switch =~ m/^OutImgWidth=(\d+)$/i) {
    $ProgOpt{-OutImg} ||= '';
    $ProgOpt{-OutImgWidth} = $1;
    return;
  }
  if ($switch =~ m/^OutSt[au]mp=(.+)$/i) {
    $ProgOpt{-OutStump} = &PathExpand ($1);
    return;
  }
  if ($switch =~ m/^Range([XY])=(.+)$/i) {
    $ProgOpt{-debug} and $Math::Range::LibGlob{switch}{-debug} = 1;
    $SwitchArg = '-Range'.uc($1);
    unless ($ProgOpt{$SwitchArg} = Math::Range->new_parsed($2)) {
      die "ERROR: invalid range argument: $2\n";
    }
    return;
  }
  if ($switch =~ m/^ReprSizeData=([\d.eE+-]+)$/i) {
    $ProgOpt{-ReprSizeData} = $1;
    return;
  }
  if ($switch =~ m/^ReprColor=(\S+)$/i) {
    $ProgOpt{-ReprColor} = $1;
    return;
  }
  if ($switch =~ m/^ReprSizePixel=(\d+)$/i) {
    $ProgOpt{-ReprSizePixel} = $1;
    return;
  }
  if ($switch =~ m/^ReprType=(.+)$/i) {
    $ProgOpt{-ReprType} = $1;
    return;
  }
  if ($switch =~ m/^WinSize=([\d.eE+-]+)$/i) {
    $ProgOpt{-WinSize} = $1;
    return;
  }
  if ($switch =~ m/^(?:var=|-)(\w+)[,=](.+)$/i) {
    $ProgOpt{-var}{$1} = $2;
    return;
  }

  # program mode switches
  if (defined $ProgMode) {
    die sprintf "ERROR: multiple specification of program mode or unknown switch, %s and %s\n",
      '-'.($ProgMode||"''"), '-'.($switch||"''");
  }
  else {
    $ProgMode = $switch;
    return;
  }
}


# work out output path base from source file and/or switches
#
# INTERFACE
# - global options:
#   -debug       [STD]
#   -OutDir      [STD]
#   -OutStump    [STD]
# - return val:  output path base
#
sub PrepOstump {
  my ($debug);
  my ($PathStamp, $PathRef);

  # function parameters
  $debug = $ProgOpt{-debug};

  # prepare output path base
  # 1st: $ProgOpt{-OutStump}
  # 2nd: $ProgArg[0]
  unless ($PathStamp = $ProgOpt{-OutStump}) {
    $PathRef = $ProgArg[0];
    if ($PathRef eq '-') { $PathRef = 'stdin'; }
    $PathStamp = &PathChgSuffix (&PathExpand($PathRef), '', -last=>1);
  }

  # determine effective directory
  $ProgOpt{-OutDir} and $PathStamp =
    $ProgOpt{-OutDir} .'/'. ${ &PathSplit($PathStamp) }{name};

  # final refinement: expand to nice, rooted path
  $PathStamp = &PathExpand ($PathStamp);

  # return path
  return $PathStamp;
}


################################################################################
# batch calculation
################################################################################


# do batch calculation
#
# INTERFACE
# - argument 1: function, stated in single quotes, use $x
#               to highlight variable
# - argument 2: range and step size for x values, syntax:
#               'xoff..xend,xstep'
#
# - global options:
#   -debug      [STD]. Resolve function argument and exit
#
# DESCRIPTION
# - modifications done on function argument:
#   constants: e, pi
#   symbols:
#     [] => ()
#     ^  => **    [raise to the power]
#
sub ProgFunc {
  my ($ArgFunc, $ArgRange) = @_;
  my ($debug, $dbg2);
  my (%range, $x, $ValY);

  # function parameters
  $debug = $ProgOpt{-debug};
  $dbg2  = $debug ? $debug-1 : undef;

  # process function and range arguments
  $ArgFunc =~ s/\be\b/$const{euler}/eg;
  $ArgFunc =~ s/\bpi\b/$const{pi}/eg;
  $ArgFunc =~ s/\^/**/g;
  $ArgFunc =~ tr/[]/()/;
  $debug and printf STDERR "%s. evaluating function:\n  %s\n", &MySub, $ArgFunc;
  if ($ArgRange =~ m/^(.+)\.\.(.+),(.+)$/) {
    %range = (
      offset => $1,
      end    => $2,
      step   => $3,
      );
  }
  unless (%range and $range{step} > 0) {
    die sprintf "%s. ERROR: unable to resolve range argument: %s\n", &MySub, $ArgRange;
  }

  # x value loop
  for ($x=$range{offset}; $x<=$range{end}; $x+=$range{step}) {

    # calculate y from x
    $ValY = eval $ArgFunc;

    # output x/y value pair
    printf "%s\t%s\n", $x, $ValY;
  }
}


################################################################################
# process table data to graph
################################################################################


# prepare plot graph for a given data table
#
# INTERFACE
# - argument 1: path of table data file
#
# - global options:
#   -debug      [STD]
#   -WinSize    for smoothening
#
# DEBUG, CHANGES, ADDITIONS
# - Parameters for smoothening, i.e. $img{StepNum} > $img{StepSize}
#   are based on image reolution, not data resolution. If data
#   reolution is much higher than image resolution, then $img{StepSize}
#   may become larger than the actual smoothening window size
#   $ProgOpt{-WinSize} > $pPlot->SmoothPlot (..., -window=>...).
#   This will cause apparent data loss in the cumulative sum; i.e.
#   the cumulation will not end in 100% but a smaller value.
#
sub ProgPlot {
  my ($PathTab) = @_;
  my $debug = $ProgOpt{-debug};
  my $dbg2  = $debug ? $debug-1 : undef;
  my %img;
  $img{width} = $ProgOpt{-OutImgWidth} || $ProgParam{default}{OutImgWidth};
  $img{RelHigh} = $ProgOpt{-OutImgRelhigh} || $ProgParam{default}{OutImgRelhigh};

  # read data from table file
  my ($pData,$pPlot,$pDataG);
  unless ($pData=&PlainToTable($PathTab,-TabType=>'AA',-comments=>1) and int(@$pData)) {
    die sprintf "%s. ERROR: unable to find data in file %s\n", &MySub, $PathTab;
  }
  # refine data
  if ($ProgOpt{-WinSize}) {
    $img{StepNum} = $img{width} * 0.5;
    $img{StepSize} = &Min ($ProgOpt{-WinSize},
      ($$pData[-1][0] - $$pData[0][0]) / $img{StepNum});
    $pPlot = Math::Plot2D->new($pData);
    $pDataG = $pPlot->SmoothPlot ($img{StepSize},
      -window=>$ProgOpt{-WinSize}, -debug=>$dbg2);
  } else {
    $pDataG = $pData;
  }

  # prepare graph of distribution function
  my %graph = (
    BgTranspar => $ProgOpt{-OutImgTransp},
    plot => [
      { DimPixel  => { x=>$img{width} },
        HeightRel => $img{RelHigh},
        DataType  => 'AA',
        data      => $pDataG,
        ReprType  => $ProgOpt{-ReprType} || 'line',
#          (@$pData==3 and lc($DataCol[2]) eq 'sd') ?
#       (ReprSD    => 'line') : (),
        ReprColor => $ProgOpt{-ReprColor},
        ReprSizeData => $ProgOpt{-ReprSizeData},
        ReprSizePixel => $ProgOpt{-ReprSizePixel},
        DataRange => {
          x => $ProgOpt{-RangeX} ? $ProgOpt{-RangeX} : undef,
          y => $ProgOpt{-RangeY} ? $ProgOpt{-RangeY} : undef,
          },
      },
      ],
    scale => [
      { location => 'x',
        PlotNum  => 0,
      },
      { location => 'left',
        PlotNum  => 0,
      },
      { location => 'right',
        PlotNum  => 0,
      },
      ],
    );
  my %path;
  $path{data} = &PrepOstump() . '_plot.dat';
  $path{datahdl} = FileHandle->new($path{data},'w');
  &DataPrint (\%graph, -handle=>$path{datahdl}, -NoAddr=>1);
  printf "plot graph data saved to %s\n", $path{data}||"''";
  $path{img} = $ProgOpt{-OutImg}||&PrepOstump() .'_plot.png';
  if (&Graph (\%graph,-save=>$path{img},-debug=>$dbg2)) {
    printf "plot graph image saved to %s\n", $path{img}||"''";
  } else {
    die "ERROR: failed to create plot graph image, path $path{img}\n";
  }
}


# create map of features from tabular data
# *** building place ***
#
# INTERFACE
# - argument 1: data file
#
# - global options:
#   -debug      [STD]
#
sub ProgFeature {
  my ($FileTab) = @_;
  my ($debug, $dbg2);
  my ($pTab, $FeatCurr, %ResultMap);
  my (%graph, $PathImg);

  # function parameters
  $debug = $ProgOpt{-debug};
  $dbg2  = $debug ? $debug-1 : undef;
  $pTab = &PlainToTable ($FileTab, -TabType=>'AH', -comments=>1, -debug=>$dbg2);
  $debug and printf STDERR "%s. %d features in data file %s\n", &MySub,
    int @$pTab, $FileTab;

  ##############################################################################
  # process features

  # loop over all features
  # - build data structure
  foreach $FeatCurr (@$pTab) {
    $debug and printf STDERR "%s. feature: pos %d, orient %s, feature %s\n", &MySub,
      $$FeatCurr{pos}, $$FeatCurr{orient}, $$FeatCurr{feature};
    push @{$ResultMap{pos}}, $$FeatCurr{pos};
    push @{$ResultMap{label}}, 
      (($$FeatCurr{orient} eq '-') ? '<' : '>') .' '.
      join (' ', $$FeatCurr{feature}, $$FeatCurr{comment});
  }
  $debug and printf STDERR "%s. %d features in map\n", &MySub,
    int @{$ResultMap{pos}};
  $debug and printf STDERR "%s. map min %s, max %s\n", &MySub,
    &Min (@{$ResultMap{pos}}), &Max (@{$ResultMap{pos}});

  ##############################################################################
  # prepare map image

  %graph = (
    BgTranspar => $ProgOpt{-OutImgTransp},
    plot => [
        { HeightRel => 0,
          DimPixel  => { x=>$ProgOpt{-OutImgWidth}||$ProgParam{default}{OutImgWidth}, y=>0 },
          DataType  => 'MapPos',
          data      => { x=>$ResultMap{pos} },
          DataRange => {
            x => $ProgOpt{-RangeX} ?
                 $ProgOpt{-RangeX} : [ &Min (@{$ResultMap{pos}}), &Max (@{$ResultMap{pos}}) ],
            y => $ProgOpt{-RangeY} ?
                 $ProgOpt{-RangeY} : [ ],
            },
        },
      ],
    scale => [
        { PlotNum  => 0,
          location => 'bottom',
          color    => 'black',
        },
        { PlotNum  => 0,
          location => 'top',
          hasLine  => 0,
          color    => 'black',
          map      => [
            { data => { pos=>$ResultMap{pos}, label=>$ResultMap{label} } },
            ],
        },
      ],
    );
  $PathImg = $ProgOpt{-OutImg}||&PrepOstump() .'_feature.png';
  unless (&Graph (\%graph,-save=>$PathImg,-debug=>$dbg2)) {
    printf STDERR "ERROR: unable to save image to %s\n", $PathImg|"''";
    undef $PathImg;
  }
}


################################################################################
# process plot data structures to graph
################################################################################


# prepare graphical plot from given graph data structure
#
# INTERFACE
# - argument 1+: path(s) of graph data structures
#
sub ProgPlotStruct {

  # function constants
  my @BorderWinFunc = (\&Min,\&Max);

  # function parameters
  my (@DataSource) = @_;
  my $debug = $ProgOpt{-debug};
  my $dbg2  = $debug ? $debug-1 : undef;

  # loop over data structure files
  my ($pGraphMst,$pGraph,%BorderFin);
  foreach my $PathSrc (@DataSource) {
    unless ($pGraph = &DataRead($PathSrc,-debug=>$dbg2)) {
      printf STDERR "ERROR: unable to load plot graph data structure from file %s\n", $PathSrc||"''";
      next;
    }
    $ProgOpt{-var}{update} and &PlotStructUpdate ($pGraph);
    $pGraphMst ||= $pGraph;

    # loop over plot data structures
    my $CtPlot = 0;
    foreach my $pPlot (@{$$pGraph{plot}}) {

      # find extremes of value range custom definitions
      # - do not inspect secondary plots in master data structure that have
      #   their own scale binding
      if (
        $$pPlot{DataRange} and ($pGraph ne $pGraphMst or !$CtPlot
          or !int (grep{ $_->{PlotNum}==$CtPlot } map{@{$_||[]}} $$pGraphMst{scale}) )
      ) {
        foreach my $ItDim (qw(x y)) {
          foreach my $ItBound (0,1) {
            my @bdary = (grep{ defined($_) }
              $BorderFin{$ItDim}[$ItBound], $$pPlot{DataRange}{$ItDim}[$ItBound]);
            if(@bdary){
              $BorderFin{$ItDim}[$ItBound] = &{$BorderWinFunc[$ItBound]} (@bdary);
            }
          }
        }
      }

      # eventually smoothen data
      if ($ProgOpt{-WinSize}) {
        my %step;
        if ($$pPlot{DataType} ne 'AA') {
          $$pPlot{data} = &TableConvert ($$pPlot{DataType}||'HCA', 'AA', $$pPlot{data});
          $$pPlot{DataType} = 'AA';
        }
        $step{width} = $ProgOpt{-OutImgWidth}
          || $$pGraphMst{plot}[0]{DimPixel}{x} || $$pPlot{DimPixel}{x}
          || $ProgParam{default}{OutImgWidth};
        $step{num} = $step{width} * 0.5;
        $step{size} = &Min ($ProgOpt{-WinSize},
          ($$pPlot{data}[-1][0] - $$pPlot{data}[0][0]) / $step{num});
        my $pPlotObj = Math::Plot2D->new($$pPlot{data});
        $$pPlot{data} = $pPlotObj->SmoothPlot ($step{size},
          -window=>$ProgOpt{-WinSize}, -debug=>$dbg2);
      }

      # update plot data node
      $$pPlot{SrcFile}  = $PathSrc;
      $$pPlot{SrcAsNum} = $CtPlot ++;

      # enter plot from secondary source file
      # data range definitions:
      # - plot sets missing DataRange definition will get DataRangeRef
      if ($pGraph ne $pGraphMst) {
        push @{$$pGraphMst{plot}}, {
            (!$$pPlot{DataRange}) ?
         (DataRangeRef => 0) : (),
            (!exists($$pPlot{DataRangeRef}) and $$pPlot{DataRange} and %{$$pPlot{DataRange}}) ?
         (DataRange    => $$pPlot{DataRange}) : (),
          DataType     => $$pPlot{DataType}||'HCA',
          data         => $$pPlot{data},
            (exists($$pPlot{ReprColor}) and defined($$pPlot{ReprColor})) ?
         (ReprColor    => $$pPlot{ReprColor}) : (),
            (exists($$pPlot{ReprSizeData}) and defined($$pPlot{ReprSizeData})) ?
         (ReprSizeData => $$pPlot{ReprSizeData}) : (),
            (exists($$pPlot{ReprSizePixel}) and defined($$pPlot{ReprSizePixel})) ?
         (ReprSizePixel=> $$pPlot{ReprSizePixel}) : (),
          ReprType     => $ProgOpt{-ReprType} || $$pPlot{ReprType},
          };
      }
    }
  }

  # dimension set by command line options -Range*
  # to be set in 1st plot set (possibly referenced by following plot sets)
  foreach my $ItDim ('X', 'Y') {
    $ProgOpt{'-Range'.uc($ItDim)} or next;
    foreach my $ItBound (0, 1) {
      if (defined $ProgOpt{'-Range'.uc($ItDim)}[$ItBound]) {
        $BorderFin{lc($ItDim)}[$ItBound] = $ProgOpt{'-Range'.uc($ItDim)}[$ItBound];
        for (my $CtI=0; $CtI<@{$$pGraphMst{plot}}; ++$CtI) {
          $$pGraphMst{plot}[$CtI]{DataRange}{lc($ItDim)}[$ItBound]
            = $ProgOpt{'-Range'.uc($ItDim)}[$ItBound];
        }
      }
    }
  }

  # final assignments
  if (exists($ProgOpt{-OutImgTransp})) {
  $$pGraphMst{BgTranspar} = $ProgOpt{-OutImgTransp};
  }
  delete $$pGraphMst{plot}[0]{DataRangeRef};
  if (exists($ProgOpt{-OutImgWidth})) {
  $$pGraphMst{plot}[0]{DimPixel}{x} = $ProgOpt{-OutImgWidth};
  }
  if (exists($ProgOpt{-OutImgRelhigh})) {
  $$pGraphMst{plot}[0]{HeightRel} = $ProgOpt{-OutImgRelhigh};
  }
  $$pGraphMst{plot}[0]{DataRange} = { %BorderFin };
  # assign color flute
  # ...

  # save image data, save image
  my %path;
  $path{substamp} = (@{$$pGraphMst{plot}}>1) ? '_combine' : '';
  $path{img} = $ProgOpt{-OutImg};
  $path{img} ||= ($ProgOpt{-OutStump} || (&PrepOstump().$path{substamp}) ) .'.png';
  $path{data} = &PathChgSuffix ($path{img}, '_img.dat', -last=>1);
  $path{datahdl} = FileHandle->new($path{data},'w');
  &DataPrint ($pGraphMst, -handle=>$path{datahdl}, -NoAddr=>1);
  printf "plot data saved to %s\n", $path{data}||"''";
  if (&Graph ($pGraphMst,-save=>$path{img},-debug=>$dbg2)) {
    printf "plot graph saved to %s\n", $path{img}||"''";
  } else {
    die sprintf "%s. ERROR: failed to create plot graph, path $path{img}\n", &MySub;
  }
}
# $Id: Plot.pl,v 1.36 2018/06/05 18:02:56 szafrans Exp $
