go-hep.org/x/hep@v0.38.1/hplot/internal/talbot/ticks.go (about)

     1  // Copyright ©2020 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package talbot
     6  
     7  import (
     8  	"math"
     9  	"strconv"
    10  
    11  	"gonum.org/v1/gonum/floats/scalar"
    12  	"gonum.org/v1/plot"
    13  )
    14  
    15  // Ticks returns Ticks in the specified range.
    16  func Ticks(min, max float64, n int) []plot.Tick {
    17  	if max <= min {
    18  		panic("illegal range")
    19  	}
    20  
    21  	suggestedTicks := n
    22  
    23  	labels, step, q, mag := talbotLinHanrahan(min, max, suggestedTicks, withinData, nil, nil, nil)
    24  	majorDelta := step * math.Pow10(mag)
    25  	if q == 0 {
    26  		// Simple fall back was chosen, so
    27  		// majorDelta is the label distance.
    28  		majorDelta = labels[1] - labels[0]
    29  	}
    30  
    31  	// Choose a reasonable, but ad
    32  	// hoc formatting for labels.
    33  	fc := byte('f')
    34  	var off int
    35  	if mag < -1 || 6 < mag {
    36  		off = 1
    37  		fc = 'g'
    38  	}
    39  	if math.Trunc(q) != q {
    40  		off += 2
    41  	}
    42  	prec := minInt(6, maxInt(off, -mag))
    43  	var ticks []plot.Tick
    44  	for _, v := range labels {
    45  		ticks = append(ticks, plot.Tick{Value: v, Label: strconv.FormatFloat(v, fc, prec, 64)})
    46  	}
    47  	lastMajor := ticks[len(ticks)-1]
    48  
    49  	var minorDelta float64
    50  	// See talbotLinHanrahan for the values used here.
    51  	switch step {
    52  	case 1, 2.5:
    53  		minorDelta = majorDelta / 5
    54  	case 2, 3, 4, 5:
    55  		minorDelta = majorDelta / step
    56  	default:
    57  		if majorDelta/2 < dlamchP {
    58  			return ticks
    59  		}
    60  		minorDelta = majorDelta / 2
    61  	}
    62  
    63  	// Find the first minor tick not greater
    64  	// than the lowest data value.
    65  	var i float64
    66  	for labels[0]+(i-1)*minorDelta > min {
    67  		i--
    68  	}
    69  	// Add ticks at minorDelta intervals when
    70  	// they are not within minorDelta/2 of a
    71  	// labelled tick.
    72  	for {
    73  		val := labels[0] + i*minorDelta
    74  		if val > max {
    75  			break
    76  		}
    77  		found := false
    78  		for _, t := range ticks {
    79  			if math.Abs(t.Value-val) < minorDelta/2 {
    80  				found = true
    81  			}
    82  		}
    83  		if !found {
    84  			ticks = append(ticks, plot.Tick{Value: val})
    85  		}
    86  		i++
    87  	}
    88  
    89  	if last := &ticks[len(ticks)-1]; last.IsMinor() {
    90  		// check if we could elect it to "major"
    91  		delta := last.Value - lastMajor.Value
    92  		elect := scalar.EqualWithinAbs(delta, majorDelta, 1e-15)
    93  		if elect {
    94  			last.Label = strconv.FormatFloat(last.Value, fc, prec, 64)
    95  		}
    96  	}
    97  
    98  	return ticks
    99  }
   100  
   101  func minInt(a, b int) int {
   102  	if a < b {
   103  		return a
   104  	}
   105  	return b
   106  }
   107  
   108  func maxInt(a, b int) int {
   109  	if a > b {
   110  		return a
   111  	}
   112  	return b
   113  }