label.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package gofpdf
  2. // Adapted from Nice Numbers for Graph Labels by Paul Heckbert from "Graphics
  3. // Gems", Academic Press, 1990
  4. // Paul Heckbert 2 Dec 88
  5. // https://github.com/erich666/GraphicsGems
  6. // LICENSE
  7. // This code repository predates the concept of Open Source, and predates most
  8. // licenses along such lines. As such, the official license truly is:
  9. // EULA: The Graphics Gems code is copyright-protected. In other words, you
  10. // cannot claim the text of the code as your own and resell it. Using the code
  11. // is permitted in any program, product, or library, non-commercial or
  12. // commercial. Giving credit is not required, though is a nice gesture. The
  13. // code comes as-is, and if there are any flaws or problems with any Gems code,
  14. // nobody involved with Gems - authors, editors, publishers, or webmasters -
  15. // are to be held responsible. Basically, don't be a jerk, and remember that
  16. // anything free comes with no guarantee.
  17. import (
  18. "math"
  19. )
  20. // niceNum returns a "nice" number approximately equal to x. The number is
  21. // rounded if round is true, converted to its ceiling otherwise.
  22. func niceNum(val float64, round bool) float64 {
  23. var nf float64
  24. exp := int(math.Floor(math.Log10(val)))
  25. f := val / math.Pow10(exp)
  26. if round {
  27. switch {
  28. case f < 1.5:
  29. nf = 1
  30. case f < 3.0:
  31. nf = 2
  32. case f < 7.0:
  33. nf = 5
  34. default:
  35. nf = 10
  36. }
  37. } else {
  38. switch {
  39. case f <= 1:
  40. nf = 1
  41. case f <= 2.0:
  42. nf = 2
  43. case f <= 5.0:
  44. nf = 5
  45. default:
  46. nf = 10
  47. }
  48. }
  49. return nf * math.Pow10(exp)
  50. }
  51. // TickmarkPrecision returns an appropriate precision value for label
  52. // formatting.
  53. func TickmarkPrecision(div float64) int {
  54. return int(math.Max(-math.Floor(math.Log10(div)), 0))
  55. }
  56. // Tickmarks returns a slice of tickmarks appropriate for a chart axis and an
  57. // appropriate precision for formatting purposes. The values min and max will
  58. // be contained within the tickmark range.
  59. func Tickmarks(min, max float64) (list []float64, precision int) {
  60. if max > min {
  61. spread := niceNum(max-min, false)
  62. d := niceNum((spread / 4), true)
  63. graphMin := math.Floor(min/d) * d
  64. graphMax := math.Ceil(max/d) * d
  65. precision = TickmarkPrecision(d)
  66. for x := graphMin; x < graphMax+0.5*d; x += d {
  67. list = append(list, x)
  68. }
  69. }
  70. return
  71. }