func (LogTicks) Ticks(min, max float64) []Tick {
	if min <= 0 || max <= 0 {
		panic("Values must be greater than 0 for a log scale.")
	}
	val := math.Pow10(int(math.Log10(min)))
	max = math.Pow10(int(math.Ceil(math.Log10(max))))
	var ticks []Tick
	for val < max {
		for i := 1; i < 10; i++ {
			if i == 1 {
				ticks = append(ticks, Tick{Value: val, Label: formatFloatTick(val, -1)})
			}
			ticks = append(ticks, Tick{Value: val * float64(i)})
		}
		val *= 10
	}
	ticks = append(ticks, Tick{Value: val, Label: formatFloatTick(val, -1)})
	return ticks
}
Ticks returns Ticks in a specified range
package plot
import (
	"image/color"
	"math"
	"strconv"
	"time"
	"gonum.org/v1/plot/vg"
	"gonum.org/v1/plot/vg/draw"
)
type Ticker interface {
	Ticks(min, max float64) []Tick
}
type Normalizer interface {
	Normalize(min, max, x float64) float64
}
type Axis struct {
	Min, Max float64
	Label struct {
		Text string
		draw.TextStyle
	}
	draw.LineStyle
	Padding vg.Length
	Tick struct {
		Label draw.TextStyle
		draw.LineStyle
		Length vg.Length
		Marker Ticker
	}
	Scale Normalizer
}
func makeAxis(orientation bool) (Axis, error) {
	labelFont, err := vg.MakeFont(DefaultFont, vg.Points(12))
	if err != nil {
		return Axis{}, err
	}
	tickFont, err := vg.MakeFont(DefaultFont, vg.Points(10))
	if err != nil {
		return Axis{}, err
	}
	a := Axis{
		Min: math.Inf(+1),
		Max: math.Inf(-1),
		LineStyle: draw.LineStyle{
			Color: color.Black,
			Width: vg.Points(0.5),
		},
		Padding: vg.Points(5),
		Scale:   LinearScale{},
	}
	a.Label.TextStyle = draw.TextStyle{
		Color:  color.Black,
		Font:   labelFont,
		XAlign: draw.XCenter,
		YAlign: draw.YBottom,
	}
	var xalign, yalign = draw.XCenter, draw.YTop
	if orientation == vertical {
		xalign, yalign = draw.XRight, draw.YCenter
	}
	a.Tick.Label = draw.TextStyle{
		Color:  color.Black,
		Font:   tickFont,
		XAlign: xalign,
		YAlign: yalign,
	}
	a.Tick.LineStyle = draw.LineStyle{
		Color: color.Black,
		Width: vg.Points(0.5),
	}
	a.Tick.Length = vg.Points(8)
	a.Tick.Marker = DefaultTicks{}
	return a, nil
}
func (a *Axis) sanitizeRange() {
	if math.IsInf(a.Min, 0) {
		a.Min = 0
	}
	if math.IsInf(a.Max, 0) {
		a.Max = 0
	}
	if a.Min > a.Max {
		a.Min, a.Max = a.Max, a.Min
	}
	if a.Min == a.Max {
		a.Min--
		a.Max++
	}
}
type LinearScale struct{}
var _ Normalizer = LinearScale{}
func (LinearScale) Normalize(min, max, x float64) float64 {
	return (x - min) / (max - min)
}
type LogScale struct{}
var _ Normalizer = LogScale{}
func (LogScale) Normalize(min, max, x float64) float64 {
	if min <= 0 || max <= 0 || x <= 0 {
		panic("Values must be greater than 0 for a log scale.")
	}
	logMin := math.Log(min)
	return (math.Log(x) - logMin) / (math.Log(max) - logMin)
}
type InvertedScale struct{ Normalizer }
var _ Normalizer = InvertedScale{}
func (is InvertedScale) Normalize(min, max, x float64) float64 {
	return is.Normalizer.Normalize(max, min, x)
}
func (a Axis) Norm(x float64) float64 {
	return a.Scale.Normalize(a.Min, a.Max, x)
}
func (a Axis) drawTicks() bool {
	return a.Tick.Width > 0 && a.Tick.Length > 0
}
type horizontalAxis struct {
	Axis
}
func (a horizontalAxis) size() (h vg.Length) {
	if a.Label.Text != "" {  
		h -= a.Label.Font.Extents().Descent
		h += a.Label.Height(a.Label.Text)
	}
	marks := a.Tick.Marker.Ticks(a.Min, a.Max)
	if len(marks) > 0 {
		if a.drawTicks() {
			h += a.Tick.Length
		}
		h += tickLabelHeight(a.Tick.Label, marks)
	}
	h += a.Width / 2
	h += a.Padding
	return h
}
func (a horizontalAxis) draw(c draw.Canvas) {
	y := c.Min.Y
	if a.Label.Text != "" {
		y -= a.Label.Font.Extents().Descent
		c.FillText(a.Label.TextStyle, vg.Point{X: c.Center().X, Y: y}, a.Label.Text)
		y += a.Label.Height(a.Label.Text)
	}
	marks := a.Tick.Marker.Ticks(a.Min, a.Max)
	ticklabelheight := tickLabelHeight(a.Tick.Label, marks)
	for _, t := range marks {
		x := c.X(a.Norm(t.Value))
		if !c.ContainsX(x) || t.IsMinor() {
			continue
		}
		c.FillText(a.Tick.Label, vg.Point{X: x, Y: y + ticklabelheight}, t.Label)
	}
	if len(marks) > 0 {
		y += ticklabelheight
	} else {
		y += a.Width / 2
	}
	if len(marks) > 0 && a.drawTicks() {
		len := a.Tick.Length
		for _, t := range marks {
			x := c.X(a.Norm(t.Value))
			if !c.ContainsX(x) {
				continue
			}
			start := t.lengthOffset(len)
			c.StrokeLine2(a.Tick.LineStyle, x, y+start, x, y+len)
		}
		y += len
	}
	c.StrokeLine2(a.LineStyle, c.Min.X, y, c.Max.X, y)
}
func (a horizontalAxis) GlyphBoxes(*Plot) []GlyphBox {
	var boxes []GlyphBox
	for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
		if t.IsMinor() {
			continue
		}
		box := GlyphBox{
			X:         a.Norm(t.Value),
			Rectangle: a.Tick.Label.Rectangle(t.Label),
		}
		boxes = append(boxes, box)
	}
	return boxes
}
type verticalAxis struct {
	Axis
}
func (a verticalAxis) size() (w vg.Length) {
	if a.Label.Text != "" {  
		w -= a.Label.Font.Extents().Descent
		w += a.Label.Height(a.Label.Text)
	}
	marks := a.Tick.Marker.Ticks(a.Min, a.Max)
	if len(marks) > 0 {
		if lwidth := tickLabelWidth(a.Tick.Label, marks); lwidth > 0 {
			w += lwidth
			w += a.Label.Width(" ")
		}
		if a.drawTicks() {
			w += a.Tick.Length
		}
	}
	w += a.Width / 2
	w += a.Padding
	return w
}
func (a verticalAxis) draw(c draw.Canvas) {
	x := c.Min.X
	if a.Label.Text != "" {
		sty := a.Label.TextStyle
		sty.Rotation += math.Pi / 2
		x += a.Label.Height(a.Label.Text)
		c.FillText(sty, vg.Point{X: x, Y: c.Center().Y}, a.Label.Text)
		x += -a.Label.Font.Extents().Descent
	}
	marks := a.Tick.Marker.Ticks(a.Min, a.Max)
	if w := tickLabelWidth(a.Tick.Label, marks); len(marks) > 0 && w > 0 {
		x += w
	}
	major := false
	for _, t := range marks {
		y := c.Y(a.Norm(t.Value))
		if !c.ContainsY(y) || t.IsMinor() {
			continue
		}
		c.FillText(a.Tick.Label, vg.Point{X: x, Y: y}, t.Label)
		major = true
	}
	if major {
		x += a.Tick.Label.Width(" ")
	}
	if a.drawTicks() && len(marks) > 0 {
		len := a.Tick.Length
		for _, t := range marks {
			y := c.Y(a.Norm(t.Value))
			if !c.ContainsY(y) {
				continue
			}
			start := t.lengthOffset(len)
			c.StrokeLine2(a.Tick.LineStyle, x+start, y, x+len, y)
		}
		x += len
	}
	c.StrokeLine2(a.LineStyle, x, c.Min.Y, x, c.Max.Y)
}
func (a verticalAxis) GlyphBoxes(*Plot) []GlyphBox {
	var boxes []GlyphBox
	for _, t := range a.Tick.Marker.Ticks(a.Min, a.Max) {
		if t.IsMinor() {
			continue
		}
		box := GlyphBox{
			Y:         a.Norm(t.Value),
			Rectangle: a.Tick.Label.Rectangle(t.Label),
		}
		boxes = append(boxes, box)
	}
	return boxes
}
type DefaultTicks struct{}
var _ Ticker = DefaultTicks{}
func (DefaultTicks) Ticks(min, max float64) []Tick {
	if max <= min {
		panic("illegal range")
	}
	const suggestedTicks = 3
	labels, step, q, mag := talbotLinHanrahan(min, max, suggestedTicks, withinData, nil, nil, nil)
	majorDelta := step * math.Pow10(mag)
	if q == 0 {
		majorDelta = labels[1] - labels[0]
	}
	fc := byte('f')
	var off int
	if mag < -1 || 6 < mag {
		off = 1
		fc = 'g'
	}
	if math.Trunc(q) != q {
		off += 2
	}
	prec := minInt(6, maxInt(off, -mag))
	var ticks []Tick
	for _, v := range labels {
		ticks = append(ticks, Tick{Value: v, Label: strconv.FormatFloat(v, fc, prec, 64)})
	}
	var minorDelta float64
	switch step {
	case 1, 2.5:
		minorDelta = majorDelta / 5
	case 2, 3, 4, 5:
		minorDelta = majorDelta / step
	default:
		if majorDelta/2 < dlamchP {
			return ticks
		}
		minorDelta = majorDelta / 2
	}
	var i float64
	for labels[0]+(i-1)*minorDelta > min {
		i--
	}
	for {
		val := labels[0] + i*minorDelta
		if val > max {
			break
		}
		found := false
		for _, t := range ticks {
			if math.Abs(t.Value-val) < minorDelta/2 {
				found = true
			}
		}
		if !found {
			ticks = append(ticks, Tick{Value: val})
		}
		i++
	}
	return ticks
}
func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func maxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}
type LogTicks struct{}
var _ Ticker = LogTicks{}