#  File: /projects/dhsvm/uvm/scripts/get_tickpoints.r
#
#  source("/projects/dhsvm/uvm/get_tickpoints.r")
#  source("get_tickpoints.r")
#
#  R script for developing methods to get pretty tickmark intervals
#  for first and second axes of a plot.  The axes are assumed to be be parallel,
#  e.g., the left and right vertical axes.  
#
#  Scott Waichler, 08/08/03.  
#  scott.waichler@pnl.gov
#  Battelle Pacific Northwest National Laboratory
#  "Serving the needs of the nation and humanity."
#

dotest <- F  # set to T for testing/debugging

# Function to get the tick mark values when you want to specify the the number of ticks.
# If number of ticks does not need to be fixed and you don't need to specify the minimum
# or maximum tick value, then consider using R's pretty() function instead, which will
# adjust the number of tick marks.
GET.FIRST.AXIS <- function (y, num.tick.marks, axis.min, axis.max) {
  # Input:  y=data, num.tick.marks=number of tick marks to appear on plot (one more than
  # number of intervals between ticks).
  # If miny is a number, than that is fixed as the minimum (lowest) tick mark;
  # otherwise, the lowest tick mark is computed below.  The same applies to the
  # maximum (highest) tick mark.  At least one of miny and maxy MUST be non-numeric
  # (and therefore computed below).
  #
  # The basic idea is to compute a nice, round interval such that num.tick.marks
  # times that interval covers the range of the data.  If either the minimum or maximum
  # tick mark is fixed, then the other end of the range is based on the computed interval
  # size and num.tick.marks.  If both miny and maxy are to be determined herein,
  # then the minimum tick mark is set to a pretty number equal to or less than the
  # minimum in the data, and the maximum tick mark floats in accordance with the
  # interval size and num.tick.marks.

  mult <- c(1,2,5,10)
  num.intervals <- num.tick.marks - 1
  
  data.min <- min(y)
  data.max <- max(y)
  
  d <- data.max - data.min
  e <- d/num.intervals
  b <- 10^(floor(log10(e)))

  if(is.numeric(axis.min)) { # minimum tick mark is specified with input
    for(m in mult) {
      u <- b * m
      upper.tick <- axis.min + (num.intervals * u)
      # the minimum basic unit that results in sufficient range is used
      if(upper.tick >= data.max) break
    }
    tick.marks <- round(c(axis.min, axis.min + (u * 1:num.intervals)), -1*floor(log10(e)))
  } else if (is.numeric(axis.max)) { # maximum tick mark is specified with input
    for(m in mult) {
      u <- b * m
      lower.tick <- axis.max - (num.intervals * u)
      # the minimum basic unit that results in sufficient range is used
      if(lower.tick <= data.min) break
    }
    tick.marks <- round(rev(c(axis.max, axis.max - (u * 1:num.intervals))), -1*floor(log10(e)))
    
  } else { # minimum tick mark is to be determined
    
    # find the number that is less than or equal to data.min, and is a multiple of b
    for(i in 1:10) { 
      axis.min <- round(data.min, -1*round(log10(e), 0) )
      # Following condition should always be true with i <= 2, but I allow up to 10 iterations to be sure
      if(axis.min > data.min) { 
        axis.min <- axis.min - b
      } else break
    }
    for(m in mult) {
      u <- b * m
      upper.tick <- axis.min + (num.intervals * u)
      # the minimum basic unit that results in sufficient range is used
      if(upper.tick >= data.max) break
    }
    tick.marks <- round(c(axis.min, axis.min + (u * 1:num.intervals)), -1*floor(log10(e)))
  }
  
  return(tick.marks)
} # end GET.FIRST.AXIS()


# Function to get the tick mark values on the second axis that are nice and round and
# will correspond to (be horizontal wit4h) the tick values on the first axis.
# The first axis tick marks can be set a priori, computed from pretty(),
# or computed from GET.FIRST.AXIS().
GET.SECOND.AXIS <- function (first.axis.tick.values, y, axis.min, axis.max) {
  # Input:  first.axis.tick.values = tick values on corresponding axis,
  # y = data for second axis.  The number of tick marks is the same as the first axis.
  # If miny is a number, than that is fixed as the minimum (lowest) tick mark;
  # otherwise, the lowest tick mark is computed below.  The same applies to the
  # maximum (highest) tick mark.  At least one of miny and maxy MUST be non-numeric
  # (and therefore computed below).
  #
  # The basic idea is to compute a nice, round interval such that the number of tick marks
  # times that interval covers the range of the data.  If either the minimum or maximum
  # tick mark is fixed, then the other end of the range is based on the computed interval
  # size and num.tick.marks.  If both miny and maxy are to be determined herein,
  # then the minimum tick mark is set to a pretty number equal to or less than the
  # minimum in the data, and the maximum tick mark floats in accordance with the
  # interval size and num.tick.marks.

  mult <- c(1,2,5,10)
  num.tick.marks <- length(first.axis.tick.values)
  num.intervals <- num.tick.marks - 1
  
  data.min <- min(y)
  data.max <- max(y)
  
  d <- data.max - data.min
  e <- d/num.intervals
  b <- 10^(floor(log10(e)))

  if(is.numeric(axis.min)) { # minimum tick mark is specified with input
    for(m in mult) {
      u <- b * m
      upper.tick <- axis.min + (num.intervals * u)
      # the minimum basic unit that results in sufficient range is used
      if(upper.tick >= data.max) break
    }
    tick.marks <- round(c(axis.min, axis.min + (u * 1:num.intervals)), -1*floor(log10(e)))
  } else if (is.numeric(axis.max)) { # maximum tick mark is specified with input
    for(m in mult) {
      u <- b * m
      lower.tick <- axis.max - (num.intervals * u)
      # the minimum basic unit that results in sufficient range is used
      if(lower.tick <= data.min) break
    }
    tick.marks <- round(rev(c(axis.max, axis.max - (u * 1:num.intervals))), -1*floor(log10(e)))
    
  } else { # minimum tick mark is to be determined
    
    # find the number that is less than or equal to data.min, and is a multiple of b
    for(i in 1:10) { 
      axis.min <- round(data.min, -1*round(log10(e), 0) )
      # Following condition should always be true with i <= 2, but I allow up to 10 iterations to be sure
      if(axis.min > data.min) { 
        axis.min <- axis.min - b
      } else break
    }
    for(m in mult) {
      u <- b * m
      upper.tick <- axis.min + (num.intervals * u)
      # the minimum basic unit that results in sufficient range is used
      if(upper.tick >= data.max) break
    }
    tick.marks <- round(c(axis.min, axis.min + (u * 1:num.intervals)), -1*floor(log10(e)))
  }
  
  return(tick.marks)
} # end GET.SECOND.AXIS()


if(dotest) {
  #y <- seq(0,10, by=1)
  #y <- seq(0,10, by=0.1)
  y <- seq(0,1.1, by=.1)
  #y <- seq(0,1, by=0.01)
  num.tick.marks <- 7

  axis.min <- 0
  axis.max<- "S"
  y1 <- GET.FIRST.AXIS(y, num.tick.marks, axis.min, axis.max)
  cat(y1, "\n")

  axis.min <- "S"
  axis.max<- 1.2
  y1 <- GET.FIRST.AXIS(y, num.tick.marks, axis.min, axis.max)
  cat(y1, "\n")

  y <- c(21, 23, 45, 78)
  axis.min <- "S"
  axis.max<- "S"
  y1 <- GET.FIRST.AXIS(y, num.tick.marks, axis.min, axis.max)
  cat(y1, "\n")

  y <- seq(0.1, 3.5, by=.1)
  axis.min <- "S"
  axis.max<- "S"
  y1 <- GET.FIRST.AXIS(y, num.tick.marks, axis.min, axis.max)
  cat(y1, "\n")

  y <- seq(11, 83, by=.1)
  axis.min <- "S"
  axis.max<- "S"
  y2 <- GET.SECOND.AXIS(y1, y, axis.min, axis.max)
  cat(y2, "\n")

  y <- seq(11, 83, by=.1)
  y1 <- pretty(seq(0.1, 3.5, by=.1), n = 5, min.n = 4)
  axis.min <- "S"
  axis.max<- "S"
  y2 <- GET.SECOND.AXIS(y1, y, axis.min, axis.max)
  cat(y2, "\n")
} # end dotest
