updates
This commit is contained in:
parent
1c1430e3b8
commit
c47025edf6
35 changed files with 287 additions and 186 deletions
|
@ -3,7 +3,7 @@ package chart
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/util"
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BollingerBandsSeries draws bollinger bands for an inner series.
|
// BollingerBandsSeries draws bollinger bands for an inner series.
|
||||||
|
@ -17,7 +17,7 @@ type BollingerBandsSeries struct {
|
||||||
K float64
|
K float64
|
||||||
InnerSeries ValuesProvider
|
InnerSeries ValuesProvider
|
||||||
|
|
||||||
valueBuffer *util.ValueBuffer
|
valueBuffer *sequence.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetName returns the name of the time series.
|
// GetName returns the name of the time series.
|
||||||
|
@ -67,7 +67,7 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if bbs.valueBuffer == nil || index == 0 {
|
if bbs.valueBuffer == nil || index == 0 {
|
||||||
bbs.valueBuffer = util.NewValueBufferWithCapacity(bbs.GetPeriod())
|
bbs.valueBuffer = sequence.NewBufferWithCapacity(bbs.GetPeriod())
|
||||||
}
|
}
|
||||||
if bbs.valueBuffer.Len() >= bbs.GetPeriod() {
|
if bbs.valueBuffer.Len() >= bbs.GetPeriod() {
|
||||||
bbs.valueBuffer.Dequeue()
|
bbs.valueBuffer.Dequeue()
|
||||||
|
@ -76,8 +76,8 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64)
|
||||||
bbs.valueBuffer.Enqueue(py)
|
bbs.valueBuffer.Enqueue(py)
|
||||||
x = px
|
x = px
|
||||||
|
|
||||||
ay := util.Sequence{bbs.valueBuffer}.Average()
|
ay := sequence.Seq{Provider: bbs.valueBuffer}.Average()
|
||||||
std := util.Sequence{bbs.valueBuffer}.StdDev()
|
std := sequence.Seq{Provider: bbs.valueBuffer}.StdDev()
|
||||||
|
|
||||||
y1 = ay + (bbs.GetK() * std)
|
y1 = ay + (bbs.GetK() * std)
|
||||||
y2 = ay - (bbs.GetK() * std)
|
y2 = ay - (bbs.GetK() * std)
|
||||||
|
@ -96,15 +96,15 @@ func (bbs *BollingerBandsSeries) GetBoundedLastValues() (x, y1, y2 float64) {
|
||||||
startAt = 0
|
startAt = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
vb := NewValueBufferWithCapacity(period)
|
vb := sequence.NewBufferWithCapacity(period)
|
||||||
for index := startAt; index < seriesLength; index++ {
|
for index := startAt; index < seriesLength; index++ {
|
||||||
xn, yn := bbs.InnerSeries.GetValues(index)
|
xn, yn := bbs.InnerSeries.GetValues(index)
|
||||||
vb.Enqueue(yn)
|
vb.Enqueue(yn)
|
||||||
x = xn
|
x = xn
|
||||||
}
|
}
|
||||||
|
|
||||||
ay := Sequence{vb}.Average()
|
ay := sequence.Seq{Provider: vb}.Average()
|
||||||
std := Sequence{vb}.StdDev()
|
std := sequence.Seq{Provider: vb}.StdDev()
|
||||||
|
|
||||||
y1 = ay + (bbs.GetK() * std)
|
y1 = ay + (bbs.GetK() * std)
|
||||||
y2 = ay - (bbs.GetK() * std)
|
y2 = ay - (bbs.GetK() * std)
|
||||||
|
|
|
@ -6,14 +6,15 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBollingerBandSeries(t *testing.T) {
|
func TestBollingerBandSeries(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
s1 := mockValuesProvider{
|
s1 := mockValuesProvider{
|
||||||
X: Generate.Float64(1.0, 100.0),
|
X: sequence.Values(1.0, 100.0),
|
||||||
Y: Generate.Random(100, 1024),
|
Y: sequence.RandomValuesWithAverage(1024, 100),
|
||||||
}
|
}
|
||||||
|
|
||||||
bbs := &BollingerBandsSeries{
|
bbs := &BollingerBandsSeries{
|
||||||
|
@ -37,8 +38,8 @@ func TestBollingerBandLastValue(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
s1 := mockValuesProvider{
|
s1 := mockValuesProvider{
|
||||||
X: Generate.Float64(1.0, 100.0),
|
X: sequence.Values(1.0, 100.0),
|
||||||
Y: Generate.Float64(1.0, 100.0),
|
Y: sequence.Values(1.0, 100.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
bbs := &BollingerBandsSeries{
|
bbs := &BollingerBandsSeries{
|
||||||
|
|
56
box.go
56
box.go
|
@ -3,6 +3,8 @@ package chart
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -89,12 +91,12 @@ func (b Box) GetBottom(defaults ...int) int {
|
||||||
|
|
||||||
// Width returns the width
|
// Width returns the width
|
||||||
func (b Box) Width() int {
|
func (b Box) Width() int {
|
||||||
return Math.AbsInt(b.Right - b.Left)
|
return util.Math.AbsInt(b.Right - b.Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Height returns the height
|
// Height returns the height
|
||||||
func (b Box) Height() int {
|
func (b Box) Height() int {
|
||||||
return Math.AbsInt(b.Bottom - b.Top)
|
return util.Math.AbsInt(b.Bottom - b.Top)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center returns the center of the box
|
// Center returns the center of the box
|
||||||
|
@ -146,10 +148,10 @@ func (b Box) Equals(other Box) bool {
|
||||||
// Grow grows a box based on another box.
|
// Grow grows a box based on another box.
|
||||||
func (b Box) Grow(other Box) Box {
|
func (b Box) Grow(other Box) Box {
|
||||||
return Box{
|
return Box{
|
||||||
Top: Math.MinInt(b.Top, other.Top),
|
Top: util.Math.MinInt(b.Top, other.Top),
|
||||||
Left: Math.MinInt(b.Left, other.Left),
|
Left: util.Math.MinInt(b.Left, other.Left),
|
||||||
Right: Math.MaxInt(b.Right, other.Right),
|
Right: util.Math.MaxInt(b.Right, other.Right),
|
||||||
Bottom: Math.MaxInt(b.Bottom, other.Bottom),
|
Bottom: util.Math.MaxInt(b.Bottom, other.Bottom),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,10 +222,10 @@ func (b Box) Fit(other Box) Box {
|
||||||
func (b Box) Constrain(other Box) Box {
|
func (b Box) Constrain(other Box) Box {
|
||||||
newBox := b.Clone()
|
newBox := b.Clone()
|
||||||
|
|
||||||
newBox.Top = Math.MaxInt(newBox.Top, other.Top)
|
newBox.Top = util.Math.MaxInt(newBox.Top, other.Top)
|
||||||
newBox.Left = Math.MaxInt(newBox.Left, other.Left)
|
newBox.Left = util.Math.MaxInt(newBox.Left, other.Left)
|
||||||
newBox.Right = Math.MinInt(newBox.Right, other.Right)
|
newBox.Right = util.Math.MinInt(newBox.Right, other.Right)
|
||||||
newBox.Bottom = Math.MinInt(newBox.Bottom, other.Bottom)
|
newBox.Bottom = util.Math.MinInt(newBox.Bottom, other.Bottom)
|
||||||
|
|
||||||
return newBox
|
return newBox
|
||||||
}
|
}
|
||||||
|
@ -262,36 +264,36 @@ type BoxCorners struct {
|
||||||
// Box return the BoxCorners as a regular box.
|
// Box return the BoxCorners as a regular box.
|
||||||
func (bc BoxCorners) Box() Box {
|
func (bc BoxCorners) Box() Box {
|
||||||
return Box{
|
return Box{
|
||||||
Top: Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y),
|
Top: util.Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y),
|
||||||
Left: Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X),
|
Left: util.Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X),
|
||||||
Right: Math.MaxInt(bc.TopRight.X, bc.BottomRight.X),
|
Right: util.Math.MaxInt(bc.TopRight.X, bc.BottomRight.X),
|
||||||
Bottom: Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y),
|
Bottom: util.Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Width returns the width
|
// Width returns the width
|
||||||
func (bc BoxCorners) Width() int {
|
func (bc BoxCorners) Width() int {
|
||||||
minLeft := Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X)
|
minLeft := util.Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X)
|
||||||
maxRight := Math.MaxInt(bc.TopRight.X, bc.BottomRight.X)
|
maxRight := util.Math.MaxInt(bc.TopRight.X, bc.BottomRight.X)
|
||||||
return maxRight - minLeft
|
return maxRight - minLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
// Height returns the height
|
// Height returns the height
|
||||||
func (bc BoxCorners) Height() int {
|
func (bc BoxCorners) Height() int {
|
||||||
minTop := Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y)
|
minTop := util.Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y)
|
||||||
maxBottom := Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y)
|
maxBottom := util.Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y)
|
||||||
return maxBottom - minTop
|
return maxBottom - minTop
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center returns the center of the box
|
// Center returns the center of the box
|
||||||
func (bc BoxCorners) Center() (x, y int) {
|
func (bc BoxCorners) Center() (x, y int) {
|
||||||
|
|
||||||
left := Math.MeanInt(bc.TopLeft.X, bc.BottomLeft.X)
|
left := util.Math.MeanInt(bc.TopLeft.X, bc.BottomLeft.X)
|
||||||
right := Math.MeanInt(bc.TopRight.X, bc.BottomRight.X)
|
right := util.Math.MeanInt(bc.TopRight.X, bc.BottomRight.X)
|
||||||
x = ((right - left) >> 1) + left
|
x = ((right - left) >> 1) + left
|
||||||
|
|
||||||
top := Math.MeanInt(bc.TopLeft.Y, bc.TopRight.Y)
|
top := util.Math.MeanInt(bc.TopLeft.Y, bc.TopRight.Y)
|
||||||
bottom := Math.MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y)
|
bottom := util.Math.MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y)
|
||||||
y = ((bottom - top) >> 1) + top
|
y = ((bottom - top) >> 1) + top
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -301,12 +303,12 @@ func (bc BoxCorners) Center() (x, y int) {
|
||||||
func (bc BoxCorners) Rotate(thetaDegrees float64) BoxCorners {
|
func (bc BoxCorners) Rotate(thetaDegrees float64) BoxCorners {
|
||||||
cx, cy := bc.Center()
|
cx, cy := bc.Center()
|
||||||
|
|
||||||
thetaRadians := Math.DegreesToRadians(thetaDegrees)
|
thetaRadians := util.Math.DegreesToRadians(thetaDegrees)
|
||||||
|
|
||||||
tlx, tly := Math.RotateCoordinate(cx, cy, bc.TopLeft.X, bc.TopLeft.Y, thetaRadians)
|
tlx, tly := util.Math.RotateCoordinate(cx, cy, bc.TopLeft.X, bc.TopLeft.Y, thetaRadians)
|
||||||
trx, try := Math.RotateCoordinate(cx, cy, bc.TopRight.X, bc.TopRight.Y, thetaRadians)
|
trx, try := util.Math.RotateCoordinate(cx, cy, bc.TopRight.X, bc.TopRight.Y, thetaRadians)
|
||||||
brx, bry := Math.RotateCoordinate(cx, cy, bc.BottomRight.X, bc.BottomRight.Y, thetaRadians)
|
brx, bry := util.Math.RotateCoordinate(cx, cy, bc.BottomRight.X, bc.BottomRight.Y, thetaRadians)
|
||||||
blx, bly := Math.RotateCoordinate(cx, cy, bc.BottomLeft.X, bc.BottomLeft.Y, thetaRadians)
|
blx, bly := util.Math.RotateCoordinate(cx, cy, bc.BottomLeft.X, bc.BottomLeft.Y, thetaRadians)
|
||||||
|
|
||||||
return BoxCorners{
|
return BoxCorners{
|
||||||
TopLeft: Point{tlx, tly},
|
TopLeft: Point{tlx, tly},
|
||||||
|
|
9
chart.go
9
chart.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chart is what we're drawing.
|
// Chart is what we're drawing.
|
||||||
|
@ -265,8 +266,8 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
// only round if we're showing the axis
|
// only round if we're showing the axis
|
||||||
if c.YAxis.Style.Show {
|
if c.YAxis.Style.Show {
|
||||||
delta := yrange.GetDelta()
|
delta := yrange.GetDelta()
|
||||||
roundTo := Math.GetRoundToForDelta(delta)
|
roundTo := util.Math.GetRoundToForDelta(delta)
|
||||||
rmin, rmax := Math.RoundDown(yrange.GetMin(), roundTo), Math.RoundUp(yrange.GetMax(), roundTo)
|
rmin, rmax := util.Math.RoundDown(yrange.GetMin(), roundTo), util.Math.RoundUp(yrange.GetMax(), roundTo)
|
||||||
|
|
||||||
yrange.SetMin(rmin)
|
yrange.SetMin(rmin)
|
||||||
yrange.SetMax(rmax)
|
yrange.SetMax(rmax)
|
||||||
|
@ -287,8 +288,8 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
|
|
||||||
if c.YAxisSecondary.Style.Show {
|
if c.YAxisSecondary.Style.Show {
|
||||||
delta := yrangeAlt.GetDelta()
|
delta := yrangeAlt.GetDelta()
|
||||||
roundTo := Math.GetRoundToForDelta(delta)
|
roundTo := util.Math.GetRoundToForDelta(delta)
|
||||||
rmin, rmax := Math.RoundDown(yrangeAlt.GetMin(), roundTo), Math.RoundUp(yrangeAlt.GetMax(), roundTo)
|
rmin, rmax := util.Math.RoundDown(yrangeAlt.GetMin(), roundTo), util.Math.RoundUp(yrangeAlt.GetMax(), roundTo)
|
||||||
yrangeAlt.SetMin(rmin)
|
yrangeAlt.SetMin(rmin)
|
||||||
yrangeAlt.SetMax(rmax)
|
yrangeAlt.SetMax(rmax)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
"github.com/wcharczuk/go-chart/drawing"
|
"github.com/wcharczuk/go-chart/drawing"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChartGetDPI(t *testing.T) {
|
func TestChartGetDPI(t *testing.T) {
|
||||||
|
@ -391,8 +392,8 @@ func TestChartRegressionBadRangesByUser(t *testing.T) {
|
||||||
},
|
},
|
||||||
Series: []Series{
|
Series: []Series{
|
||||||
ContinuousSeries{
|
ContinuousSeries{
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -407,8 +408,8 @@ func TestChartValidatesSeries(t *testing.T) {
|
||||||
c := Chart{
|
c := Chart{
|
||||||
Series: []Series{
|
Series: []Series{
|
||||||
ContinuousSeries{
|
ContinuousSeries{
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -418,7 +419,7 @@ func TestChartValidatesSeries(t *testing.T) {
|
||||||
c = Chart{
|
c = Chart{
|
||||||
Series: []Series{
|
Series: []Series{
|
||||||
ContinuousSeries{
|
ContinuousSeries{
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -504,8 +505,8 @@ func TestChartE2ELine(t *testing.T) {
|
||||||
},
|
},
|
||||||
Series: []Series{
|
Series: []Series{
|
||||||
ContinuousSeries{
|
ContinuousSeries{
|
||||||
XValues: Generate.Float64(0, 4, 1),
|
XValues: sequence.Values(0, 4, 1),
|
||||||
YValues: Generate.Float64(0, 4, 1),
|
YValues: sequence.Values(0, 4, 1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -549,8 +550,8 @@ func TestChartE2ELineWithFill(t *testing.T) {
|
||||||
StrokeColor: drawing.ColorBlue,
|
StrokeColor: drawing.ColorBlue,
|
||||||
FillColor: drawing.ColorRed,
|
FillColor: drawing.ColorRed,
|
||||||
},
|
},
|
||||||
XValues: Generate.Float64(0, 4, 1),
|
XValues: sequence.Values(0, 4, 1),
|
||||||
YValues: Generate.Float64(0, 4, 1),
|
YValues: sequence.Values(0, 4, 1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,25 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
assert "github.com/blendlabs/go-assert"
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConcatSeries(t *testing.T) {
|
func TestConcatSeries(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
s1 := ContinuousSeries{
|
s1 := ContinuousSeries{
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
s2 := ContinuousSeries{
|
s2 := ContinuousSeries{
|
||||||
XValues: Generate.Float64(11, 20.0),
|
XValues: sequence.Values(11, 20.0),
|
||||||
YValues: Generate.Float64(10.0, 1.0),
|
YValues: sequence.Values(10.0, 1.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
s3 := ContinuousSeries{
|
s3 := ContinuousSeries{
|
||||||
XValues: Generate.Float64(21, 30.0),
|
XValues: sequence.Values(21, 30.0),
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := ConcatSeries([]Series{s1, s2, s3})
|
cs := ConcatSeries([]Series{s1, s2, s3})
|
||||||
|
|
|
@ -4,13 +4,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRangeTranslate(t *testing.T) {
|
func TestRangeTranslate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
values := []float64{1.0, 2.0, 2.5, 2.7, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}
|
values := []float64{1.0, 2.0, 2.5, 2.7, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}
|
||||||
r := ContinuousRange{Domain: 1000}
|
r := ContinuousRange{Domain: 1000}
|
||||||
r.Min, r.Max = Math.MinAndMax(values...)
|
r.Min, r.Max = util.Math.MinAndMax(values...)
|
||||||
|
|
||||||
// delta = ~7.0
|
// delta = ~7.0
|
||||||
// value = ~5.0
|
// value = ~5.0
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
assert "github.com/blendlabs/go-assert"
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContinuousSeries(t *testing.T) {
|
func TestContinuousSeries(t *testing.T) {
|
||||||
|
@ -12,8 +13,8 @@ func TestContinuousSeries(t *testing.T) {
|
||||||
|
|
||||||
cs := ContinuousSeries{
|
cs := ContinuousSeries{
|
||||||
Name: "Test Series",
|
Name: "Test Series",
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal("Test Series", cs.GetName())
|
assert.Equal("Test Series", cs.GetName())
|
||||||
|
@ -53,20 +54,20 @@ func TestContinuousSeriesValidate(t *testing.T) {
|
||||||
|
|
||||||
cs := ContinuousSeries{
|
cs := ContinuousSeries{
|
||||||
Name: "Test Series",
|
Name: "Test Series",
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
}
|
}
|
||||||
assert.Nil(cs.Validate())
|
assert.Nil(cs.Validate())
|
||||||
|
|
||||||
cs = ContinuousSeries{
|
cs = ContinuousSeries{
|
||||||
Name: "Test Series",
|
Name: "Test Series",
|
||||||
XValues: Generate.Float64(1.0, 10.0),
|
XValues: sequence.Values(1.0, 10.0),
|
||||||
}
|
}
|
||||||
assert.NotNil(cs.Validate())
|
assert.NotNil(cs.Validate())
|
||||||
|
|
||||||
cs = ContinuousSeries{
|
cs = ContinuousSeries{
|
||||||
Name: "Test Series",
|
Name: "Test Series",
|
||||||
YValues: Generate.Float64(1.0, 10.0),
|
YValues: sequence.Values(1.0, 10.0),
|
||||||
}
|
}
|
||||||
assert.NotNil(cs.Validate())
|
assert.NotNil(cs.Validate())
|
||||||
}
|
}
|
||||||
|
|
10
draw.go
10
draw.go
|
@ -1,6 +1,10 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Draw contains helpers for drawing common objects.
|
// Draw contains helpers for drawing common objects.
|
||||||
|
@ -36,8 +40,8 @@ func (d draw) LineSeries(r Renderer, canvasBox Box, xrange, yrange Range, style
|
||||||
y = cb - yrange.Translate(vy)
|
y = cb - yrange.Translate(vy)
|
||||||
r.LineTo(x, y)
|
r.LineTo(x, y)
|
||||||
}
|
}
|
||||||
r.LineTo(x, Math.MinInt(cb, cb-yv0))
|
r.LineTo(x, util.Math.MinInt(cb, cb-yv0))
|
||||||
r.LineTo(x0, Math.MinInt(cb, cb-yv0))
|
r.LineTo(x0, util.Math.MinInt(cb, cb-yv0))
|
||||||
r.LineTo(x0, y0)
|
r.LineTo(x0, y0)
|
||||||
r.Fill()
|
r.Fill()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
emaXValues = Generate.Float64(1.0, 50.0)
|
emaXValues = sequence.Values(1.0, 50.0)
|
||||||
emaYValues = []float64{
|
emaYValues = []float64{
|
||||||
1, 2, 3, 4, 5, 4, 3, 2,
|
1, 2, 3, 4, 5, 4, 3, 2,
|
||||||
1, 2, 3, 4, 5, 4, 3, 2,
|
1, 2, 3, 4, 5, 4, 3, 2,
|
||||||
|
|
3
font.go
3
font.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
|
"github.com/wcharczuk/go-chart/roboto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -17,7 +18,7 @@ func GetDefaultFont() (*truetype.Font, error) {
|
||||||
_defaultFontLock.Lock()
|
_defaultFontLock.Lock()
|
||||||
defer _defaultFontLock.Unlock()
|
defer _defaultFontLock.Unlock()
|
||||||
if _defaultFont == nil {
|
if _defaultFont == nil {
|
||||||
font, err := truetype.Parse(roboto)
|
font, err := truetype.Parse(roboto.Roboto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
assert "github.com/blendlabs/go-assert"
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHistogramSeries(t *testing.T) {
|
func TestHistogramSeries(t *testing.T) {
|
||||||
|
@ -11,8 +12,8 @@ func TestHistogramSeries(t *testing.T) {
|
||||||
|
|
||||||
cs := ContinuousSeries{
|
cs := ContinuousSeries{
|
||||||
Name: "Test Series",
|
Name: "Test Series",
|
||||||
XValues: Generate.Float64(1.0, 20.0),
|
XValues: sequence.Values(1.0, 20.0),
|
||||||
YValues: Generate.Float64(10.0, -10.0),
|
YValues: sequence.Values(10.0, -10.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
hs := HistogramSeries{
|
hs := HistogramSeries{
|
||||||
|
|
13
legend.go
13
legend.go
|
@ -1,6 +1,9 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "github.com/wcharczuk/go-chart/drawing"
|
import (
|
||||||
|
"github.com/wcharczuk/go-chart/drawing"
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
// Legend returns a legend renderable function.
|
// Legend returns a legend renderable function.
|
||||||
func Legend(c *Chart, userDefaults ...Style) Renderable {
|
func Legend(c *Chart, userDefaults ...Style) Renderable {
|
||||||
|
@ -66,7 +69,7 @@ func Legend(c *Chart, userDefaults ...Style) Renderable {
|
||||||
}
|
}
|
||||||
legendContent.Bottom += tb.Height()
|
legendContent.Bottom += tb.Height()
|
||||||
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
|
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
|
||||||
legendContent.Right = Math.MaxInt(legendContent.Right, right)
|
legendContent.Right = util.Math.MaxInt(legendContent.Right, right)
|
||||||
labelCount++
|
labelCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,8 +164,8 @@ func LegendThin(c *Chart, userDefaults ...Style) Renderable {
|
||||||
for x := 0; x < len(labels); x++ {
|
for x := 0; x < len(labels); x++ {
|
||||||
if len(labels[x]) > 0 {
|
if len(labels[x]) > 0 {
|
||||||
textBox = r.MeasureText(labels[x])
|
textBox = r.MeasureText(labels[x])
|
||||||
textHeight = Math.MaxInt(textBox.Height(), textHeight)
|
textHeight = util.Math.MaxInt(textBox.Height(), textHeight)
|
||||||
textWidth = Math.MaxInt(textBox.Width(), textWidth)
|
textWidth = util.Math.MaxInt(textBox.Width(), textWidth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +281,7 @@ func LegendLeft(c *Chart, userDefaults ...Style) Renderable {
|
||||||
}
|
}
|
||||||
legendContent.Bottom += tb.Height()
|
legendContent.Bottom += tb.Height()
|
||||||
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
|
right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum
|
||||||
legendContent.Right = Math.MaxInt(legendContent.Right, right)
|
legendContent.Right = util.Math.MaxInt(legendContent.Right, right)
|
||||||
labelCount++
|
labelCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
// LinearRegressionSeries is a series that plots the n-nearest neighbors
|
// LinearRegressionSeries is a series that plots the n-nearest neighbors
|
||||||
// linear regression for the values.
|
// linear regression for the values.
|
||||||
|
@ -36,7 +41,7 @@ func (lrs LinearRegressionSeries) GetYAxis() YAxisType {
|
||||||
|
|
||||||
// Len returns the number of elements in the series.
|
// Len returns the number of elements in the series.
|
||||||
func (lrs LinearRegressionSeries) Len() int {
|
func (lrs LinearRegressionSeries) Len() int {
|
||||||
return Math.MinInt(lrs.GetLimit(), lrs.InnerSeries.Len()-lrs.GetOffset())
|
return util.Math.MinInt(lrs.GetLimit(), lrs.InnerSeries.Len()-lrs.GetOffset())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLimit returns the window size.
|
// GetLimit returns the window size.
|
||||||
|
@ -51,7 +56,7 @@ func (lrs LinearRegressionSeries) GetLimit() int {
|
||||||
func (lrs LinearRegressionSeries) GetEndIndex() int {
|
func (lrs LinearRegressionSeries) GetEndIndex() int {
|
||||||
windowEnd := lrs.GetOffset() + lrs.GetLimit()
|
windowEnd := lrs.GetOffset() + lrs.GetLimit()
|
||||||
innerSeriesLastIndex := lrs.InnerSeries.Len() - 1
|
innerSeriesLastIndex := lrs.InnerSeries.Len() - 1
|
||||||
return Math.MinInt(windowEnd, innerSeriesLastIndex)
|
return util.Math.MinInt(windowEnd, innerSeriesLastIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOffset returns the data offset.
|
// GetOffset returns the data offset.
|
||||||
|
@ -71,7 +76,7 @@ func (lrs *LinearRegressionSeries) GetValues(index int) (x, y float64) {
|
||||||
lrs.computeCoefficients()
|
lrs.computeCoefficients()
|
||||||
}
|
}
|
||||||
offset := lrs.GetOffset()
|
offset := lrs.GetOffset()
|
||||||
effectiveIndex := Math.MinInt(index+offset, lrs.InnerSeries.Len())
|
effectiveIndex := util.Math.MinInt(index+offset, lrs.InnerSeries.Len())
|
||||||
x, y = lrs.InnerSeries.GetValues(effectiveIndex)
|
x, y = lrs.InnerSeries.GetValues(effectiveIndex)
|
||||||
y = (lrs.m * lrs.normalize(x)) + lrs.b
|
y = (lrs.m * lrs.normalize(x)) + lrs.b
|
||||||
return
|
return
|
||||||
|
@ -102,14 +107,14 @@ func (lrs *LinearRegressionSeries) computeCoefficients() {
|
||||||
|
|
||||||
p := float64(endIndex - startIndex)
|
p := float64(endIndex - startIndex)
|
||||||
|
|
||||||
xvalues := NewValueBufferWithCapacity(lrs.Len())
|
xvalues := sequence.NewBufferWithCapacity(lrs.Len())
|
||||||
for index := startIndex; index < endIndex; index++ {
|
for index := startIndex; index < endIndex; index++ {
|
||||||
x, _ := lrs.InnerSeries.GetValues(index)
|
x, _ := lrs.InnerSeries.GetValues(index)
|
||||||
xvalues.Enqueue(x)
|
xvalues.Enqueue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
lrs.avgx = Sequence{xvalues}.Average()
|
lrs.avgx = sequence.Seq{Provider: xvalues}.Average()
|
||||||
lrs.stddevx = Sequence{xvalues}.StdDev()
|
lrs.stddevx = sequence.Seq{Provider: xvalues}.StdDev()
|
||||||
|
|
||||||
var sumx, sumy, sumxx, sumxy float64
|
var sumx, sumy, sumxx, sumxy float64
|
||||||
for index := startIndex; index < endIndex; index++ {
|
for index := startIndex; index < endIndex; index++ {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
assert "github.com/blendlabs/go-assert"
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLinearRegressionSeries(t *testing.T) {
|
func TestLinearRegressionSeries(t *testing.T) {
|
||||||
|
@ -11,8 +12,8 @@ func TestLinearRegressionSeries(t *testing.T) {
|
||||||
|
|
||||||
mainSeries := ContinuousSeries{
|
mainSeries := ContinuousSeries{
|
||||||
Name: "A test series",
|
Name: "A test series",
|
||||||
XValues: Generate.Float64(1.0, 100.0),
|
XValues: sequence.Values(1.0, 100.0),
|
||||||
YValues: Generate.Float64(1.0, 100.0),
|
YValues: sequence.Values(1.0, 100.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
linRegSeries := &LinearRegressionSeries{
|
linRegSeries := &LinearRegressionSeries{
|
||||||
|
@ -33,8 +34,8 @@ func TestLinearRegressionSeriesDesc(t *testing.T) {
|
||||||
|
|
||||||
mainSeries := ContinuousSeries{
|
mainSeries := ContinuousSeries{
|
||||||
Name: "A test series",
|
Name: "A test series",
|
||||||
XValues: Generate.Float64(100.0, 1.0),
|
XValues: sequence.Values(100.0, 1.0),
|
||||||
YValues: Generate.Float64(100.0, 1.0),
|
YValues: sequence.Values(100.0, 1.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
linRegSeries := &LinearRegressionSeries{
|
linRegSeries := &LinearRegressionSeries{
|
||||||
|
@ -55,8 +56,8 @@ func TestLinearRegressionSeriesWindowAndOffset(t *testing.T) {
|
||||||
|
|
||||||
mainSeries := ContinuousSeries{
|
mainSeries := ContinuousSeries{
|
||||||
Name: "A test series",
|
Name: "A test series",
|
||||||
XValues: Generate.Float64(100.0, 1.0),
|
XValues: sequence.Values(100.0, 1.0),
|
||||||
YValues: Generate.Float64(100.0, 1.0),
|
YValues: sequence.Values(100.0, 1.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
linRegSeries := &LinearRegressionSeries{
|
linRegSeries := &LinearRegressionSeries{
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
"github.com/wcharczuk/go-chart/util"
|
"github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,17 +42,17 @@ func (mhr MarketHoursRange) IsZero() bool {
|
||||||
|
|
||||||
// GetMin returns the min value.
|
// GetMin returns the min value.
|
||||||
func (mhr MarketHoursRange) GetMin() float64 {
|
func (mhr MarketHoursRange) GetMin() float64 {
|
||||||
return Time.ToFloat64(mhr.Min)
|
return util.Time.ToFloat64(mhr.Min)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMax returns the max value.
|
// GetMax returns the max value.
|
||||||
func (mhr MarketHoursRange) GetMax() float64 {
|
func (mhr MarketHoursRange) GetMax() float64 {
|
||||||
return Time.ToFloat64(mhr.GetEffectiveMax())
|
return util.Time.ToFloat64(mhr.GetEffectiveMax())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEffectiveMax gets either the close on the max, or the max itself.
|
// GetEffectiveMax gets either the close on the max, or the max itself.
|
||||||
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
|
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
|
||||||
maxClose := Date.On(mhr.MarketClose, mhr.Max)
|
maxClose := util.Date.On(mhr.MarketClose, mhr.Max)
|
||||||
if maxClose.After(mhr.Max) {
|
if maxClose.After(mhr.Max) {
|
||||||
return maxClose
|
return maxClose
|
||||||
}
|
}
|
||||||
|
@ -60,13 +61,13 @@ func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
|
||||||
|
|
||||||
// SetMin sets the min value.
|
// SetMin sets the min value.
|
||||||
func (mhr *MarketHoursRange) SetMin(min float64) {
|
func (mhr *MarketHoursRange) SetMin(min float64) {
|
||||||
mhr.Min = Time.FromFloat64(min)
|
mhr.Min = util.Time.FromFloat64(min)
|
||||||
mhr.Min = mhr.Min.In(mhr.GetTimezone())
|
mhr.Min = mhr.Min.In(mhr.GetTimezone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMax sets the max value.
|
// SetMax sets the max value.
|
||||||
func (mhr *MarketHoursRange) SetMax(max float64) {
|
func (mhr *MarketHoursRange) SetMax(max float64) {
|
||||||
mhr.Max = Time.FromFloat64(max)
|
mhr.Max = util.Time.FromFloat64(max)
|
||||||
mhr.Max = mhr.Max.In(mhr.GetTimezone())
|
mhr.Max = mhr.Max.In(mhr.GetTimezone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,9 +89,9 @@ func (mhr *MarketHoursRange) SetDomain(domain int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
|
// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
|
||||||
func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
|
func (mhr MarketHoursRange) GetHolidayProvider() util.HolidayProvider {
|
||||||
if mhr.HolidayProvider == nil {
|
if mhr.HolidayProvider == nil {
|
||||||
return defaultHolidayProvider
|
return func(_ time.Time) bool { return false }
|
||||||
}
|
}
|
||||||
return mhr.HolidayProvider
|
return mhr.HolidayProvider
|
||||||
}
|
}
|
||||||
|
@ -98,7 +99,7 @@ func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
|
||||||
// GetMarketOpen returns the market open time.
|
// GetMarketOpen returns the market open time.
|
||||||
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
|
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
|
||||||
if mhr.MarketOpen.IsZero() {
|
if mhr.MarketOpen.IsZero() {
|
||||||
return NYSEOpen()
|
return util.NYSEOpen()
|
||||||
}
|
}
|
||||||
return mhr.MarketOpen
|
return mhr.MarketOpen
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ func (mhr MarketHoursRange) GetMarketOpen() time.Time {
|
||||||
// GetMarketClose returns the market close time.
|
// GetMarketClose returns the market close time.
|
||||||
func (mhr MarketHoursRange) GetMarketClose() time.Time {
|
func (mhr MarketHoursRange) GetMarketClose() time.Time {
|
||||||
if mhr.MarketClose.IsZero() {
|
if mhr.MarketClose.IsZero() {
|
||||||
return NYSEClose()
|
return util.NYSEClose()
|
||||||
}
|
}
|
||||||
return mhr.MarketClose
|
return mhr.MarketClose
|
||||||
}
|
}
|
||||||
|
@ -114,31 +115,31 @@ func (mhr MarketHoursRange) GetMarketClose() time.Time {
|
||||||
// GetTicks returns the ticks for the range.
|
// GetTicks returns the ticks for the range.
|
||||||
// This is to override the default continous ticks that would be generated for the range.
|
// This is to override the default continous ticks that would be generated for the range.
|
||||||
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
|
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
|
||||||
times := Generate.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times := sequence.Time.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth := mhr.measureTimes(r, defaults, vf, times)
|
timesWidth := mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
}
|
}
|
||||||
|
|
||||||
times = Generate.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times = sequence.Time.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
}
|
}
|
||||||
|
|
||||||
times = Generate.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times = sequence.Time.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
}
|
}
|
||||||
|
|
||||||
times = Generate.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times = sequence.Time.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
}
|
}
|
||||||
|
|
||||||
times = Generate.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times = sequence.Time.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
|
@ -167,7 +168,7 @@ func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []T
|
||||||
ticks := make([]Tick, len(times))
|
ticks := make([]Tick, len(times))
|
||||||
for index, t := range times {
|
for index, t := range times {
|
||||||
ticks[index] = Tick{
|
ticks[index] = Tick{
|
||||||
Value: Time.ToFloat64(t),
|
Value: util.Time.ToFloat64(t),
|
||||||
Label: vf(t),
|
Label: vf(t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,10 +181,10 @@ func (mhr MarketHoursRange) String() string {
|
||||||
|
|
||||||
// Translate maps a given value into the ContinuousRange space.
|
// Translate maps a given value into the ContinuousRange space.
|
||||||
func (mhr MarketHoursRange) Translate(value float64) int {
|
func (mhr MarketHoursRange) Translate(value float64) int {
|
||||||
valueTime := Time.FromFloat64(value)
|
valueTime := util.Time.FromFloat64(value)
|
||||||
valueTimeEastern := valueTime.In(Date.Eastern())
|
valueTimeEastern := valueTime.In(util.Date.Eastern())
|
||||||
totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
|
totalSeconds := util.Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
|
||||||
valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
|
valueDelta := util.Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
|
||||||
translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))
|
translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))
|
||||||
|
|
||||||
if mhr.IsDescending() {
|
if mhr.IsDescending() {
|
||||||
|
|
|
@ -5,17 +5,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
assert "github.com/blendlabs/go-assert"
|
assert "github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarketHoursRangeGetDelta(t *testing.T) {
|
func TestMarketHoursRangeGetDelta(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
r := &MarketHoursRange{
|
r := &MarketHoursRange{
|
||||||
Min: time.Date(2016, 07, 19, 9, 30, 0, 0, Date.Eastern()),
|
Min: time.Date(2016, 07, 19, 9, 30, 0, 0, util.Date.Eastern()),
|
||||||
Max: time.Date(2016, 07, 22, 16, 00, 0, 0, Date.Eastern()),
|
Max: time.Date(2016, 07, 22, 16, 00, 0, 0, util.Date.Eastern()),
|
||||||
MarketOpen: NYSEOpen(),
|
MarketOpen: util.NYSEOpen(),
|
||||||
MarketClose: NYSEClose(),
|
MarketClose: util.NYSEClose(),
|
||||||
HolidayProvider: Date.IsNYSEHoliday,
|
HolidayProvider: util.Date.IsNYSEHoliday,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NotZero(r.GetDelta())
|
assert.NotZero(r.GetDelta())
|
||||||
|
@ -25,19 +26,19 @@ func TestMarketHoursRangeTranslate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
r := &MarketHoursRange{
|
r := &MarketHoursRange{
|
||||||
Min: time.Date(2016, 07, 18, 9, 30, 0, 0, Date.Eastern()),
|
Min: time.Date(2016, 07, 18, 9, 30, 0, 0, util.Date.Eastern()),
|
||||||
Max: time.Date(2016, 07, 22, 16, 00, 0, 0, Date.Eastern()),
|
Max: time.Date(2016, 07, 22, 16, 00, 0, 0, util.Date.Eastern()),
|
||||||
MarketOpen: NYSEOpen(),
|
MarketOpen: util.NYSEOpen(),
|
||||||
MarketClose: NYSEClose(),
|
MarketClose: util.NYSEClose(),
|
||||||
HolidayProvider: Date.IsNYSEHoliday,
|
HolidayProvider: util.Date.IsNYSEHoliday,
|
||||||
Domain: 1000,
|
Domain: 1000,
|
||||||
}
|
}
|
||||||
|
|
||||||
weds := time.Date(2016, 07, 20, 9, 30, 0, 0, Date.Eastern())
|
weds := time.Date(2016, 07, 20, 9, 30, 0, 0, util.Date.Eastern())
|
||||||
|
|
||||||
assert.Equal(0, r.Translate(Time.ToFloat64(r.Min)))
|
assert.Equal(0, r.Translate(util.Time.ToFloat64(r.Min)))
|
||||||
assert.Equal(400, r.Translate(Time.ToFloat64(weds)))
|
assert.Equal(400, r.Translate(util.Time.ToFloat64(weds)))
|
||||||
assert.Equal(1000, r.Translate(Time.ToFloat64(r.Max)))
|
assert.Equal(1000, r.Translate(util.Time.ToFloat64(r.Max)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarketHoursRangeGetTicks(t *testing.T) {
|
func TestMarketHoursRangeGetTicks(t *testing.T) {
|
||||||
|
@ -56,17 +57,17 @@ func TestMarketHoursRangeGetTicks(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ra := &MarketHoursRange{
|
ra := &MarketHoursRange{
|
||||||
Min: Date.On(NYSEOpen(), Date.Date(2016, 07, 18, Date.Eastern())),
|
Min: util.Date.On(util.NYSEOpen(), util.Date.Date(2016, 07, 18, util.Date.Eastern())),
|
||||||
Max: Date.On(NYSEClose(), Date.Date(2016, 07, 22, Date.Eastern())),
|
Max: util.Date.On(util.NYSEClose(), util.Date.Date(2016, 07, 22, util.Date.Eastern())),
|
||||||
MarketOpen: NYSEOpen(),
|
MarketOpen: util.NYSEOpen(),
|
||||||
MarketClose: NYSEClose(),
|
MarketClose: util.NYSEClose(),
|
||||||
HolidayProvider: Date.IsNYSEHoliday,
|
HolidayProvider: util.Date.IsNYSEHoliday,
|
||||||
Domain: 1024,
|
Domain: 1024,
|
||||||
}
|
}
|
||||||
|
|
||||||
ticks := ra.GetTicks(r, defaults, TimeValueFormatter)
|
ticks := ra.GetTicks(r, defaults, TimeValueFormatter)
|
||||||
assert.NotEmpty(ticks)
|
assert.NotEmpty(ticks)
|
||||||
assert.Len(ticks, 5)
|
assert.Len(ticks, 5)
|
||||||
assert.NotEqual(Time.ToFloat64(ra.Min), ticks[0].Value)
|
assert.NotEqual(util.Time.ToFloat64(ra.Min), ticks[0].Value)
|
||||||
assert.NotEmpty(ticks[0].Label)
|
assert.NotEmpty(ticks[0].Label)
|
||||||
}
|
}
|
||||||
|
|
25
pie_chart.go
25
pie_chart.go
|
@ -4,8 +4,15 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_pi2 = math.Pi / 2.0
|
||||||
|
_pi4 = math.Pi / 4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
// PieChart is a chart that draws sections of a circle based on percentages.
|
// PieChart is a chart that draws sections of a circle based on percentages.
|
||||||
|
@ -123,7 +130,7 @@ func (pc PieChart) drawTitle(r Renderer) {
|
||||||
|
|
||||||
func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
||||||
cx, cy := canvasBox.Center()
|
cx, cy := canvasBox.Center()
|
||||||
diameter := Math.MinInt(canvasBox.Width(), canvasBox.Height())
|
diameter := util.Math.MinInt(canvasBox.Width(), canvasBox.Height())
|
||||||
radius := float64(diameter >> 1)
|
radius := float64(diameter >> 1)
|
||||||
labelRadius := (radius * 2.0) / 3.0
|
labelRadius := (radius * 2.0) / 3.0
|
||||||
|
|
||||||
|
@ -134,8 +141,8 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
||||||
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
|
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
|
||||||
|
|
||||||
r.MoveTo(cx, cy)
|
r.MoveTo(cx, cy)
|
||||||
rads = Math.PercentToRadians(total)
|
rads = util.Math.PercentToRadians(total)
|
||||||
delta = Math.PercentToRadians(v.Value)
|
delta = util.Math.PercentToRadians(v.Value)
|
||||||
|
|
||||||
r.ArcTo(cx, cy, radius, radius, rads, delta)
|
r.ArcTo(cx, cy, radius, radius, rads, delta)
|
||||||
|
|
||||||
|
@ -150,9 +157,9 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
|
||||||
for index, v := range values {
|
for index, v := range values {
|
||||||
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
|
v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r)
|
||||||
if len(v.Label) > 0 {
|
if len(v.Label) > 0 {
|
||||||
delta2 = Math.PercentToRadians(total + (v.Value / 2.0))
|
delta2 = util.Math.PercentToRadians(total + (v.Value / 2.0))
|
||||||
delta2 = Math.RadianAdd(delta2, _pi2)
|
delta2 = util.Math.RadianAdd(delta2, _pi2)
|
||||||
lx, ly = Math.CirclePoint(cx, cy, labelRadius, delta2)
|
lx, ly = util.Math.CirclePoint(cx, cy, labelRadius, delta2)
|
||||||
|
|
||||||
tb := r.MeasureText(v.Label)
|
tb := r.MeasureText(v.Label)
|
||||||
lx = lx - (tb.Width() >> 1)
|
lx = lx - (tb.Width() >> 1)
|
||||||
|
@ -177,7 +184,7 @@ func (pc PieChart) getDefaultCanvasBox() Box {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
|
func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
|
||||||
circleDiameter := Math.MinInt(canvasBox.Width(), canvasBox.Height())
|
circleDiameter := util.Math.MinInt(canvasBox.Width(), canvasBox.Height())
|
||||||
|
|
||||||
square := Box{
|
square := Box{
|
||||||
Right: circleDiameter,
|
Right: circleDiameter,
|
||||||
|
@ -223,7 +230,7 @@ func (pc PieChart) stylePieChartValue(index int) Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) getScaledFontSize() float64 {
|
func (pc PieChart) getScaledFontSize() float64 {
|
||||||
effectiveDimension := Math.MinInt(pc.GetWidth(), pc.GetHeight())
|
effectiveDimension := util.Math.MinInt(pc.GetWidth(), pc.GetHeight())
|
||||||
if effectiveDimension >= 2048 {
|
if effectiveDimension >= 2048 {
|
||||||
return 48.0
|
return 48.0
|
||||||
} else if effectiveDimension >= 1024 {
|
} else if effectiveDimension >= 1024 {
|
||||||
|
@ -262,7 +269,7 @@ func (pc PieChart) styleDefaultsTitle() Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) getTitleFontSize() float64 {
|
func (pc PieChart) getTitleFontSize() float64 {
|
||||||
effectiveDimension := Math.MinInt(pc.GetWidth(), pc.GetHeight())
|
effectiveDimension := util.Math.MinInt(pc.GetWidth(), pc.GetHeight())
|
||||||
if effectiveDimension >= 2048 {
|
if effectiveDimension >= 2048 {
|
||||||
return 48
|
return 48
|
||||||
} else if effectiveDimension >= 1024 {
|
} else if effectiveDimension >= 1024 {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/matrix"
|
"github.com/wcharczuk/go-chart/matrix"
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PolynomialRegressionSeries implements a polynomial regression over a given
|
// PolynomialRegressionSeries implements a polynomial regression over a given
|
||||||
|
@ -39,7 +40,7 @@ func (prs PolynomialRegressionSeries) GetYAxis() YAxisType {
|
||||||
|
|
||||||
// Len returns the number of elements in the series.
|
// Len returns the number of elements in the series.
|
||||||
func (prs PolynomialRegressionSeries) Len() int {
|
func (prs PolynomialRegressionSeries) Len() int {
|
||||||
return Math.MinInt(prs.GetLimit(), prs.InnerSeries.Len()-prs.GetOffset())
|
return util.Math.MinInt(prs.GetLimit(), prs.InnerSeries.Len()-prs.GetOffset())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLimit returns the window size.
|
// GetLimit returns the window size.
|
||||||
|
@ -54,7 +55,7 @@ func (prs PolynomialRegressionSeries) GetLimit() int {
|
||||||
func (prs PolynomialRegressionSeries) GetEndIndex() int {
|
func (prs PolynomialRegressionSeries) GetEndIndex() int {
|
||||||
windowEnd := prs.GetOffset() + prs.GetLimit()
|
windowEnd := prs.GetOffset() + prs.GetLimit()
|
||||||
innerSeriesLastIndex := prs.InnerSeries.Len() - 1
|
innerSeriesLastIndex := prs.InnerSeries.Len() - 1
|
||||||
return Math.MinInt(windowEnd, innerSeriesLastIndex)
|
return util.Math.MinInt(windowEnd, innerSeriesLastIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOffset returns the data offset.
|
// GetOffset returns the data offset.
|
||||||
|
@ -94,7 +95,7 @@ func (prs *PolynomialRegressionSeries) GetValues(index int) (x, y float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := prs.GetOffset()
|
offset := prs.GetOffset()
|
||||||
effectiveIndex := Math.MinInt(index+offset, prs.InnerSeries.Len())
|
effectiveIndex := util.Math.MinInt(index+offset, prs.InnerSeries.Len())
|
||||||
x, y = prs.InnerSeries.GetValues(effectiveIndex)
|
x, y = prs.InnerSeries.GetValues(effectiveIndex)
|
||||||
y = prs.apply(x)
|
y = prs.apply(x)
|
||||||
return
|
return
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
util "github.com/blendlabs/go-util"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/drawing"
|
"github.com/wcharczuk/go-chart/drawing"
|
||||||
)
|
)
|
||||||
|
@ -191,7 +192,7 @@ func (rr *rasterRenderer) MeasureText(body string) Box {
|
||||||
return textBox
|
return textBox
|
||||||
}
|
}
|
||||||
|
|
||||||
return textBox.Corners().Rotate(Math.RadiansToDegrees(*rr.rotateRadians)).Box()
|
return textBox.Corners().Rotate(util.Math.RadiansToDegrees(*rr.rotateRadians)).Box()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTextRotation sets a text rotation.
|
// SetTextRotation sets a text rotation.
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
package sequence
|
package sequence
|
||||||
|
|
||||||
|
// Values returns the array values of a linear sequence with a given start, end and optional step.
|
||||||
|
func Values(start, end float64) []float64 {
|
||||||
|
return Seq{NewLinear().WithOffset(start).WithLimit(end).WithStep(1.0)}.Array()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValuesWithStep returns the array values of a linear sequence with a given start, end and optional step.
|
||||||
|
func ValuesWithStep(start, end, step float64) []float64 {
|
||||||
|
return Seq{NewLinear().WithOffset(start).WithLimit(end).WithStep(step)}.Array()
|
||||||
|
}
|
||||||
|
|
||||||
// NewLinear returns a new linear generator.
|
// NewLinear returns a new linear generator.
|
||||||
func NewLinear() *Linear {
|
func NewLinear() *Linear {
|
||||||
return &Linear{}
|
return &Linear{}
|
||||||
|
|
|
@ -3,8 +3,26 @@ package sequence
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RandomValues returns an array of random values.
|
||||||
|
func RandomValues(count int) []float64 {
|
||||||
|
return Seq{NewRandom().WithLen(count)}.Array()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomValuesWithAverage returns an array of random values with a given average.
|
||||||
|
func RandomValuesWithAverage(average float64, count int) []float64 {
|
||||||
|
return Seq{NewRandom().WithAverage(average).WithLen(count)}.Array()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRandom creates a new random sequence.
|
||||||
|
func NewRandom() *Random {
|
||||||
|
return &Random{
|
||||||
|
rnd: rand.New(rand.NewSource(time.Now().Unix())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Random is a random number sequence generator.
|
// Random is a random number sequence generator.
|
||||||
type Random struct {
|
type Random struct {
|
||||||
rnd *rand.Rand
|
rnd *rand.Rand
|
||||||
|
|
|
@ -13,6 +13,15 @@ type Seq struct {
|
||||||
Provider
|
Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Array enumerates the sequence into a slice.
|
||||||
|
func (s Seq) Array() (output []float64) {
|
||||||
|
output = make([]float64, s.Len())
|
||||||
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
output[i] = s.GetValue(i)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Each applies the `mapfn` to all values in the value provider.
|
// Each applies the `mapfn` to all values in the value provider.
|
||||||
func (s Seq) Each(mapfn func(int, float64)) {
|
func (s Seq) Each(mapfn func(int, float64)) {
|
||||||
for i := 0; i < s.Len(); i++ {
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultSimpleMovingAveragePeriod is the default number of values to average.
|
// DefaultSimpleMovingAveragePeriod is the default number of values to average.
|
||||||
|
@ -74,7 +78,7 @@ func (sma SMASeries) GetLastValues() (x, y float64) {
|
||||||
|
|
||||||
func (sma SMASeries) getAverage(index int) float64 {
|
func (sma SMASeries) getAverage(index int) float64 {
|
||||||
period := sma.GetPeriod()
|
period := sma.GetPeriod()
|
||||||
floor := Math.MaxInt(0, index-period)
|
floor := util.Math.MaxInt(0, index-period)
|
||||||
var accum float64
|
var accum float64
|
||||||
var count float64
|
var count float64
|
||||||
for x := index; x >= floor; x-- {
|
for x := index; x >= floor; x-- {
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockValuesProvider struct {
|
type mockValuesProvider struct {
|
||||||
|
@ -12,14 +14,14 @@ type mockValuesProvider struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mockValuesProvider) Len() int {
|
func (m mockValuesProvider) Len() int {
|
||||||
return Math.MinInt(len(m.X), len(m.Y))
|
return util.Math.MinInt(len(m.X), len(m.Y))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mockValuesProvider) GetValues(index int) (x, y float64) {
|
func (m mockValuesProvider) GetValues(index int) (x, y float64) {
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
panic("negative index at GetValue()")
|
panic("negative index at GetValue()")
|
||||||
}
|
}
|
||||||
if index > Math.MinInt(len(m.X), len(m.Y)) {
|
if index > util.Math.MinInt(len(m.X), len(m.Y)) {
|
||||||
panic("index is outside the length of m.X or m.Y")
|
panic("index is outside the length of m.X or m.Y")
|
||||||
}
|
}
|
||||||
x = m.X[index]
|
x = m.X[index]
|
||||||
|
@ -31,8 +33,8 @@ func TestSMASeriesGetValue(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
mockSeries := mockValuesProvider{
|
mockSeries := mockValuesProvider{
|
||||||
Generate.Float64(1.0, 10.0),
|
sequence.Values(1.0, 10.0),
|
||||||
Generate.Float64(10, 1.0),
|
sequence.Values(10, 1.0),
|
||||||
}
|
}
|
||||||
assert.Equal(10, mockSeries.Len())
|
assert.Equal(10, mockSeries.Len())
|
||||||
|
|
||||||
|
@ -62,8 +64,8 @@ func TestSMASeriesGetLastValueWindowOverlap(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
mockSeries := mockValuesProvider{
|
mockSeries := mockValuesProvider{
|
||||||
Generate.Float64(1.0, 10.0),
|
sequence.Values(1.0, 10.0),
|
||||||
Generate.Float64(10, 1.0),
|
sequence.Values(10, 1.0),
|
||||||
}
|
}
|
||||||
assert.Equal(10, mockSeries.Len())
|
assert.Equal(10, mockSeries.Len())
|
||||||
|
|
||||||
|
@ -88,8 +90,8 @@ func TestSMASeriesGetLastValue(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
mockSeries := mockValuesProvider{
|
mockSeries := mockValuesProvider{
|
||||||
Generate.Float64(1.0, 100.0),
|
sequence.Values(1.0, 100.0),
|
||||||
Generate.Float64(100, 1.0),
|
sequence.Values(100, 1.0),
|
||||||
}
|
}
|
||||||
assert.Equal(100, mockSeries.Len())
|
assert.Equal(100, mockSeries.Len())
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
|
"github.com/wcharczuk/go-chart/sequence"
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StackedBar is a bar within a StackedBarChart.
|
// StackedBar is a bar within a StackedBarChart.
|
||||||
|
@ -145,7 +147,7 @@ func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar S
|
||||||
Top: yoffset,
|
Top: yoffset,
|
||||||
Left: bxl,
|
Left: bxl,
|
||||||
Right: bxr,
|
Right: bxr,
|
||||||
Bottom: Math.MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth),
|
Bottom: util.Math.MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth),
|
||||||
}
|
}
|
||||||
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
Draw.Box(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
||||||
yoffset += barHeight
|
yoffset += barHeight
|
||||||
|
@ -200,7 +202,7 @@ func (sbc StackedBarChart) drawYAxis(r Renderer, canvasBox Box) {
|
||||||
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
|
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
|
||||||
r.Stroke()
|
r.Stroke()
|
||||||
|
|
||||||
ticks := Generate.Float64(1.0, 0.0, 0.2)
|
ticks := sequence.Seq{Provider: sequence.NewLinear().WithLimit(1.0).WithStep(0.2)}.Array()
|
||||||
for _, t := range ticks {
|
for _, t := range ticks {
|
||||||
axisStyle.GetStrokeOptions().WriteToRenderer(r)
|
axisStyle.GetStrokeOptions().WriteToRenderer(r)
|
||||||
ty := canvasBox.Bottom - int(t*float64(canvasBox.Height()))
|
ty := canvasBox.Bottom - int(t*float64(canvasBox.Height()))
|
||||||
|
@ -252,7 +254,7 @@ func (sbc StackedBarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box) Box {
|
||||||
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
|
lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle)
|
||||||
linesBox := Text.MeasureLines(r, lines, axisStyle)
|
linesBox := Text.MeasureLines(r, lines, axisStyle)
|
||||||
|
|
||||||
xaxisHeight = Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
|
xaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Box{
|
return Box{
|
||||||
|
@ -304,7 +306,7 @@ func (sbc StackedBarChart) styleDefaultsTitle() Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sbc StackedBarChart) getTitleFontSize() float64 {
|
func (sbc StackedBarChart) getTitleFontSize() float64 {
|
||||||
effectiveDimension := Math.MinInt(sbc.GetWidth(), sbc.GetHeight())
|
effectiveDimension := util.Math.MinInt(sbc.GetWidth(), sbc.GetHeight())
|
||||||
if effectiveDimension >= 2048 {
|
if effectiveDimension >= 2048 {
|
||||||
return 48
|
return 48
|
||||||
} else if effectiveDimension >= 1024 {
|
} else if effectiveDimension >= 1024 {
|
||||||
|
|
3
style.go
3
style.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
util "github.com/blendlabs/go-util"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/drawing"
|
"github.com/wcharczuk/go-chart/drawing"
|
||||||
)
|
)
|
||||||
|
@ -330,7 +331,7 @@ func (s Style) WriteToRenderer(r Renderer) {
|
||||||
|
|
||||||
r.ClearTextRotation()
|
r.ClearTextRotation()
|
||||||
if s.GetTextRotationDegrees() != 0 {
|
if s.GetTextRotationDegrees() != 0 {
|
||||||
r.SetTextRotation(Math.DegreesToRadians(s.GetTextRotationDegrees()))
|
r.SetTextRotation(util.Math.DegreesToRadians(s.GetTextRotationDegrees()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
text.go
8
text.go
|
@ -1,6 +1,10 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
// TextHorizontalAlign is an enum for the horizontal alignment options.
|
// TextHorizontalAlign is an enum for the horizontal alignment options.
|
||||||
type TextHorizontalAlign int
|
type TextHorizontalAlign int
|
||||||
|
@ -145,7 +149,7 @@ func (t text) MeasureLines(r Renderer, lines []string, style Style) Box {
|
||||||
var output Box
|
var output Box
|
||||||
for index, line := range lines {
|
for index, line := range lines {
|
||||||
lineBox := r.MeasureText(line)
|
lineBox := r.MeasureText(line)
|
||||||
output.Right = Math.MaxInt(lineBox.Right, output.Right)
|
output.Right = util.Math.MaxInt(lineBox.Right, output.Right)
|
||||||
output.Bottom += lineBox.Height()
|
output.Bottom += lineBox.Height()
|
||||||
if index < len(lines)-1 {
|
if index < len(lines)-1 {
|
||||||
output.Bottom += +style.GetTextLineSpacing()
|
output.Bottom += +style.GetTextLineSpacing()
|
||||||
|
|
10
tick.go
10
tick.go
|
@ -4,6 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TicksProvider is a type that provides ticks.
|
// TicksProvider is a type that provides ticks.
|
||||||
|
@ -83,15 +85,15 @@ func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style,
|
||||||
rangeDelta := math.Abs(max - min)
|
rangeDelta := math.Abs(max - min)
|
||||||
tickStep := rangeDelta / float64(intermediateTickCount)
|
tickStep := rangeDelta / float64(intermediateTickCount)
|
||||||
|
|
||||||
roundTo := Math.GetRoundToForDelta(rangeDelta) / 10
|
roundTo := util.Math.GetRoundToForDelta(rangeDelta) / 10
|
||||||
intermediateTickCount = Math.MinInt(intermediateTickCount, 1<<10)
|
intermediateTickCount = util.Math.MinInt(intermediateTickCount, 1<<10)
|
||||||
|
|
||||||
for x := 1; x < intermediateTickCount; x++ {
|
for x := 1; x < intermediateTickCount; x++ {
|
||||||
var tickValue float64
|
var tickValue float64
|
||||||
if ra.IsDescending() {
|
if ra.IsDescending() {
|
||||||
tickValue = max - Math.RoundUp(tickStep*float64(x), roundTo)
|
tickValue = max - util.Math.RoundUp(tickStep*float64(x), roundTo)
|
||||||
} else {
|
} else {
|
||||||
tickValue = min + Math.RoundUp(tickStep*float64(x), roundTo)
|
tickValue = min + util.Math.RoundUp(tickStep*float64(x), roundTo)
|
||||||
}
|
}
|
||||||
ticks = append(ticks, Tick{
|
ticks = append(ticks, Tick{
|
||||||
Value: tickValue,
|
Value: tickValue,
|
||||||
|
|
|
@ -3,6 +3,8 @@ package chart
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TimeSeries is a line on a chart.
|
// TimeSeries is a line on a chart.
|
||||||
|
@ -33,14 +35,14 @@ func (ts TimeSeries) Len() int {
|
||||||
|
|
||||||
// GetValues gets a value at a given index.
|
// GetValues gets a value at a given index.
|
||||||
func (ts TimeSeries) GetValues(index int) (x, y float64) {
|
func (ts TimeSeries) GetValues(index int) (x, y float64) {
|
||||||
x = Time.ToFloat64(ts.XValues[index])
|
x = util.Time.ToFloat64(ts.XValues[index])
|
||||||
y = ts.YValues[index]
|
y = ts.YValues[index]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLastValues gets the last value.
|
// GetLastValues gets the last value.
|
||||||
func (ts TimeSeries) GetLastValues() (x, y float64) {
|
func (ts TimeSeries) GetLastValues() (x, y float64) {
|
||||||
x = Time.ToFloat64(ts.XValues[len(ts.XValues)-1])
|
x = util.Time.ToFloat64(ts.XValues[len(ts.XValues)-1])
|
||||||
y = ts.YValues[len(ts.YValues)-1]
|
y = ts.YValues[len(ts.YValues)-1]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
6
value.go
6
value.go
|
@ -1,5 +1,7 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
|
import util "github.com/blendlabs/go-util"
|
||||||
|
|
||||||
// Value is a chart value.
|
// Value is a chart value.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
Style Style
|
Style Style
|
||||||
|
@ -21,7 +23,7 @@ func (vs Values) Values() []float64 {
|
||||||
|
|
||||||
// ValuesNormalized returns normalized values.
|
// ValuesNormalized returns normalized values.
|
||||||
func (vs Values) ValuesNormalized() []float64 {
|
func (vs Values) ValuesNormalized() []float64 {
|
||||||
return Math.Normalize(vs.Values()...)
|
return util.Math.Normalize(vs.Values()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize returns the values normalized.
|
// Normalize returns the values normalized.
|
||||||
|
@ -38,7 +40,7 @@ func (vs Values) Normalize() []Value {
|
||||||
output = append(output, Value{
|
output = append(output, Value{
|
||||||
Style: v.Style,
|
Style: v.Style,
|
||||||
Label: v.Label,
|
Label: v.Label,
|
||||||
Value: Math.RoundDown(v.Value/total, 0.0001),
|
Value: util.Math.RoundDown(v.Value/total, 0.0001),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/blendlabs/go-assert"
|
"github.com/blendlabs/go-assert"
|
||||||
|
"github.com/wcharczuk/go-chart/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTimeValueFormatterWithFormat(t *testing.T) {
|
func TestTimeValueFormatterWithFormat(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
d := time.Now()
|
d := time.Now()
|
||||||
di := Time.ToFloat64(d)
|
di := util.Time.ToFloat64(d)
|
||||||
df := float64(di)
|
df := float64(di)
|
||||||
|
|
||||||
s := formatTime(d, DefaultDateFormat)
|
s := formatTime(d, DefaultDateFormat)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/image/font"
|
"golang.org/x/image/font"
|
||||||
|
|
||||||
|
util "github.com/blendlabs/go-util"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/drawing"
|
"github.com/wcharczuk/go-chart/drawing"
|
||||||
)
|
)
|
||||||
|
@ -89,8 +90,8 @@ func (vr *vectorRenderer) QuadCurveTo(cx, cy, x, y int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
|
func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
|
||||||
startAngle = Math.RadianAdd(startAngle, _pi2)
|
startAngle = util.Math.RadianAdd(startAngle, _pi2)
|
||||||
endAngle := Math.RadianAdd(startAngle, delta)
|
endAngle := util.Math.RadianAdd(startAngle, delta)
|
||||||
|
|
||||||
startx := cx + int(rx*math.Sin(startAngle))
|
startx := cx + int(rx*math.Sin(startAngle))
|
||||||
starty := cy - int(ry*math.Cos(startAngle))
|
starty := cy - int(ry*math.Cos(startAngle))
|
||||||
|
@ -104,7 +105,7 @@ func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
|
||||||
endx := cx + int(rx*math.Sin(endAngle))
|
endx := cx + int(rx*math.Sin(endAngle))
|
||||||
endy := cy - int(ry*math.Cos(endAngle))
|
endy := cy - int(ry*math.Cos(endAngle))
|
||||||
|
|
||||||
dd := Math.RadiansToDegrees(delta)
|
dd := util.Math.RadiansToDegrees(delta)
|
||||||
|
|
||||||
vr.p = append(vr.p, fmt.Sprintf("A %d %d %0.2f 0 1 %d %d", int(rx), int(ry), dd, endx, endy))
|
vr.p = append(vr.p, fmt.Sprintf("A %d %d %0.2f 0 1 %d %d", int(rx), int(ry), dd, endx, endy))
|
||||||
}
|
}
|
||||||
|
@ -176,7 +177,7 @@ func (vr *vectorRenderer) MeasureText(body string) (box Box) {
|
||||||
if vr.c.textTheta == nil {
|
if vr.c.textTheta == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
box = box.Corners().Rotate(Math.RadiansToDegrees(*vr.c.textTheta)).Box()
|
box = box.Corners().Rotate(util.Math.RadiansToDegrees(*vr.c.textTheta)).Box()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -231,7 +232,7 @@ func (c *canvas) Text(x, y int, body string, style Style) {
|
||||||
if c.textTheta == nil {
|
if c.textTheta == nil {
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
|
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
|
||||||
} else {
|
} else {
|
||||||
transform := fmt.Sprintf(` transform="rotate(%0.2f,%d,%d)"`, Math.RadiansToDegrees(*c.textTheta), x, y)
|
transform := fmt.Sprintf(` transform="rotate(%0.2f,%d,%d)"`, util.Math.RadiansToDegrees(*c.textTheta), x, y)
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s"%s>%s</text>`, x, y, c.styleAsSVG(style), transform, body)))
|
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s"%s>%s</text>`, x, y, c.styleAsSVG(style), transform, body)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
xaxis.go
16
xaxis.go
|
@ -1,6 +1,10 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
// XAxis represents the horizontal axis.
|
// XAxis represents the horizontal axis.
|
||||||
type XAxis struct {
|
type XAxis struct {
|
||||||
|
@ -101,9 +105,9 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
left = Math.MinInt(left, ltx)
|
left = util.Math.MinInt(left, ltx)
|
||||||
right = Math.MaxInt(right, rtx)
|
right = util.Math.MaxInt(right, rtx)
|
||||||
bottom = Math.MaxInt(bottom, ty)
|
bottom = util.Math.MaxInt(bottom, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
if xa.NameStyle.Show && len(xa.Name) > 0 {
|
if xa.NameStyle.Show && len(xa.Name) > 0 {
|
||||||
|
@ -155,7 +159,7 @@ func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
|
||||||
ty = canvasBox.Bottom + (2 * DefaultXAxisMargin)
|
ty = canvasBox.Bottom + (2 * DefaultXAxisMargin)
|
||||||
}
|
}
|
||||||
Draw.Text(r, t.Label, tx, ty, tickWithAxisStyle)
|
Draw.Text(r, t.Label, tx, ty, tickWithAxisStyle)
|
||||||
maxTextHeight = Math.MaxInt(maxTextHeight, tb.Height())
|
maxTextHeight = util.Math.MaxInt(maxTextHeight, tb.Height())
|
||||||
break
|
break
|
||||||
case TickPositionBetweenTicks:
|
case TickPositionBetweenTicks:
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
|
@ -171,7 +175,7 @@ func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
|
||||||
}, finalTickStyle)
|
}, finalTickStyle)
|
||||||
|
|
||||||
ftb := Text.MeasureLines(r, Text.WrapFit(r, t.Label, tx-ltx, finalTickStyle), finalTickStyle)
|
ftb := Text.MeasureLines(r, Text.WrapFit(r, t.Label, tx-ltx, finalTickStyle), finalTickStyle)
|
||||||
maxTextHeight = Math.MaxInt(maxTextHeight, ftb.Height())
|
maxTextHeight = util.Math.MaxInt(maxTextHeight, ftb.Height())
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
18
yaxis.go
18
yaxis.go
|
@ -1,6 +1,10 @@
|
||||||
package chart
|
package chart
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
util "github.com/wcharczuk/go-chart/util"
|
||||||
|
)
|
||||||
|
|
||||||
// YAxis is a veritcal rule of the range.
|
// YAxis is a veritcal rule of the range.
|
||||||
// There can be (2) y-axes; a primary and secondary.
|
// There can be (2) y-axes; a primary and secondary.
|
||||||
|
@ -101,18 +105,18 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
finalTextX = tx - tb.Width()
|
finalTextX = tx - tb.Width()
|
||||||
}
|
}
|
||||||
|
|
||||||
maxTextHeight = Math.MaxInt(tb.Height(), maxTextHeight)
|
maxTextHeight = util.Math.MaxInt(tb.Height(), maxTextHeight)
|
||||||
|
|
||||||
if ya.AxisType == YAxisPrimary {
|
if ya.AxisType == YAxisPrimary {
|
||||||
minx = canvasBox.Right
|
minx = canvasBox.Right
|
||||||
maxx = Math.MaxInt(maxx, tx+tb.Width())
|
maxx = util.Math.MaxInt(maxx, tx+tb.Width())
|
||||||
} else if ya.AxisType == YAxisSecondary {
|
} else if ya.AxisType == YAxisSecondary {
|
||||||
minx = Math.MinInt(minx, finalTextX)
|
minx = util.Math.MinInt(minx, finalTextX)
|
||||||
maxx = Math.MaxInt(maxx, tx)
|
maxx = util.Math.MaxInt(maxx, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
miny = Math.MinInt(miny, ly-tbh2)
|
miny = util.Math.MinInt(miny, ly-tbh2)
|
||||||
maxy = Math.MaxInt(maxy, ly+tbh2)
|
maxy = util.Math.MaxInt(maxy, ly+tbh2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ya.NameStyle.Show && len(ya.Name) > 0 {
|
if ya.NameStyle.Show && len(ya.Name) > 0 {
|
||||||
|
|
Loading…
Reference in a new issue