fixed dependencies
This commit is contained in:
751
vendor/gonum.org/v1/plot/axis.go
generated
vendored
Normal file
751
vendor/gonum.org/v1/plot/axis.go
generated
vendored
Normal file
@@ -0,0 +1,751 @@
|
||||
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package plot
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gonum.org/v1/plot/font"
|
||||
"gonum.org/v1/plot/text"
|
||||
"gonum.org/v1/plot/vg"
|
||||
"gonum.org/v1/plot/vg/draw"
|
||||
)
|
||||
|
||||
// Ticker creates Ticks in a specified range
|
||||
type Ticker interface {
|
||||
// Ticks returns Ticks in a specified range
|
||||
Ticks(min, max float64) []Tick
|
||||
}
|
||||
|
||||
// Normalizer rescales values from the data coordinate system to the
|
||||
// normalized coordinate system.
|
||||
type Normalizer interface {
|
||||
// Normalize transforms a value x in the data coordinate system to
|
||||
// the normalized coordinate system.
|
||||
Normalize(min, max, x float64) float64
|
||||
}
|
||||
|
||||
// An Axis represents either a horizontal or vertical
|
||||
// axis of a plot.
|
||||
type Axis struct {
|
||||
// Min and Max are the minimum and maximum data
|
||||
// values represented by the axis.
|
||||
Min, Max float64
|
||||
|
||||
Label struct {
|
||||
// Text is the axis label string.
|
||||
Text string
|
||||
|
||||
// Padding is the distance between the label and the axis.
|
||||
Padding vg.Length
|
||||
|
||||
// TextStyle is the style of the axis label text.
|
||||
// For the vertical axis, one quarter turn
|
||||
// counterclockwise will be added to the label
|
||||
// text before drawing.
|
||||
TextStyle text.Style
|
||||
|
||||
// Position is where the axis label string should be drawn.
|
||||
// The default value is draw.PosCenter, displaying the label
|
||||
// at the center of the axis.
|
||||
// Valid values are [-1,+1], with +1 being the far right/top
|
||||
// of the axis, and -1 the far left/bottom of the axis.
|
||||
Position float64
|
||||
}
|
||||
|
||||
// LineStyle is the style of the axis line.
|
||||
draw.LineStyle
|
||||
|
||||
// Padding between the axis line and the data. Having
|
||||
// non-zero padding ensures that the data is never drawn
|
||||
// on the axis, thus making it easier to see.
|
||||
Padding vg.Length
|
||||
|
||||
Tick struct {
|
||||
// Label is the TextStyle on the tick labels.
|
||||
Label text.Style
|
||||
|
||||
// LineStyle is the LineStyle of the tick lines.
|
||||
draw.LineStyle
|
||||
|
||||
// Length is the length of a major tick mark.
|
||||
// Minor tick marks are half of the length of major
|
||||
// tick marks.
|
||||
Length vg.Length
|
||||
|
||||
// Marker returns the tick marks. Any tick marks
|
||||
// returned by the Marker function that are not in
|
||||
// range of the axis are not drawn.
|
||||
Marker Ticker
|
||||
}
|
||||
|
||||
// Scale transforms a value given in the data coordinate system
|
||||
// to the normalized coordinate system of the axis—its distance
|
||||
// along the axis as a fraction of the axis range.
|
||||
Scale Normalizer
|
||||
|
||||
// AutoRescale enables an axis to automatically adapt its minimum
|
||||
// and maximum boundaries, according to its underlying Ticker.
|
||||
AutoRescale bool
|
||||
}
|
||||
|
||||
// makeAxis returns a default Axis.
|
||||
//
|
||||
// The default range is (∞, ∞), and thus any finite
|
||||
// value is less than Min and greater than Max.
|
||||
func makeAxis(o orientation) Axis {
|
||||
|
||||
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 = text.Style{
|
||||
Color: color.Black,
|
||||
Font: font.From(DefaultFont, 12),
|
||||
XAlign: draw.XCenter,
|
||||
YAlign: draw.YBottom,
|
||||
Handler: DefaultTextHandler,
|
||||
}
|
||||
a.Label.Position = draw.PosCenter
|
||||
|
||||
var (
|
||||
xalign draw.XAlignment
|
||||
yalign draw.YAlignment
|
||||
)
|
||||
switch o {
|
||||
case vertical:
|
||||
xalign = draw.XRight
|
||||
yalign = draw.YCenter
|
||||
case horizontal:
|
||||
xalign = draw.XCenter
|
||||
yalign = draw.YTop
|
||||
}
|
||||
|
||||
a.Tick.Label = text.Style{
|
||||
Color: color.Black,
|
||||
Font: font.From(DefaultFont, 10),
|
||||
XAlign: xalign,
|
||||
YAlign: yalign,
|
||||
Handler: DefaultTextHandler,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// sanitizeRange ensures that the range of the
|
||||
// axis makes sense.
|
||||
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++
|
||||
}
|
||||
|
||||
if a.AutoRescale {
|
||||
marks := a.Tick.Marker.Ticks(a.Min, a.Max)
|
||||
for _, t := range marks {
|
||||
a.Min = math.Min(a.Min, t.Value)
|
||||
a.Max = math.Max(a.Max, t.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LinearScale an be used as the value of an Axis.Scale function to
|
||||
// set the axis to a standard linear scale.
|
||||
type LinearScale struct{}
|
||||
|
||||
var _ Normalizer = LinearScale{}
|
||||
|
||||
// Normalize returns the fractional distance of x between min and max.
|
||||
func (LinearScale) Normalize(min, max, x float64) float64 {
|
||||
return (x - min) / (max - min)
|
||||
}
|
||||
|
||||
// LogScale can be used as the value of an Axis.Scale function to
|
||||
// set the axis to a log scale.
|
||||
type LogScale struct{}
|
||||
|
||||
var _ Normalizer = LogScale{}
|
||||
|
||||
// Normalize returns the fractional logarithmic distance of
|
||||
// x between min and max.
|
||||
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)
|
||||
}
|
||||
|
||||
// InvertedScale can be used as the value of an Axis.Scale function to
|
||||
// invert the axis using any Normalizer.
|
||||
type InvertedScale struct{ Normalizer }
|
||||
|
||||
var _ Normalizer = InvertedScale{}
|
||||
|
||||
// Normalize returns a normalized [0, 1] value for the position of x.
|
||||
func (is InvertedScale) Normalize(min, max, x float64) float64 {
|
||||
return is.Normalizer.Normalize(max, min, x)
|
||||
}
|
||||
|
||||
// Norm returns the value of x, given in the data coordinate
|
||||
// system, normalized to its distance as a fraction of the
|
||||
// range of this axis. For example, if x is a.Min then the return
|
||||
// value is 0, and if x is a.Max then the return value is 1.
|
||||
func (a Axis) Norm(x float64) float64 {
|
||||
return a.Scale.Normalize(a.Min, a.Max, x)
|
||||
}
|
||||
|
||||
// drawTicks returns true if the tick marks should be drawn.
|
||||
func (a Axis) drawTicks() bool {
|
||||
return a.Tick.Width > 0 && a.Tick.Length > 0
|
||||
}
|
||||
|
||||
// A horizontalAxis draws horizontally across the bottom
|
||||
// of a plot.
|
||||
type horizontalAxis struct {
|
||||
Axis
|
||||
}
|
||||
|
||||
// size returns the height of the axis.
|
||||
func (a horizontalAxis) size() (h vg.Length) {
|
||||
if a.Label.Text != "" { // We assume that the label isn't rotated.
|
||||
h += a.Label.TextStyle.FontExtents().Descent
|
||||
h += a.Label.TextStyle.Height(a.Label.Text)
|
||||
h += a.Label.Padding
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// draw draws the axis along the lower edge of a draw.Canvas.
|
||||
func (a horizontalAxis) draw(c draw.Canvas) {
|
||||
var (
|
||||
x vg.Length
|
||||
y = c.Min.Y
|
||||
)
|
||||
switch a.Label.Position {
|
||||
case draw.PosCenter:
|
||||
x = c.Center().X
|
||||
case draw.PosRight:
|
||||
x = c.Max.X
|
||||
x -= a.Label.TextStyle.Width(a.Label.Text) / 2
|
||||
}
|
||||
if a.Label.Text != "" {
|
||||
descent := a.Label.TextStyle.FontExtents().Descent
|
||||
c.FillText(a.Label.TextStyle, vg.Point{X: x, Y: y + descent}, a.Label.Text)
|
||||
y += a.Label.TextStyle.Height(a.Label.Text)
|
||||
y += a.Label.Padding
|
||||
}
|
||||
|
||||
marks := a.Tick.Marker.Ticks(a.Min, a.Max)
|
||||
ticklabelheight := tickLabelHeight(a.Tick.Label, marks)
|
||||
descent := a.Tick.Label.FontExtents().Descent
|
||||
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 + descent}, 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)
|
||||
}
|
||||
|
||||
// GlyphBoxes returns the GlyphBoxes for the tick labels.
|
||||
func (a horizontalAxis) GlyphBoxes(p *Plot) []GlyphBox {
|
||||
var (
|
||||
boxes []GlyphBox
|
||||
yoff font.Length
|
||||
)
|
||||
|
||||
if a.Label.Text != "" {
|
||||
x := a.Norm(p.X.Max)
|
||||
switch a.Label.Position {
|
||||
case draw.PosCenter:
|
||||
x = a.Norm(0.5 * (p.X.Max + p.X.Min))
|
||||
case draw.PosRight:
|
||||
x -= a.Norm(0.5 * a.Label.TextStyle.Width(a.Label.Text).Points()) // FIXME(sbinet): want data coordinates
|
||||
}
|
||||
descent := a.Label.TextStyle.FontExtents().Descent
|
||||
boxes = append(boxes, GlyphBox{
|
||||
X: x,
|
||||
Rectangle: a.Label.TextStyle.Rectangle(a.Label.Text).Add(vg.Point{Y: yoff + descent}),
|
||||
})
|
||||
yoff += a.Label.TextStyle.Height(a.Label.Text)
|
||||
yoff += a.Label.Padding
|
||||
}
|
||||
|
||||
var (
|
||||
marks = a.Tick.Marker.Ticks(a.Min, a.Max)
|
||||
height = tickLabelHeight(a.Tick.Label, marks)
|
||||
descent = a.Tick.Label.FontExtents().Descent
|
||||
)
|
||||
for _, t := range marks {
|
||||
if t.IsMinor() {
|
||||
continue
|
||||
}
|
||||
box := GlyphBox{
|
||||
X: a.Norm(t.Value),
|
||||
Rectangle: a.Tick.Label.Rectangle(t.Label).Add(vg.Point{Y: yoff + height + descent}),
|
||||
}
|
||||
boxes = append(boxes, box)
|
||||
}
|
||||
return boxes
|
||||
}
|
||||
|
||||
// A verticalAxis is drawn vertically up the left side of a plot.
|
||||
type verticalAxis struct {
|
||||
Axis
|
||||
}
|
||||
|
||||
// size returns the width of the axis.
|
||||
func (a verticalAxis) size() (w vg.Length) {
|
||||
if a.Label.Text != "" { // We assume that the label isn't rotated.
|
||||
w += a.Label.TextStyle.FontExtents().Descent
|
||||
w += a.Label.TextStyle.Height(a.Label.Text)
|
||||
w += a.Label.Padding
|
||||
}
|
||||
|
||||
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.TextStyle.Width(" ")
|
||||
}
|
||||
if a.drawTicks() {
|
||||
w += a.Tick.Length
|
||||
}
|
||||
}
|
||||
w += a.Width / 2
|
||||
w += a.Padding
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
// draw draws the axis along the left side of a draw.Canvas.
|
||||
func (a verticalAxis) draw(c draw.Canvas) {
|
||||
var (
|
||||
x = c.Min.X
|
||||
y vg.Length
|
||||
)
|
||||
if a.Label.Text != "" {
|
||||
sty := a.Label.TextStyle
|
||||
sty.Rotation += math.Pi / 2
|
||||
x += a.Label.TextStyle.Height(a.Label.Text)
|
||||
switch a.Label.Position {
|
||||
case draw.PosCenter:
|
||||
y = c.Center().Y
|
||||
case draw.PosTop:
|
||||
y = c.Max.Y
|
||||
y -= a.Label.TextStyle.Width(a.Label.Text) / 2
|
||||
}
|
||||
descent := a.Label.TextStyle.FontExtents().Descent
|
||||
c.FillText(sty, vg.Point{X: x - descent, Y: y}, a.Label.Text)
|
||||
x += descent
|
||||
x += a.Label.Padding
|
||||
}
|
||||
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
|
||||
descent := a.Tick.Label.FontExtents().Descent
|
||||
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 + descent}, 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)
|
||||
}
|
||||
|
||||
// GlyphBoxes returns the GlyphBoxes for the tick labels
|
||||
func (a verticalAxis) GlyphBoxes(p *Plot) []GlyphBox {
|
||||
var (
|
||||
boxes []GlyphBox
|
||||
xoff font.Length
|
||||
)
|
||||
|
||||
if a.Label.Text != "" {
|
||||
yoff := a.Norm(p.Y.Max)
|
||||
switch a.Label.Position {
|
||||
case draw.PosCenter:
|
||||
yoff = a.Norm(0.5 * (p.Y.Max + p.Y.Min))
|
||||
case draw.PosTop:
|
||||
yoff -= a.Norm(0.5 * a.Label.TextStyle.Width(a.Label.Text).Points()) // FIXME(sbinet): want data coordinates
|
||||
}
|
||||
|
||||
sty := a.Label.TextStyle
|
||||
sty.Rotation += math.Pi / 2
|
||||
|
||||
xoff += a.Label.TextStyle.Height(a.Label.Text)
|
||||
descent := a.Label.TextStyle.FontExtents().Descent
|
||||
boxes = append(boxes, GlyphBox{
|
||||
Y: yoff,
|
||||
Rectangle: sty.Rectangle(a.Label.Text).Add(vg.Point{X: xoff - descent}),
|
||||
})
|
||||
xoff += descent
|
||||
xoff += a.Label.Padding
|
||||
}
|
||||
|
||||
marks := a.Tick.Marker.Ticks(a.Min, a.Max)
|
||||
if w := tickLabelWidth(a.Tick.Label, marks); len(marks) != 0 && w > 0 {
|
||||
xoff += w
|
||||
}
|
||||
|
||||
var (
|
||||
ext = a.Tick.Label.FontExtents()
|
||||
desc = ext.Height - ext.Ascent // descent + linegap
|
||||
)
|
||||
for _, t := range marks {
|
||||
if t.IsMinor() {
|
||||
continue
|
||||
}
|
||||
box := GlyphBox{
|
||||
Y: a.Norm(t.Value),
|
||||
Rectangle: a.Tick.Label.Rectangle(t.Label).Add(vg.Point{X: xoff, Y: desc}),
|
||||
}
|
||||
boxes = append(boxes, box)
|
||||
}
|
||||
return boxes
|
||||
}
|
||||
|
||||
// DefaultTicks is suitable for the Tick.Marker field of an Axis,
|
||||
// it returns a reasonable default set of tick marks.
|
||||
type DefaultTicks struct{}
|
||||
|
||||
var _ Ticker = DefaultTicks{}
|
||||
|
||||
// Ticks returns Ticks in the specified range.
|
||||
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 {
|
||||
// Simple fall back was chosen, so
|
||||
// majorDelta is the label distance.
|
||||
majorDelta = labels[1] - labels[0]
|
||||
}
|
||||
|
||||
// Choose a reasonable, but ad
|
||||
// hoc formatting for labels.
|
||||
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))
|
||||
ticks := make([]Tick, len(labels))
|
||||
for i, v := range labels {
|
||||
ticks[i] = Tick{Value: v, Label: strconv.FormatFloat(v, fc, prec, 64)}
|
||||
}
|
||||
|
||||
var minorDelta float64
|
||||
// See talbotLinHanrahan for the values used here.
|
||||
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
|
||||
}
|
||||
|
||||
// Find the first minor tick not greater
|
||||
// than the lowest data value.
|
||||
var i float64
|
||||
for labels[0]+(i-1)*minorDelta > min {
|
||||
i--
|
||||
}
|
||||
// Add ticks at minorDelta intervals when
|
||||
// they are not within minorDelta/2 of a
|
||||
// labelled tick.
|
||||
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
|
||||
}
|
||||
|
||||
// LogTicks is suitable for the Tick.Marker field of an Axis,
|
||||
// it returns tick marks suitable for a log-scale axis.
|
||||
type LogTicks struct {
|
||||
// Prec specifies the precision of tick rendering
|
||||
// according to the documentation for strconv.FormatFloat.
|
||||
Prec int
|
||||
}
|
||||
|
||||
var _ Ticker = LogTicks{}
|
||||
|
||||
// Ticks returns Ticks in a specified range
|
||||
func (t 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, t.Prec)})
|
||||
}
|
||||
ticks = append(ticks, Tick{Value: val * float64(i)})
|
||||
}
|
||||
val *= 10
|
||||
}
|
||||
ticks = append(ticks, Tick{Value: val, Label: formatFloatTick(val, t.Prec)})
|
||||
|
||||
return ticks
|
||||
}
|
||||
|
||||
// ConstantTicks is suitable for the Tick.Marker field of an Axis.
|
||||
// This function returns the given set of ticks.
|
||||
type ConstantTicks []Tick
|
||||
|
||||
var _ Ticker = ConstantTicks{}
|
||||
|
||||
// Ticks returns Ticks in a specified range
|
||||
func (ts ConstantTicks) Ticks(float64, float64) []Tick {
|
||||
return ts
|
||||
}
|
||||
|
||||
// UnixTimeIn returns a time conversion function for the given location.
|
||||
func UnixTimeIn(loc *time.Location) func(t float64) time.Time {
|
||||
return func(t float64) time.Time {
|
||||
return time.Unix(int64(t), 0).In(loc)
|
||||
}
|
||||
}
|
||||
|
||||
// UTCUnixTime is the default time conversion for TimeTicks.
|
||||
var UTCUnixTime = UnixTimeIn(time.UTC)
|
||||
|
||||
// TimeTicks is suitable for axes representing time values.
|
||||
type TimeTicks struct {
|
||||
// Ticker is used to generate a set of ticks.
|
||||
// If nil, DefaultTicks will be used.
|
||||
Ticker Ticker
|
||||
|
||||
// Format is the textual representation of the time value.
|
||||
// If empty, time.RFC3339 will be used
|
||||
Format string
|
||||
|
||||
// Time takes a float64 value and converts it into a time.Time.
|
||||
// If nil, UTCUnixTime is used.
|
||||
Time func(t float64) time.Time
|
||||
}
|
||||
|
||||
var _ Ticker = TimeTicks{}
|
||||
|
||||
// Ticks implements plot.Ticker.
|
||||
func (t TimeTicks) Ticks(min, max float64) []Tick {
|
||||
if t.Ticker == nil {
|
||||
t.Ticker = DefaultTicks{}
|
||||
}
|
||||
if t.Format == "" {
|
||||
t.Format = time.RFC3339
|
||||
}
|
||||
if t.Time == nil {
|
||||
t.Time = UTCUnixTime
|
||||
}
|
||||
|
||||
ticks := t.Ticker.Ticks(min, max)
|
||||
for i := range ticks {
|
||||
tick := &ticks[i]
|
||||
if tick.Label == "" {
|
||||
continue
|
||||
}
|
||||
tick.Label = t.Time(tick.Value).Format(t.Format)
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
|
||||
// A Tick is a single tick mark on an axis.
|
||||
type Tick struct {
|
||||
// Value is the data value marked by this Tick.
|
||||
Value float64
|
||||
|
||||
// Label is the text to display at the tick mark.
|
||||
// If Label is an empty string then this is a minor
|
||||
// tick mark.
|
||||
Label string
|
||||
}
|
||||
|
||||
// IsMinor returns true if this is a minor tick mark.
|
||||
func (t Tick) IsMinor() bool {
|
||||
return t.Label == ""
|
||||
}
|
||||
|
||||
// lengthOffset returns an offset that should be added to the
|
||||
// tick mark's line to accout for its length. I.e., the start of
|
||||
// the line for a minor tick mark must be shifted by half of
|
||||
// the length.
|
||||
func (t Tick) lengthOffset(len vg.Length) vg.Length {
|
||||
if t.IsMinor() {
|
||||
return len / 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// tickLabelHeight returns height of the tick mark labels.
|
||||
func tickLabelHeight(sty text.Style, ticks []Tick) vg.Length {
|
||||
maxHeight := vg.Length(0)
|
||||
for _, t := range ticks {
|
||||
if t.IsMinor() {
|
||||
continue
|
||||
}
|
||||
r := sty.Rectangle(t.Label)
|
||||
h := r.Max.Y - r.Min.Y
|
||||
if h > maxHeight {
|
||||
maxHeight = h
|
||||
}
|
||||
}
|
||||
return maxHeight
|
||||
}
|
||||
|
||||
// tickLabelWidth returns the width of the widest tick mark label.
|
||||
func tickLabelWidth(sty text.Style, ticks []Tick) vg.Length {
|
||||
maxWidth := vg.Length(0)
|
||||
for _, t := range ticks {
|
||||
if t.IsMinor() {
|
||||
continue
|
||||
}
|
||||
r := sty.Rectangle(t.Label)
|
||||
w := r.Max.X - r.Min.X
|
||||
if w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
}
|
||||
return maxWidth
|
||||
}
|
||||
|
||||
// formatFloatTick returns a g-formated string representation of v
|
||||
// to the specified precision.
|
||||
func formatFloatTick(v float64, prec int) string {
|
||||
return strconv.FormatFloat(v, 'g', prec, 64)
|
||||
}
|
||||
|
||||
// TickerFunc is suitable for the Tick.Marker field of an Axis.
|
||||
// It is an adapter which allows to quickly setup a Ticker using a function with an appropriate signature.
|
||||
type TickerFunc func(min, max float64) []Tick
|
||||
|
||||
var _ Ticker = TickerFunc(nil)
|
||||
|
||||
// Ticks implements plot.Ticker.
|
||||
func (f TickerFunc) Ticks(min, max float64) []Tick {
|
||||
return f(min, max)
|
||||
}
|
||||
Reference in New Issue
Block a user