diff --git a/bollinger_band_series.go b/bollinger_band_series.go index 18cd60e..9173825 100644 --- a/bollinger_band_series.go +++ b/bollinger_band_series.go @@ -3,7 +3,7 @@ package chart import ( "fmt" - "github.com/wcharczuk/go-chart/util" + "github.com/wcharczuk/go-chart/sequence" ) // BollingerBandsSeries draws bollinger bands for an inner series. @@ -17,7 +17,7 @@ type BollingerBandsSeries struct { K float64 InnerSeries ValuesProvider - valueBuffer *util.ValueBuffer + valueBuffer *sequence.Buffer } // GetName returns the name of the time series. @@ -67,7 +67,7 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64) return } if bbs.valueBuffer == nil || index == 0 { - bbs.valueBuffer = util.NewValueBufferWithCapacity(bbs.GetPeriod()) + bbs.valueBuffer = sequence.NewBufferWithCapacity(bbs.GetPeriod()) } if bbs.valueBuffer.Len() >= bbs.GetPeriod() { bbs.valueBuffer.Dequeue() @@ -76,8 +76,8 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64) bbs.valueBuffer.Enqueue(py) x = px - ay := util.Sequence{bbs.valueBuffer}.Average() - std := util.Sequence{bbs.valueBuffer}.StdDev() + ay := sequence.Seq{Provider: bbs.valueBuffer}.Average() + std := sequence.Seq{Provider: bbs.valueBuffer}.StdDev() y1 = ay + (bbs.GetK() * std) y2 = ay - (bbs.GetK() * std) @@ -96,15 +96,15 @@ func (bbs *BollingerBandsSeries) GetBoundedLastValues() (x, y1, y2 float64) { startAt = 0 } - vb := NewValueBufferWithCapacity(period) + vb := sequence.NewBufferWithCapacity(period) for index := startAt; index < seriesLength; index++ { xn, yn := bbs.InnerSeries.GetValues(index) vb.Enqueue(yn) x = xn } - ay := Sequence{vb}.Average() - std := Sequence{vb}.StdDev() + ay := sequence.Seq{Provider: vb}.Average() + std := sequence.Seq{Provider: vb}.StdDev() y1 = ay + (bbs.GetK() * std) y2 = ay - (bbs.GetK() * std) diff --git a/bollinger_band_series_test.go b/bollinger_band_series_test.go index fbd2c35..b591848 100644 --- a/bollinger_band_series_test.go +++ b/bollinger_band_series_test.go @@ -6,14 +6,15 @@ import ( "testing" "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" ) func TestBollingerBandSeries(t *testing.T) { assert := assert.New(t) s1 := mockValuesProvider{ - X: Generate.Float64(1.0, 100.0), - Y: Generate.Random(100, 1024), + X: sequence.Values(1.0, 100.0), + Y: sequence.RandomValuesWithAverage(1024, 100), } bbs := &BollingerBandsSeries{ @@ -37,8 +38,8 @@ func TestBollingerBandLastValue(t *testing.T) { assert := assert.New(t) s1 := mockValuesProvider{ - X: Generate.Float64(1.0, 100.0), - Y: Generate.Float64(1.0, 100.0), + X: sequence.Values(1.0, 100.0), + Y: sequence.Values(1.0, 100.0), } bbs := &BollingerBandsSeries{ diff --git a/box.go b/box.go index 291b429..c59ab69 100644 --- a/box.go +++ b/box.go @@ -3,6 +3,8 @@ package chart import ( "fmt" "math" + + util "github.com/wcharczuk/go-chart/util" ) var ( @@ -89,12 +91,12 @@ func (b Box) GetBottom(defaults ...int) int { // Width returns the width func (b Box) Width() int { - return Math.AbsInt(b.Right - b.Left) + return util.Math.AbsInt(b.Right - b.Left) } // Height returns the height 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 @@ -146,10 +148,10 @@ func (b Box) Equals(other Box) bool { // Grow grows a box based on another box. func (b Box) Grow(other Box) Box { return Box{ - Top: Math.MinInt(b.Top, other.Top), - Left: Math.MinInt(b.Left, other.Left), - Right: Math.MaxInt(b.Right, other.Right), - Bottom: Math.MaxInt(b.Bottom, other.Bottom), + Top: util.Math.MinInt(b.Top, other.Top), + Left: util.Math.MinInt(b.Left, other.Left), + Right: util.Math.MaxInt(b.Right, other.Right), + 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 { newBox := b.Clone() - newBox.Top = Math.MaxInt(newBox.Top, other.Top) - newBox.Left = Math.MaxInt(newBox.Left, other.Left) - newBox.Right = Math.MinInt(newBox.Right, other.Right) - newBox.Bottom = Math.MinInt(newBox.Bottom, other.Bottom) + newBox.Top = util.Math.MaxInt(newBox.Top, other.Top) + newBox.Left = util.Math.MaxInt(newBox.Left, other.Left) + newBox.Right = util.Math.MinInt(newBox.Right, other.Right) + newBox.Bottom = util.Math.MinInt(newBox.Bottom, other.Bottom) return newBox } @@ -262,36 +264,36 @@ type BoxCorners struct { // Box return the BoxCorners as a regular box. func (bc BoxCorners) Box() Box { return Box{ - Top: Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y), - Left: Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X), - Right: Math.MaxInt(bc.TopRight.X, bc.BottomRight.X), - Bottom: Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y), + Top: util.Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y), + Left: util.Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X), + Right: util.Math.MaxInt(bc.TopRight.X, bc.BottomRight.X), + Bottom: util.Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y), } } // Width returns the width func (bc BoxCorners) Width() int { - minLeft := Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X) - maxRight := Math.MaxInt(bc.TopRight.X, bc.BottomRight.X) + minLeft := util.Math.MinInt(bc.TopLeft.X, bc.BottomLeft.X) + maxRight := util.Math.MaxInt(bc.TopRight.X, bc.BottomRight.X) return maxRight - minLeft } // Height returns the height func (bc BoxCorners) Height() int { - minTop := Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y) - maxBottom := Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y) + minTop := util.Math.MinInt(bc.TopLeft.Y, bc.TopRight.Y) + maxBottom := util.Math.MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y) return maxBottom - minTop } // Center returns the center of the box func (bc BoxCorners) Center() (x, y int) { - left := Math.MeanInt(bc.TopLeft.X, bc.BottomLeft.X) - right := Math.MeanInt(bc.TopRight.X, bc.BottomRight.X) + left := util.Math.MeanInt(bc.TopLeft.X, bc.BottomLeft.X) + right := util.Math.MeanInt(bc.TopRight.X, bc.BottomRight.X) x = ((right - left) >> 1) + left - top := Math.MeanInt(bc.TopLeft.Y, bc.TopRight.Y) - bottom := Math.MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y) + top := util.Math.MeanInt(bc.TopLeft.Y, bc.TopRight.Y) + bottom := util.Math.MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y) y = ((bottom - top) >> 1) + top return @@ -301,12 +303,12 @@ func (bc BoxCorners) Center() (x, y int) { func (bc BoxCorners) Rotate(thetaDegrees float64) BoxCorners { 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) - trx, try := Math.RotateCoordinate(cx, cy, bc.TopRight.X, bc.TopRight.Y, thetaRadians) - brx, bry := Math.RotateCoordinate(cx, cy, bc.BottomRight.X, bc.BottomRight.Y, thetaRadians) - blx, bly := Math.RotateCoordinate(cx, cy, bc.BottomLeft.X, bc.BottomLeft.Y, thetaRadians) + tlx, tly := util.Math.RotateCoordinate(cx, cy, bc.TopLeft.X, bc.TopLeft.Y, thetaRadians) + trx, try := util.Math.RotateCoordinate(cx, cy, bc.TopRight.X, bc.TopRight.Y, thetaRadians) + brx, bry := util.Math.RotateCoordinate(cx, cy, bc.BottomRight.X, bc.BottomRight.Y, thetaRadians) + blx, bly := util.Math.RotateCoordinate(cx, cy, bc.BottomLeft.X, bc.BottomLeft.Y, thetaRadians) return BoxCorners{ TopLeft: Point{tlx, tly}, diff --git a/chart.go b/chart.go index 15a9e14..83b70ec 100644 --- a/chart.go +++ b/chart.go @@ -7,6 +7,7 @@ import ( "math" "github.com/golang/freetype/truetype" + util "github.com/wcharczuk/go-chart/util" ) // 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 if c.YAxis.Style.Show { delta := yrange.GetDelta() - roundTo := Math.GetRoundToForDelta(delta) - rmin, rmax := Math.RoundDown(yrange.GetMin(), roundTo), Math.RoundUp(yrange.GetMax(), roundTo) + roundTo := util.Math.GetRoundToForDelta(delta) + rmin, rmax := util.Math.RoundDown(yrange.GetMin(), roundTo), util.Math.RoundUp(yrange.GetMax(), roundTo) yrange.SetMin(rmin) yrange.SetMax(rmax) @@ -287,8 +288,8 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) { if c.YAxisSecondary.Style.Show { delta := yrangeAlt.GetDelta() - roundTo := Math.GetRoundToForDelta(delta) - rmin, rmax := Math.RoundDown(yrangeAlt.GetMin(), roundTo), Math.RoundUp(yrangeAlt.GetMax(), roundTo) + roundTo := util.Math.GetRoundToForDelta(delta) + rmin, rmax := util.Math.RoundDown(yrangeAlt.GetMin(), roundTo), util.Math.RoundUp(yrangeAlt.GetMax(), roundTo) yrangeAlt.SetMin(rmin) yrangeAlt.SetMax(rmax) } diff --git a/chart_test.go b/chart_test.go index f29387f..a14a60c 100644 --- a/chart_test.go +++ b/chart_test.go @@ -10,6 +10,7 @@ import ( "github.com/blendlabs/go-assert" "github.com/wcharczuk/go-chart/drawing" + "github.com/wcharczuk/go-chart/sequence" ) func TestChartGetDPI(t *testing.T) { @@ -391,8 +392,8 @@ func TestChartRegressionBadRangesByUser(t *testing.T) { }, Series: []Series{ ContinuousSeries{ - XValues: Generate.Float64(1.0, 10.0), - YValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(1.0, 10.0), + YValues: sequence.Values(1.0, 10.0), }, }, } @@ -407,8 +408,8 @@ func TestChartValidatesSeries(t *testing.T) { c := Chart{ Series: []Series{ ContinuousSeries{ - XValues: Generate.Float64(1.0, 10.0), - YValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(1.0, 10.0), + YValues: sequence.Values(1.0, 10.0), }, }, } @@ -418,7 +419,7 @@ func TestChartValidatesSeries(t *testing.T) { c = Chart{ Series: []Series{ 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{ ContinuousSeries{ - XValues: Generate.Float64(0, 4, 1), - YValues: Generate.Float64(0, 4, 1), + XValues: sequence.Values(0, 4, 1), + YValues: sequence.Values(0, 4, 1), }, }, } @@ -549,8 +550,8 @@ func TestChartE2ELineWithFill(t *testing.T) { StrokeColor: drawing.ColorBlue, FillColor: drawing.ColorRed, }, - XValues: Generate.Float64(0, 4, 1), - YValues: Generate.Float64(0, 4, 1), + XValues: sequence.Values(0, 4, 1), + YValues: sequence.Values(0, 4, 1), }, }, } diff --git a/concat_series_test.go b/concat_series_test.go index e0d763a..1f15339 100644 --- a/concat_series_test.go +++ b/concat_series_test.go @@ -4,24 +4,25 @@ import ( "testing" assert "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" ) func TestConcatSeries(t *testing.T) { assert := assert.New(t) s1 := ContinuousSeries{ - XValues: Generate.Float64(1.0, 10.0), - YValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(1.0, 10.0), + YValues: sequence.Values(1.0, 10.0), } s2 := ContinuousSeries{ - XValues: Generate.Float64(11, 20.0), - YValues: Generate.Float64(10.0, 1.0), + XValues: sequence.Values(11, 20.0), + YValues: sequence.Values(10.0, 1.0), } s3 := ContinuousSeries{ - XValues: Generate.Float64(21, 30.0), - YValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(21, 30.0), + YValues: sequence.Values(1.0, 10.0), } cs := ConcatSeries([]Series{s1, s2, s3}) diff --git a/continuous_range_test.go b/continuous_range_test.go index 114ecbe..fe3b233 100644 --- a/continuous_range_test.go +++ b/continuous_range_test.go @@ -4,13 +4,14 @@ import ( "testing" "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/util" ) func TestRangeTranslate(t *testing.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} r := ContinuousRange{Domain: 1000} - r.Min, r.Max = Math.MinAndMax(values...) + r.Min, r.Max = util.Math.MinAndMax(values...) // delta = ~7.0 // value = ~5.0 diff --git a/continuous_series_test.go b/continuous_series_test.go index dca5afc..828493c 100644 --- a/continuous_series_test.go +++ b/continuous_series_test.go @@ -5,6 +5,7 @@ import ( "testing" assert "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" ) func TestContinuousSeries(t *testing.T) { @@ -12,8 +13,8 @@ func TestContinuousSeries(t *testing.T) { cs := ContinuousSeries{ Name: "Test Series", - XValues: Generate.Float64(1.0, 10.0), - YValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(1.0, 10.0), + YValues: sequence.Values(1.0, 10.0), } assert.Equal("Test Series", cs.GetName()) @@ -53,20 +54,20 @@ func TestContinuousSeriesValidate(t *testing.T) { cs := ContinuousSeries{ Name: "Test Series", - XValues: Generate.Float64(1.0, 10.0), - YValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(1.0, 10.0), + YValues: sequence.Values(1.0, 10.0), } assert.Nil(cs.Validate()) cs = ContinuousSeries{ Name: "Test Series", - XValues: Generate.Float64(1.0, 10.0), + XValues: sequence.Values(1.0, 10.0), } assert.NotNil(cs.Validate()) cs = ContinuousSeries{ Name: "Test Series", - YValues: Generate.Float64(1.0, 10.0), + YValues: sequence.Values(1.0, 10.0), } assert.NotNil(cs.Validate()) } diff --git a/draw.go b/draw.go index 5335930..ef79dc6 100644 --- a/draw.go +++ b/draw.go @@ -1,6 +1,10 @@ package chart -import "math" +import ( + "math" + + util "github.com/wcharczuk/go-chart/util" +) var ( // 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) r.LineTo(x, y) } - r.LineTo(x, Math.MinInt(cb, cb-yv0)) - r.LineTo(x0, Math.MinInt(cb, cb-yv0)) + r.LineTo(x, util.Math.MinInt(cb, cb-yv0)) + r.LineTo(x0, util.Math.MinInt(cb, cb-yv0)) r.LineTo(x0, y0) r.Fill() } diff --git a/ema_series_test.go b/ema_series_test.go index b0050b4..4b08570 100644 --- a/ema_series_test.go +++ b/ema_series_test.go @@ -4,10 +4,11 @@ import ( "testing" "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" ) var ( - emaXValues = Generate.Float64(1.0, 50.0) + emaXValues = sequence.Values(1.0, 50.0) emaYValues = []float64{ 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, diff --git a/font.go b/font.go index 749c605..a82880c 100644 --- a/font.go +++ b/font.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/golang/freetype/truetype" + "github.com/wcharczuk/go-chart/roboto" ) var ( @@ -17,7 +18,7 @@ func GetDefaultFont() (*truetype.Font, error) { _defaultFontLock.Lock() defer _defaultFontLock.Unlock() if _defaultFont == nil { - font, err := truetype.Parse(roboto) + font, err := truetype.Parse(roboto.Roboto) if err != nil { return nil, err } diff --git a/histogram_series_test.go b/histogram_series_test.go index cfc9d00..0c7e6c0 100644 --- a/histogram_series_test.go +++ b/histogram_series_test.go @@ -4,6 +4,7 @@ import ( "testing" assert "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" ) func TestHistogramSeries(t *testing.T) { @@ -11,8 +12,8 @@ func TestHistogramSeries(t *testing.T) { cs := ContinuousSeries{ Name: "Test Series", - XValues: Generate.Float64(1.0, 20.0), - YValues: Generate.Float64(10.0, -10.0), + XValues: sequence.Values(1.0, 20.0), + YValues: sequence.Values(10.0, -10.0), } hs := HistogramSeries{ diff --git a/legend.go b/legend.go index 4b2037b..42c11a3 100644 --- a/legend.go +++ b/legend.go @@ -1,6 +1,9 @@ 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. func Legend(c *Chart, userDefaults ...Style) Renderable { @@ -66,7 +69,7 @@ func Legend(c *Chart, userDefaults ...Style) Renderable { } legendContent.Bottom += tb.Height() right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum - legendContent.Right = Math.MaxInt(legendContent.Right, right) + legendContent.Right = util.Math.MaxInt(legendContent.Right, right) labelCount++ } } @@ -161,8 +164,8 @@ func LegendThin(c *Chart, userDefaults ...Style) Renderable { for x := 0; x < len(labels); x++ { if len(labels[x]) > 0 { textBox = r.MeasureText(labels[x]) - textHeight = Math.MaxInt(textBox.Height(), textHeight) - textWidth = Math.MaxInt(textBox.Width(), textWidth) + textHeight = util.Math.MaxInt(textBox.Height(), textHeight) + textWidth = util.Math.MaxInt(textBox.Width(), textWidth) } } @@ -278,7 +281,7 @@ func LegendLeft(c *Chart, userDefaults ...Style) Renderable { } legendContent.Bottom += tb.Height() right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum - legendContent.Right = Math.MaxInt(legendContent.Right, right) + legendContent.Right = util.Math.MaxInt(legendContent.Right, right) labelCount++ } } diff --git a/linear_regression_series.go b/linear_regression_series.go index a3c5049..1ff2dc4 100644 --- a/linear_regression_series.go +++ b/linear_regression_series.go @@ -1,6 +1,11 @@ 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 // linear regression for the values. @@ -36,7 +41,7 @@ func (lrs LinearRegressionSeries) GetYAxis() YAxisType { // Len returns the number of elements in the series. 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. @@ -51,7 +56,7 @@ func (lrs LinearRegressionSeries) GetLimit() int { func (lrs LinearRegressionSeries) GetEndIndex() int { windowEnd := lrs.GetOffset() + lrs.GetLimit() innerSeriesLastIndex := lrs.InnerSeries.Len() - 1 - return Math.MinInt(windowEnd, innerSeriesLastIndex) + return util.Math.MinInt(windowEnd, innerSeriesLastIndex) } // GetOffset returns the data offset. @@ -71,7 +76,7 @@ func (lrs *LinearRegressionSeries) GetValues(index int) (x, y float64) { lrs.computeCoefficients() } 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) y = (lrs.m * lrs.normalize(x)) + lrs.b return @@ -102,14 +107,14 @@ func (lrs *LinearRegressionSeries) computeCoefficients() { p := float64(endIndex - startIndex) - xvalues := NewValueBufferWithCapacity(lrs.Len()) + xvalues := sequence.NewBufferWithCapacity(lrs.Len()) for index := startIndex; index < endIndex; index++ { x, _ := lrs.InnerSeries.GetValues(index) xvalues.Enqueue(x) } - lrs.avgx = Sequence{xvalues}.Average() - lrs.stddevx = Sequence{xvalues}.StdDev() + lrs.avgx = sequence.Seq{Provider: xvalues}.Average() + lrs.stddevx = sequence.Seq{Provider: xvalues}.StdDev() var sumx, sumy, sumxx, sumxy float64 for index := startIndex; index < endIndex; index++ { diff --git a/linear_regression_series_test.go b/linear_regression_series_test.go index 1063082..c26934b 100644 --- a/linear_regression_series_test.go +++ b/linear_regression_series_test.go @@ -4,6 +4,7 @@ import ( "testing" assert "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" ) func TestLinearRegressionSeries(t *testing.T) { @@ -11,8 +12,8 @@ func TestLinearRegressionSeries(t *testing.T) { mainSeries := ContinuousSeries{ Name: "A test series", - XValues: Generate.Float64(1.0, 100.0), - YValues: Generate.Float64(1.0, 100.0), + XValues: sequence.Values(1.0, 100.0), + YValues: sequence.Values(1.0, 100.0), } linRegSeries := &LinearRegressionSeries{ @@ -33,8 +34,8 @@ func TestLinearRegressionSeriesDesc(t *testing.T) { mainSeries := ContinuousSeries{ Name: "A test series", - XValues: Generate.Float64(100.0, 1.0), - YValues: Generate.Float64(100.0, 1.0), + XValues: sequence.Values(100.0, 1.0), + YValues: sequence.Values(100.0, 1.0), } linRegSeries := &LinearRegressionSeries{ @@ -55,8 +56,8 @@ func TestLinearRegressionSeriesWindowAndOffset(t *testing.T) { mainSeries := ContinuousSeries{ Name: "A test series", - XValues: Generate.Float64(100.0, 1.0), - YValues: Generate.Float64(100.0, 1.0), + XValues: sequence.Values(100.0, 1.0), + YValues: sequence.Values(100.0, 1.0), } linRegSeries := &LinearRegressionSeries{ diff --git a/market_hours_range.go b/market_hours_range.go index 5fba57d..19ccf0f 100644 --- a/market_hours_range.go +++ b/market_hours_range.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/wcharczuk/go-chart/sequence" "github.com/wcharczuk/go-chart/util" ) @@ -41,17 +42,17 @@ func (mhr MarketHoursRange) IsZero() bool { // GetMin returns the min value. func (mhr MarketHoursRange) GetMin() float64 { - return Time.ToFloat64(mhr.Min) + return util.Time.ToFloat64(mhr.Min) } // GetMax returns the max value. 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. 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) { return maxClose } @@ -60,13 +61,13 @@ func (mhr MarketHoursRange) GetEffectiveMax() time.Time { // SetMin sets the min value. func (mhr *MarketHoursRange) SetMin(min float64) { - mhr.Min = Time.FromFloat64(min) + mhr.Min = util.Time.FromFloat64(min) mhr.Min = mhr.Min.In(mhr.GetTimezone()) } // SetMax sets the max value. func (mhr *MarketHoursRange) SetMax(max float64) { - mhr.Max = Time.FromFloat64(max) + mhr.Max = util.Time.FromFloat64(max) 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. -func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider { +func (mhr MarketHoursRange) GetHolidayProvider() util.HolidayProvider { if mhr.HolidayProvider == nil { - return defaultHolidayProvider + return func(_ time.Time) bool { return false } } return mhr.HolidayProvider } @@ -98,7 +99,7 @@ func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider { // GetMarketOpen returns the market open time. func (mhr MarketHoursRange) GetMarketOpen() time.Time { if mhr.MarketOpen.IsZero() { - return NYSEOpen() + return util.NYSEOpen() } return mhr.MarketOpen } @@ -106,7 +107,7 @@ func (mhr MarketHoursRange) GetMarketOpen() time.Time { // GetMarketClose returns the market close time. func (mhr MarketHoursRange) GetMarketClose() time.Time { if mhr.MarketClose.IsZero() { - return NYSEClose() + return util.NYSEClose() } return mhr.MarketClose } @@ -114,31 +115,31 @@ func (mhr MarketHoursRange) GetMarketClose() time.Time { // GetTicks returns the ticks 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 { - 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) if timesWidth <= mhr.Domain { 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) if timesWidth <= mhr.Domain { 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) if timesWidth <= mhr.Domain { 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) if timesWidth <= mhr.Domain { 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) if timesWidth <= mhr.Domain { return mhr.makeTicks(vf, times) @@ -167,7 +168,7 @@ func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []T ticks := make([]Tick, len(times)) for index, t := range times { ticks[index] = Tick{ - Value: Time.ToFloat64(t), + Value: util.Time.ToFloat64(t), Label: vf(t), } } @@ -180,10 +181,10 @@ func (mhr MarketHoursRange) String() string { // Translate maps a given value into the ContinuousRange space. func (mhr MarketHoursRange) Translate(value float64) int { - valueTime := Time.FromFloat64(value) - valueTimeEastern := valueTime.In(Date.Eastern()) - totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider) - valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider) + valueTime := util.Time.FromFloat64(value) + valueTimeEastern := valueTime.In(util.Date.Eastern()) + totalSeconds := util.Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), 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)) if mhr.IsDescending() { diff --git a/market_hours_range_test.go b/market_hours_range_test.go index 1cdf8cc..3846ccc 100644 --- a/market_hours_range_test.go +++ b/market_hours_range_test.go @@ -5,17 +5,18 @@ import ( "time" assert "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/util" ) func TestMarketHoursRangeGetDelta(t *testing.T) { assert := assert.New(t) r := &MarketHoursRange{ - Min: time.Date(2016, 07, 19, 9, 30, 0, 0, Date.Eastern()), - Max: time.Date(2016, 07, 22, 16, 00, 0, 0, Date.Eastern()), - MarketOpen: NYSEOpen(), - MarketClose: NYSEClose(), - HolidayProvider: Date.IsNYSEHoliday, + Min: time.Date(2016, 07, 19, 9, 30, 0, 0, util.Date.Eastern()), + Max: time.Date(2016, 07, 22, 16, 00, 0, 0, util.Date.Eastern()), + MarketOpen: util.NYSEOpen(), + MarketClose: util.NYSEClose(), + HolidayProvider: util.Date.IsNYSEHoliday, } assert.NotZero(r.GetDelta()) @@ -25,19 +26,19 @@ func TestMarketHoursRangeTranslate(t *testing.T) { assert := assert.New(t) r := &MarketHoursRange{ - Min: time.Date(2016, 07, 18, 9, 30, 0, 0, Date.Eastern()), - Max: time.Date(2016, 07, 22, 16, 00, 0, 0, Date.Eastern()), - MarketOpen: NYSEOpen(), - MarketClose: NYSEClose(), - HolidayProvider: Date.IsNYSEHoliday, + Min: time.Date(2016, 07, 18, 9, 30, 0, 0, util.Date.Eastern()), + Max: time.Date(2016, 07, 22, 16, 00, 0, 0, util.Date.Eastern()), + MarketOpen: util.NYSEOpen(), + MarketClose: util.NYSEClose(), + HolidayProvider: util.Date.IsNYSEHoliday, 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(400, r.Translate(Time.ToFloat64(weds))) - assert.Equal(1000, r.Translate(Time.ToFloat64(r.Max))) + assert.Equal(0, r.Translate(util.Time.ToFloat64(r.Min))) + assert.Equal(400, r.Translate(util.Time.ToFloat64(weds))) + assert.Equal(1000, r.Translate(util.Time.ToFloat64(r.Max))) } func TestMarketHoursRangeGetTicks(t *testing.T) { @@ -56,17 +57,17 @@ func TestMarketHoursRangeGetTicks(t *testing.T) { } ra := &MarketHoursRange{ - Min: Date.On(NYSEOpen(), Date.Date(2016, 07, 18, Date.Eastern())), - Max: Date.On(NYSEClose(), Date.Date(2016, 07, 22, Date.Eastern())), - MarketOpen: NYSEOpen(), - MarketClose: NYSEClose(), - HolidayProvider: Date.IsNYSEHoliday, + Min: util.Date.On(util.NYSEOpen(), util.Date.Date(2016, 07, 18, util.Date.Eastern())), + Max: util.Date.On(util.NYSEClose(), util.Date.Date(2016, 07, 22, util.Date.Eastern())), + MarketOpen: util.NYSEOpen(), + MarketClose: util.NYSEClose(), + HolidayProvider: util.Date.IsNYSEHoliday, Domain: 1024, } ticks := ra.GetTicks(r, defaults, TimeValueFormatter) assert.NotEmpty(ticks) 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) } diff --git a/pie_chart.go b/pie_chart.go index 815e6de..d0f1260 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -4,8 +4,15 @@ import ( "errors" "fmt" "io" + "math" "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. @@ -123,7 +130,7 @@ func (pc PieChart) drawTitle(r Renderer) { func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) { cx, cy := canvasBox.Center() - diameter := Math.MinInt(canvasBox.Width(), canvasBox.Height()) + diameter := util.Math.MinInt(canvasBox.Width(), canvasBox.Height()) radius := float64(diameter >> 1) 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) r.MoveTo(cx, cy) - rads = Math.PercentToRadians(total) - delta = Math.PercentToRadians(v.Value) + rads = util.Math.PercentToRadians(total) + delta = util.Math.PercentToRadians(v.Value) 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 { v.Style.InheritFrom(pc.stylePieChartValue(index)).WriteToRenderer(r) if len(v.Label) > 0 { - delta2 = Math.PercentToRadians(total + (v.Value / 2.0)) - delta2 = Math.RadianAdd(delta2, _pi2) - lx, ly = Math.CirclePoint(cx, cy, labelRadius, delta2) + delta2 = util.Math.PercentToRadians(total + (v.Value / 2.0)) + delta2 = util.Math.RadianAdd(delta2, _pi2) + lx, ly = util.Math.CirclePoint(cx, cy, labelRadius, delta2) tb := r.MeasureText(v.Label) lx = lx - (tb.Width() >> 1) @@ -177,7 +184,7 @@ func (pc PieChart) getDefaultCanvasBox() 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{ Right: circleDiameter, @@ -223,7 +230,7 @@ func (pc PieChart) stylePieChartValue(index int) Style { } func (pc PieChart) getScaledFontSize() float64 { - effectiveDimension := Math.MinInt(pc.GetWidth(), pc.GetHeight()) + effectiveDimension := util.Math.MinInt(pc.GetWidth(), pc.GetHeight()) if effectiveDimension >= 2048 { return 48.0 } else if effectiveDimension >= 1024 { @@ -262,7 +269,7 @@ func (pc PieChart) styleDefaultsTitle() Style { } func (pc PieChart) getTitleFontSize() float64 { - effectiveDimension := Math.MinInt(pc.GetWidth(), pc.GetHeight()) + effectiveDimension := util.Math.MinInt(pc.GetWidth(), pc.GetHeight()) if effectiveDimension >= 2048 { return 48 } else if effectiveDimension >= 1024 { diff --git a/polynomial_regression_series.go b/polynomial_regression_series.go index e9dd1bd..506a4cb 100644 --- a/polynomial_regression_series.go +++ b/polynomial_regression_series.go @@ -5,6 +5,7 @@ import ( "math" "github.com/wcharczuk/go-chart/matrix" + util "github.com/wcharczuk/go-chart/util" ) // 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. 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. @@ -54,7 +55,7 @@ func (prs PolynomialRegressionSeries) GetLimit() int { func (prs PolynomialRegressionSeries) GetEndIndex() int { windowEnd := prs.GetOffset() + prs.GetLimit() innerSeriesLastIndex := prs.InnerSeries.Len() - 1 - return Math.MinInt(windowEnd, innerSeriesLastIndex) + return util.Math.MinInt(windowEnd, innerSeriesLastIndex) } // GetOffset returns the data offset. @@ -94,7 +95,7 @@ func (prs *PolynomialRegressionSeries) GetValues(index int) (x, y float64) { } 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) y = prs.apply(x) return diff --git a/raster_renderer.go b/raster_renderer.go index 3088658..dacc939 100644 --- a/raster_renderer.go +++ b/raster_renderer.go @@ -6,6 +6,7 @@ import ( "io" "math" + util "github.com/blendlabs/go-util" "github.com/golang/freetype/truetype" "github.com/wcharczuk/go-chart/drawing" ) @@ -191,7 +192,7 @@ func (rr *rasterRenderer) MeasureText(body string) Box { 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. diff --git a/sequence/linear.go b/sequence/linear.go index ab10906..71f4ffa 100644 --- a/sequence/linear.go +++ b/sequence/linear.go @@ -1,5 +1,15 @@ 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. func NewLinear() *Linear { return &Linear{} diff --git a/sequence/random.go b/sequence/random.go index 3846513..bbdb2df 100644 --- a/sequence/random.go +++ b/sequence/random.go @@ -3,8 +3,26 @@ package sequence import ( "math" "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. type Random struct { rnd *rand.Rand diff --git a/sequence/sequence.go b/sequence/sequence.go index 1b6060d..b2b2f00 100644 --- a/sequence/sequence.go +++ b/sequence/sequence.go @@ -13,6 +13,15 @@ type Seq struct { 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. func (s Seq) Each(mapfn func(int, float64)) { for i := 0; i < s.Len(); i++ { diff --git a/sma_series.go b/sma_series.go index 73f563c..396ecaa 100644 --- a/sma_series.go +++ b/sma_series.go @@ -1,6 +1,10 @@ package chart -import "fmt" +import ( + "fmt" + + util "github.com/wcharczuk/go-chart/util" +) const ( // 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 { period := sma.GetPeriod() - floor := Math.MaxInt(0, index-period) + floor := util.Math.MaxInt(0, index-period) var accum float64 var count float64 for x := index; x >= floor; x-- { diff --git a/sma_series_test.go b/sma_series_test.go index c2f7d81..b416698 100644 --- a/sma_series_test.go +++ b/sma_series_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/sequence" + "github.com/wcharczuk/go-chart/util" ) type mockValuesProvider struct { @@ -12,14 +14,14 @@ type mockValuesProvider struct { } 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) { if index < 0 { 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") } x = m.X[index] @@ -31,8 +33,8 @@ func TestSMASeriesGetValue(t *testing.T) { assert := assert.New(t) mockSeries := mockValuesProvider{ - Generate.Float64(1.0, 10.0), - Generate.Float64(10, 1.0), + sequence.Values(1.0, 10.0), + sequence.Values(10, 1.0), } assert.Equal(10, mockSeries.Len()) @@ -62,8 +64,8 @@ func TestSMASeriesGetLastValueWindowOverlap(t *testing.T) { assert := assert.New(t) mockSeries := mockValuesProvider{ - Generate.Float64(1.0, 10.0), - Generate.Float64(10, 1.0), + sequence.Values(1.0, 10.0), + sequence.Values(10, 1.0), } assert.Equal(10, mockSeries.Len()) @@ -88,8 +90,8 @@ func TestSMASeriesGetLastValue(t *testing.T) { assert := assert.New(t) mockSeries := mockValuesProvider{ - Generate.Float64(1.0, 100.0), - Generate.Float64(100, 1.0), + sequence.Values(1.0, 100.0), + sequence.Values(100, 1.0), } assert.Equal(100, mockSeries.Len()) diff --git a/stacked_bar_chart.go b/stacked_bar_chart.go index 6b85d81..33dd5b1 100644 --- a/stacked_bar_chart.go +++ b/stacked_bar_chart.go @@ -7,6 +7,8 @@ import ( "math" "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. @@ -145,7 +147,7 @@ func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar S Top: yoffset, Left: bxl, 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))) yoffset += barHeight @@ -200,7 +202,7 @@ func (sbc StackedBarChart) drawYAxis(r Renderer, canvasBox Box) { r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom) 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 { axisStyle.GetStrokeOptions().WriteToRenderer(r) 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) 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{ @@ -304,7 +306,7 @@ func (sbc StackedBarChart) styleDefaultsTitle() Style { } func (sbc StackedBarChart) getTitleFontSize() float64 { - effectiveDimension := Math.MinInt(sbc.GetWidth(), sbc.GetHeight()) + effectiveDimension := util.Math.MinInt(sbc.GetWidth(), sbc.GetHeight()) if effectiveDimension >= 2048 { return 48 } else if effectiveDimension >= 1024 { diff --git a/style.go b/style.go index 72f2577..b1a145d 100644 --- a/style.go +++ b/style.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + util "github.com/blendlabs/go-util" "github.com/golang/freetype/truetype" "github.com/wcharczuk/go-chart/drawing" ) @@ -330,7 +331,7 @@ func (s Style) WriteToRenderer(r Renderer) { r.ClearTextRotation() if s.GetTextRotationDegrees() != 0 { - r.SetTextRotation(Math.DegreesToRadians(s.GetTextRotationDegrees())) + r.SetTextRotation(util.Math.DegreesToRadians(s.GetTextRotationDegrees())) } } diff --git a/text.go b/text.go index 156d5b3..a312c5b 100644 --- a/text.go +++ b/text.go @@ -1,6 +1,10 @@ package chart -import "strings" +import ( + "strings" + + util "github.com/wcharczuk/go-chart/util" +) // TextHorizontalAlign is an enum for the horizontal alignment options. type TextHorizontalAlign int @@ -145,7 +149,7 @@ func (t text) MeasureLines(r Renderer, lines []string, style Style) Box { var output Box for index, line := range lines { 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() if index < len(lines)-1 { output.Bottom += +style.GetTextLineSpacing() diff --git a/tick.go b/tick.go index 2764fcf..875fc5f 100644 --- a/tick.go +++ b/tick.go @@ -4,6 +4,8 @@ import ( "fmt" "math" "strings" + + util "github.com/wcharczuk/go-chart/util" ) // 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) tickStep := rangeDelta / float64(intermediateTickCount) - roundTo := Math.GetRoundToForDelta(rangeDelta) / 10 - intermediateTickCount = Math.MinInt(intermediateTickCount, 1<<10) + roundTo := util.Math.GetRoundToForDelta(rangeDelta) / 10 + intermediateTickCount = util.Math.MinInt(intermediateTickCount, 1<<10) for x := 1; x < intermediateTickCount; x++ { var tickValue float64 if ra.IsDescending() { - tickValue = max - Math.RoundUp(tickStep*float64(x), roundTo) + tickValue = max - util.Math.RoundUp(tickStep*float64(x), roundTo) } else { - tickValue = min + Math.RoundUp(tickStep*float64(x), roundTo) + tickValue = min + util.Math.RoundUp(tickStep*float64(x), roundTo) } ticks = append(ticks, Tick{ Value: tickValue, diff --git a/time_series.go b/time_series.go index 7707dd1..d2636a1 100644 --- a/time_series.go +++ b/time_series.go @@ -3,6 +3,8 @@ package chart import ( "fmt" "time" + + util "github.com/wcharczuk/go-chart/util" ) // TimeSeries is a line on a chart. @@ -33,14 +35,14 @@ func (ts TimeSeries) Len() int { // GetValues gets a value at a given index. 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] return } // GetLastValues gets the last value. 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] return } diff --git a/value.go b/value.go index 0346c9a..4b747e0 100644 --- a/value.go +++ b/value.go @@ -1,5 +1,7 @@ package chart +import util "github.com/blendlabs/go-util" + // Value is a chart value. type Value struct { Style Style @@ -21,7 +23,7 @@ func (vs Values) Values() []float64 { // ValuesNormalized returns normalized values. func (vs Values) ValuesNormalized() []float64 { - return Math.Normalize(vs.Values()...) + return util.Math.Normalize(vs.Values()...) } // Normalize returns the values normalized. @@ -38,7 +40,7 @@ func (vs Values) Normalize() []Value { output = append(output, Value{ Style: v.Style, Label: v.Label, - Value: Math.RoundDown(v.Value/total, 0.0001), + Value: util.Math.RoundDown(v.Value/total, 0.0001), }) } } diff --git a/value_formatter_test.go b/value_formatter_test.go index d471e86..ac94060 100644 --- a/value_formatter_test.go +++ b/value_formatter_test.go @@ -5,13 +5,14 @@ import ( "time" "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/util" ) func TestTimeValueFormatterWithFormat(t *testing.T) { assert := assert.New(t) d := time.Now() - di := Time.ToFloat64(d) + di := util.Time.ToFloat64(d) df := float64(di) s := formatTime(d, DefaultDateFormat) diff --git a/vector_renderer.go b/vector_renderer.go index 537d7b4..17d7b73 100644 --- a/vector_renderer.go +++ b/vector_renderer.go @@ -9,6 +9,7 @@ import ( "golang.org/x/image/font" + util "github.com/blendlabs/go-util" "github.com/golang/freetype/truetype" "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) { - startAngle = Math.RadianAdd(startAngle, _pi2) - endAngle := Math.RadianAdd(startAngle, delta) + startAngle = util.Math.RadianAdd(startAngle, _pi2) + endAngle := util.Math.RadianAdd(startAngle, delta) startx := cx + int(rx*math.Sin(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)) 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)) } @@ -176,7 +177,7 @@ func (vr *vectorRenderer) MeasureText(body string) (box Box) { if vr.c.textTheta == nil { return } - box = box.Corners().Rotate(Math.RadiansToDegrees(*vr.c.textTheta)).Box() + box = box.Corners().Rotate(util.Math.RadiansToDegrees(*vr.c.textTheta)).Box() } return } @@ -231,7 +232,7 @@ func (c *canvas) Text(x, y int, body string, style Style) { if c.textTheta == nil { c.w.Write([]byte(fmt.Sprintf(`%s`, x, y, c.styleAsSVG(style), body))) } 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(`%s`, x, y, c.styleAsSVG(style), transform, body))) } } diff --git a/xaxis.go b/xaxis.go index dd6e0fd..d97616c 100644 --- a/xaxis.go +++ b/xaxis.go @@ -1,6 +1,10 @@ package chart -import "math" +import ( + "math" + + util "github.com/wcharczuk/go-chart/util" +) // XAxis represents the horizontal axis. type XAxis struct { @@ -101,9 +105,9 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic break } - left = Math.MinInt(left, ltx) - right = Math.MaxInt(right, rtx) - bottom = Math.MaxInt(bottom, ty) + left = util.Math.MinInt(left, ltx) + right = util.Math.MaxInt(right, rtx) + bottom = util.Math.MaxInt(bottom, ty) } 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) } Draw.Text(r, t.Label, tx, ty, tickWithAxisStyle) - maxTextHeight = Math.MaxInt(maxTextHeight, tb.Height()) + maxTextHeight = util.Math.MaxInt(maxTextHeight, tb.Height()) break case TickPositionBetweenTicks: if index > 0 { @@ -171,7 +175,7 @@ func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick }, 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 } diff --git a/yaxis.go b/yaxis.go index 3904729..3549888 100644 --- a/yaxis.go +++ b/yaxis.go @@ -1,6 +1,10 @@ package chart -import "math" +import ( + "math" + + util "github.com/wcharczuk/go-chart/util" +) // YAxis is a veritcal rule of the range. // 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() } - maxTextHeight = Math.MaxInt(tb.Height(), maxTextHeight) + maxTextHeight = util.Math.MaxInt(tb.Height(), maxTextHeight) if ya.AxisType == YAxisPrimary { minx = canvasBox.Right - maxx = Math.MaxInt(maxx, tx+tb.Width()) + maxx = util.Math.MaxInt(maxx, tx+tb.Width()) } else if ya.AxisType == YAxisSecondary { - minx = Math.MinInt(minx, finalTextX) - maxx = Math.MaxInt(maxx, tx) + minx = util.Math.MinInt(minx, finalTextX) + maxx = util.Math.MaxInt(maxx, tx) } - miny = Math.MinInt(miny, ly-tbh2) - maxy = Math.MaxInt(maxy, ly+tbh2) + miny = util.Math.MinInt(miny, ly-tbh2) + maxy = util.Math.MaxInt(maxy, ly+tbh2) } if ya.NameStyle.Show && len(ya.Name) > 0 {