All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barrybecker4.math.cutpoints.CutPointGenerator.scala Maven / Gradle / Ivy

The newest version!
/* Copyright by Barry G. Becker, 2000-2018. Licensed under MIT License: http://www.opensource.org/licenses/MIT */
package com.barrybecker4.math.cutpoints

import java.text.DecimalFormat

import com.barrybecker4.math.{MathUtil, Range}
import com.barrybecker4.math.cutpoints.AbstractCutPointFinder.MIN_RANGE

/**
  * Calculates nicely rounded intervals for a specified range.
  * From an article by Paul Heckbert in Graphics Gems 1.
  * @param useTightLabeling whether or not to use tight labeling.
  * @param formatter method for formatting the label values.
  * @author Barry Becker
  */
class CutPointGenerator(val useTightLabeling: Boolean, val formatter: DecimalFormat) {

  setUseTightLabeling(useTightLabeling)
  /** If true, show the precise min/max values at the extreme cut points (tight), else loose labels */
  private var cutPointFinder: AbstractCutPointFinder = _

  def this() = {
    this(true, new DecimalFormat("###,###.##"))
  }

  def setUseTightLabeling(useTight: Boolean): Unit =
    cutPointFinder = if (useTight) new TightCutPointFinder else new LooseCutPointFinder

  /** Retrieve the cut point values.
    * If its a really small range include both min and max to avoid having just one label.
    * @param range       range to be divided into intervals.
    * @param maxNumTicks upper limit on number of cut points to return.
    * @return the cut points
    */
  def getCutPoints(range: Range, maxNumTicks: Int): Array[Double] = cutPointFinder.getCutPoints(range, maxNumTicks)

  /** Labels for the found cut points.
    * @param range    tickmark range.
    * @param maxTicks upper limit on the number of cuts.
    * @return cut point labels
    */
  def getCutPointLabels(range: Range, maxTicks: Int): Array[String] = {
    val cutPoints = cutPointFinder.getCutPoints(range, maxTicks)
    var maxFracDigits = getNumberOfFractionDigits(range, maxTicks)
    val useTight = cutPointFinder.isInstanceOf[TightCutPointFinder]
    val length = cutPoints.length
    val labels = Array.ofDim[String](length)

    for (i <- 0 until length) {
      // show a little more precision for the tight labels.
      maxFracDigits += (if (useTight && (i == 0 || i == length - 1)) 1 else 0)
      formatter.setMaximumFractionDigits(maxFracDigits)
      labels(i) = formatter.format(cutPoints(i))
    }
    labels
  }

  /** Determine the number of fractional digits to show in the nice numbered cut points.
    * @param range       the range to check.
    * @param maxNumTicks no more than this many cut points.
    * @return Recommended number of fractional digits to display. The cut points: eg. 0, 1, 2, etc.
    */
  private[cutpoints] def getNumberOfFractionDigits(range: Range, maxNumTicks: Int) = {
    var max1 = range.max
    if (range.getExtent <= MIN_RANGE) max1 = range.min + MIN_RANGE
    val extent = Rounder.roundUp(max1 - range.min)
    val d = Rounder.roundDown(extent / (maxNumTicks - 1))
    Math.max(-Math.floor(MathUtil.log10(d)), 0).toInt
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy