snapshot ahead of tests

This commit is contained in:
Will Charczuk 2022-01-10 15:52:32 -08:00
parent db4dcecf1f
commit f23b63bae4
39 changed files with 606 additions and 582 deletions

View file

@ -60,10 +60,10 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran
lx := canvasBox.Left + xrange.Translate(a.XValue) lx := canvasBox.Left + xrange.Translate(a.XValue)
ly := canvasBox.Bottom - yrange.Translate(a.YValue) ly := canvasBox.Bottom - yrange.Translate(a.YValue)
ab := Draw.MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label) ab := Draw.MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label)
box.Top = MinInt(box.Top, ab.Top) box.Top = Min(box.Top, ab.Top)
box.Left = MinInt(box.Left, ab.Left) box.Left = Min(box.Left, ab.Left)
box.Right = MaxInt(box.Right, ab.Right) box.Right = Max(box.Right, ab.Right)
box.Bottom = MaxInt(box.Bottom, ab.Bottom) box.Bottom = Max(box.Bottom, ab.Bottom)
} }
} }
return box return box

View file

@ -1,24 +1,24 @@
package chart package chart
var ( var (
_ Sequence = (*Array)(nil) _ Sequence[int] = (*array[int])(nil)
) )
// NewArray returns a new array from a given set of values. // NewArray returns a new array from a given set of values.
// Array implements Sequence, which allows it to be used with the sequence helpers. // Array implements Sequence, which allows it to be used with the sequence helpers.
func NewArray(values ...float64) Array { func Array[A any](values ...A) Sequence[A] {
return Array(values) return array[A](values)
} }
// Array is a wrapper for an array of floats that implements `ValuesProvider`. // Array is a wrapper for an array of floats that implements `ValuesProvider`.
type Array []float64 type array[A any] []A
// Len returns the value provider length. // Len returns the value provider length.
func (a Array) Len() int { func (a array[A]) Len() int {
return len(a) return len(a)
} }
// GetValue returns the value at a given index. // GetValue returns the value at a given index.
func (a Array) GetValue(index int) float64 { func (a array[A]) GetValue(index int) A {
return a[index] return a[index]
} }

View file

@ -409,7 +409,7 @@ func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range,
lines := Text.WrapFit(r, bar.Label, barLabelBox.Width(), axisStyle) lines := Text.WrapFit(r, bar.Label, barLabelBox.Width(), axisStyle)
linesBox := Text.MeasureLines(r, lines, axisStyle) linesBox := Text.MeasureLines(r, lines, axisStyle)
xaxisHeight = MinInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight) xaxisHeight = Min(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
} }
} }
@ -476,7 +476,7 @@ func (bc BarChart) styleDefaultsTitle() Style {
} }
func (bc BarChart) getTitleFontSize() float64 { func (bc BarChart) getTitleFontSize() float64 {
effectiveDimension := MinInt(bc.GetWidth(), bc.GetHeight()) effectiveDimension := Min(bc.GetWidth(), bc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48 return 48
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {

View file

@ -20,7 +20,7 @@ type BollingerBandsSeries struct {
K float64 K float64
InnerSeries ValuesProvider InnerSeries ValuesProvider
valueBuffer *ValueBuffer valueBuffer *ValueBuffer[float64]
} }
// GetName returns the name of the time series. // GetName returns the name of the time series.
@ -70,7 +70,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 = NewValueBufferWithCapacity(bbs.GetPeriod()) bbs.valueBuffer = NewValueBufferWithCapacity[float64](bbs.GetPeriod())
} }
if bbs.valueBuffer.Len() >= bbs.GetPeriod() { if bbs.valueBuffer.Len() >= bbs.GetPeriod() {
bbs.valueBuffer.Dequeue() bbs.valueBuffer.Dequeue()
@ -79,8 +79,8 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64)
bbs.valueBuffer.Enqueue(py) bbs.valueBuffer.Enqueue(py)
x = px x = px
ay := Seq{bbs.valueBuffer}.Average() ay := Seq[float64]{bbs.valueBuffer}.Average()
std := Seq{bbs.valueBuffer}.StdDev() std := Seq[float64]{bbs.valueBuffer}.StdDev()
y1 = ay + (bbs.GetK() * std) y1 = ay + (bbs.GetK() * std)
y2 = ay - (bbs.GetK() * std) y2 = ay - (bbs.GetK() * std)
@ -99,15 +99,15 @@ func (bbs *BollingerBandsSeries) GetBoundedLastValues() (x, y1, y2 float64) {
startAt = 0 startAt = 0
} }
vb := NewValueBufferWithCapacity(period) vb := NewValueBufferWithCapacity[float64](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 := Seq{vb}.Average() ay := Seq[float64]{vb}.Average()
std := Seq{vb}.StdDev() std := Seq[float64]{vb}.StdDev()
y1 = ay + (bbs.GetK() * std) y1 = ay + (bbs.GetK() * std)
y2 = ay - (bbs.GetK() * std) y2 = ay - (bbs.GetK() * std)

44
box.go
View file

@ -89,12 +89,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 AbsInt(b.Right - b.Left) return Abs(b.Right - b.Left)
} }
// Height returns the height // Height returns the height
func (b Box) Height() int { func (b Box) Height() int {
return AbsInt(b.Bottom - b.Top) return Abs(b.Bottom - b.Top)
} }
// Center returns the center of the box // Center returns the center of the box
@ -146,10 +146,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: MinInt(b.Top, other.Top), Top: Min(b.Top, other.Top),
Left: MinInt(b.Left, other.Left), Left: Min(b.Left, other.Left),
Right: MaxInt(b.Right, other.Right), Right: Max(b.Right, other.Right),
Bottom: MaxInt(b.Bottom, other.Bottom), Bottom: Max(b.Bottom, other.Bottom),
} }
} }
@ -220,10 +220,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 = MaxInt(newBox.Top, other.Top) newBox.Top = Max(newBox.Top, other.Top)
newBox.Left = MaxInt(newBox.Left, other.Left) newBox.Left = Max(newBox.Left, other.Left)
newBox.Right = MinInt(newBox.Right, other.Right) newBox.Right = Min(newBox.Right, other.Right)
newBox.Bottom = MinInt(newBox.Bottom, other.Bottom) newBox.Bottom = Min(newBox.Bottom, other.Bottom)
return newBox return newBox
} }
@ -262,36 +262,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: MinInt(bc.TopLeft.Y, bc.TopRight.Y), Top: Min(bc.TopLeft.Y, bc.TopRight.Y),
Left: MinInt(bc.TopLeft.X, bc.BottomLeft.X), Left: Min(bc.TopLeft.X, bc.BottomLeft.X),
Right: MaxInt(bc.TopRight.X, bc.BottomRight.X), Right: Max(bc.TopRight.X, bc.BottomRight.X),
Bottom: MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y), Bottom: Max(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 := MinInt(bc.TopLeft.X, bc.BottomLeft.X) minLeft := Min(bc.TopLeft.X, bc.BottomLeft.X)
maxRight := MaxInt(bc.TopRight.X, bc.BottomRight.X) maxRight := Max(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 := MinInt(bc.TopLeft.Y, bc.TopRight.Y) minTop := Min(bc.TopLeft.Y, bc.TopRight.Y)
maxBottom := MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y) maxBottom := Max(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 := MeanInt(bc.TopLeft.X, bc.BottomLeft.X) left := Mean(bc.TopLeft.X, bc.BottomLeft.X)
right := MeanInt(bc.TopRight.X, bc.BottomRight.X) right := Mean(bc.TopRight.X, bc.BottomRight.X)
x = ((right - left) >> 1) + left x = ((right - left) >> 1) + left
top := MeanInt(bc.TopLeft.Y, bc.TopRight.Y) top := Mean(bc.TopLeft.Y, bc.TopRight.Y)
bottom := MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y) bottom := Mean(bc.BottomLeft.Y, bc.BottomRight.Y)
y = ((bottom - top) >> 1) + top y = ((bottom - top) >> 1) + top
return return

View file

@ -123,7 +123,7 @@ func (pc DonutChart) drawTitle(r Renderer) {
func (pc DonutChart) drawSlices(r Renderer, canvasBox Box, values []Value) { func (pc DonutChart) drawSlices(r Renderer, canvasBox Box, values []Value) {
cx, cy := canvasBox.Center() cx, cy := canvasBox.Center()
diameter := MinInt(canvasBox.Width(), canvasBox.Height()) diameter := Min(canvasBox.Width(), canvasBox.Height())
radius := float64(diameter>>1) / 1.1 radius := float64(diameter>>1) / 1.1
labelRadius := (radius * 2.83) / 3.0 labelRadius := (radius * 2.83) / 3.0
@ -195,7 +195,7 @@ func (pc DonutChart) getDefaultCanvasBox() Box {
} }
func (pc DonutChart) getCircleAdjustedCanvasBox(canvasBox Box) Box { func (pc DonutChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height()) circleDiameter := Min(canvasBox.Width(), canvasBox.Height())
square := Box{ square := Box{
Right: circleDiameter, Right: circleDiameter,
@ -241,7 +241,7 @@ func (pc DonutChart) styleDonutChartValue(index int) Style {
} }
func (pc DonutChart) getScaledFontSize() float64 { func (pc DonutChart) getScaledFontSize() float64 {
effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) effectiveDimension := Min(pc.GetWidth(), pc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48.0 return 48.0
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {
@ -280,7 +280,7 @@ func (pc DonutChart) styleDefaultsTitle() Style {
} }
func (pc DonutChart) getTitleFontSize() float64 { func (pc DonutChart) getTitleFontSize() float64 {
effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) effectiveDimension := Min(pc.GetWidth(), pc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48 return 48
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {

View file

@ -38,8 +38,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, MinInt(cb, cb-yv0)) r.LineTo(x, Min(cb, cb-yv0))
r.LineTo(x0, MinInt(cb, cb-yv0)) r.LineTo(x0, Min(cb, cb-yv0))
r.LineTo(x0, y0) r.LineTo(x0, y0)
r.Fill() r.Fill()
} }

View file

@ -22,8 +22,8 @@ func main() {
}, },
Series: []chart.Series{ Series: []chart.Series{
chart.ContinuousSeries{ chart.ContinuousSeries{
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(),
YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(100).WithMax(512)}.Values(), YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(100).WithMax(512)}.Values(),
}, },
}, },
} }

View file

@ -17,8 +17,8 @@ func main() {
mainSeries := chart.ContinuousSeries{ mainSeries := chart.ContinuousSeries{
Name: "A test series", Name: "A test series",
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), //generates a []float64 from 1.0 to 100.0 in 1.0 step increments, or 100 elements. XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), //generates a []float64 from 1.0 to 100.0 in 1.0 step increments, or 100 elements.
YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 100 elements. YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 100 elements.
} }
// note we create a LinearRegressionSeries series by assignin the inner series. // note we create a LinearRegressionSeries series by assignin the inner series.

View file

@ -11,8 +11,8 @@ import (
func main() { func main() {
mainSeries := chart.ContinuousSeries{ mainSeries := chart.ContinuousSeries{
Name: "A test series", Name: "A test series",
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(),
YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(50).WithMax(150)}.Values(), YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(50).WithMax(150)}.Values(),
} }
minSeries := &chart.MinSeries{ minSeries := &chart.MinSeries{

View file

@ -17,8 +17,8 @@ func main() {
mainSeries := chart.ContinuousSeries{ mainSeries := chart.ContinuousSeries{
Name: "A test series", Name: "A test series",
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), //generates a []float64 from 1.0 to 100.0 in 1.0 step increments, or 100 elements. XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), //generates a []float64 from 1.0 to 100.0 in 1.0 step increments, or 100 elements.
YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 100 elements. YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 100 elements.
} }
polyRegSeries := &chart.PolynomialRegressionSeries{ polyRegSeries := &chart.PolynomialRegressionSeries{

View file

@ -24,8 +24,8 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
DotWidth: 5, DotWidth: 5,
DotColorProvider: viridisByY, DotColorProvider: viridisByY,
}, },
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(127)}.Values(), XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(127)}.Values(),
YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(128).WithMin(0).WithMax(1024)}.Values(), YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(128).WithMin(0).WithMax(1024)}.Values(),
}, },
}, },
} }
@ -49,8 +49,8 @@ func unit(res http.ResponseWriter, req *http.Request) {
}, },
Series: []chart.Series{ Series: []chart.Series{
chart.ContinuousSeries{ chart.ContinuousSeries{
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(4)}.Values(), XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(4)}.Values(),
YValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(4)}.Values(), YValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(4)}.Values(),
}, },
}, },
} }

View file

@ -11,8 +11,8 @@ import (
func main() { func main() {
mainSeries := chart.ContinuousSeries{ mainSeries := chart.ContinuousSeries{
Name: "A test series", Name: "A test series",
XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), //generates a []float64 from 1.0 to 100.0 in 1.0 step increments, or 100 elements. XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), //generates a []float64 from 1.0 to 100.0 in 1.0 step increments, or 100 elements.
YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 100 elements. YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 100 elements.
} }
// note we create a SimpleMovingAverage series by assignin the inner series. // note we create a SimpleMovingAverage series by assignin the inner series.

View file

@ -68,7 +68,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 = MaxInt(legendContent.Right, right) legendContent.Right = Max(legendContent.Right, right)
labelCount++ labelCount++
} }
} }
@ -163,8 +163,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 = MaxInt(textBox.Height(), textHeight) textHeight = Max(textBox.Height(), textHeight)
textWidth = MaxInt(textBox.Width(), textWidth) textWidth = Max(textBox.Width(), textWidth)
} }
} }
@ -280,7 +280,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 = MaxInt(legendContent.Right, right) legendContent.Right = Max(legendContent.Right, right)
labelCount++ labelCount++
} }
} }

View file

@ -59,7 +59,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 MinInt(lrs.GetLimit(), lrs.InnerSeries.Len()-lrs.GetOffset()) return Min(lrs.GetLimit(), lrs.InnerSeries.Len()-lrs.GetOffset())
} }
// GetLimit returns the window size. // GetLimit returns the window size.
@ -74,7 +74,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 MinInt(windowEnd, innerSeriesLastIndex) return Min(windowEnd, innerSeriesLastIndex)
} }
// GetOffset returns the data offset. // GetOffset returns the data offset.
@ -94,7 +94,7 @@ func (lrs *LinearRegressionSeries) GetValues(index int) (x, y float64) {
lrs.computeCoefficients() lrs.computeCoefficients()
} }
offset := lrs.GetOffset() offset := lrs.GetOffset()
effectiveIndex := MinInt(index+offset, lrs.InnerSeries.Len()) effectiveIndex := Min(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
@ -161,14 +161,14 @@ func (lrs *LinearRegressionSeries) computeCoefficients() {
p := float64(endIndex - startIndex) p := float64(endIndex - startIndex)
xvalues := NewValueBufferWithCapacity(lrs.Len()) xvalues := NewValueBufferWithCapacity[float64](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 = Seq{xvalues}.Average() lrs.avgx = Seq[float64]{xvalues}.Average()
lrs.stddevx = Seq{xvalues}.StdDev() lrs.stddevx = Seq[float64]{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++ {

View file

@ -2,12 +2,12 @@ package chart
// LinearRange returns an array of values representing the range from start to end, incremented by 1.0. // LinearRange returns an array of values representing the range from start to end, incremented by 1.0.
func LinearRange(start, end float64) []float64 { func LinearRange(start, end float64) []float64 {
return Seq{NewLinearSequence().WithStart(start).WithEnd(end).WithStep(1.0)}.Values() return Seq[float64]{NewLinearSequence().WithStart(start).WithEnd(end).WithStep(1.0)}.Values()
} }
// LinearRangeWithStep returns the array values of a linear seq with a given start, end and optional step. // LinearRangeWithStep returns the array values of a linear seq with a given start, end and optional step.
func LinearRangeWithStep(start, end, step float64) []float64 { func LinearRangeWithStep(start, end, step float64) []float64 {
return Seq{NewLinearSequence().WithStart(start).WithEnd(end).WithStep(step)}.Values() return Seq[float64]{NewLinearSequence().WithStart(start).WithEnd(end).WithStep(step)}.Values()
} }
// NewLinearSequence returns a new linear generator. // NewLinearSequence returns a new linear generator.

48
linear_sequence_test.go Normal file
View file

@ -0,0 +1,48 @@
package chart
import (
"testing"
"github.com/wcharczuk/go-chart/v2/testutil"
)
func Test_LinearRange(t *testing.T) {
// replaced new assertions helper
values := LinearRange(1, 100)
testutil.AssertLen(t, values, 100)
testutil.AssertEqual(t, 1, values[0])
testutil.AssertEqual(t, 100, values[99])
}
func Test_LinearRange_WithStep(t *testing.T) {
// replaced new assertions helper
values := LinearRangeWithStep(0, 100, 5)
testutil.AssertEqual(t, 100, values[20])
testutil.AssertLen(t, values, 21)
}
func Test_LinearRange_reversed(t *testing.T) {
// replaced new assertions helper
values := LinearRange(10.0, 1.0)
testutil.AssertEqual(t, 10, len(values))
testutil.AssertEqual(t, 10.0, values[0])
testutil.AssertEqual(t, 1.0, values[9])
}
func Test_LinearSequence_Regression(t *testing.T) {
// replaced new assertions helper
// note; this assumes a 1.0 step is implicitly set in the constructor.
linearProvider := NewLinearSequence().WithStart(1.0).WithEnd(100.0)
testutil.AssertEqual(t, 1, linearProvider.Start())
testutil.AssertEqual(t, 100, linearProvider.End())
testutil.AssertEqual(t, 100, linearProvider.Len())
values := Seq[float64]{linearProvider}.Values()
testutil.AssertLen(t, values, 100)
testutil.AssertEqual(t, 1.0, values[0])
testutil.AssertEqual(t, 100, values[99])
}

View file

@ -1,6 +1,9 @@
package chart package chart
import "math" import (
"constraints"
"math"
)
const ( const (
_pi = math.Pi _pi = math.Pi
@ -17,14 +20,14 @@ const (
) )
// MinMax returns the minimum and maximum of a given set of values. // MinMax returns the minimum and maximum of a given set of values.
func MinMax(values ...float64) (min, max float64) { func MinMax[A constraints.Ordered](values ...A) (min, max A) {
if len(values) == 0 { if len(values) == 0 {
return return
} }
max = values[0] max = values[0]
min = values[0] min = values[0]
var value float64 var value A
for index := 1; index < len(values); index++ { for index := 1; index < len(values); index++ {
value = values[index] value = values[index]
if value < min { if value < min {
@ -38,13 +41,13 @@ func MinMax(values ...float64) (min, max float64) {
} }
// MinInt returns the minimum int. // MinInt returns the minimum int.
func MinInt(values ...int) (min int) { func Min[A constraints.Ordered](values ...A) (min A) {
if len(values) == 0 { if len(values) == 0 {
return return
} }
min = values[0] min = values[0]
var value int var value A
for index := 1; index < len(values); index++ { for index := 1; index < len(values); index++ {
value = values[index] value = values[index]
if value < min { if value < min {
@ -55,13 +58,13 @@ func MinInt(values ...int) (min int) {
} }
// MaxInt returns the maximum int. // MaxInt returns the maximum int.
func MaxInt(values ...int) (max int) { func Max[A constraints.Ordered](values ...A) (max A) {
if len(values) == 0 { if len(values) == 0 {
return return
} }
max = values[0] max = values[0]
var value int var value A
for index := 1; index < len(values); index++ { for index := 1; index < len(values); index++ {
value = values[index] value = values[index]
if value > max { if value > max {
@ -71,9 +74,15 @@ func MaxInt(values ...int) (max int) {
return return
} }
// Number is a type that is a number.
type Number interface {
~int | ~uint | ~float64
}
// AbsInt returns the absolute value of an int. // AbsInt returns the absolute value of an int.
func AbsInt(value int) int { func Abs[A Number](value A) A {
if value < 0 { var zero A
if value < zero {
return -value return -value
} }
return value return value
@ -173,18 +182,12 @@ func Normalize(values ...float64) []float64 {
} }
// Mean returns the mean of a set of values // Mean returns the mean of a set of values
func Mean(values ...float64) float64 { func Mean[A Number](values ...A) A {
return Sum(values...) / float64(len(values)) return Sum(values...) / A(len(values))
}
// MeanInt returns the mean of a set of integer values.
func MeanInt(values ...int) int {
return SumInt(values...) / len(values)
} }
// Sum sums a set of values. // Sum sums a set of values.
func Sum(values ...float64) float64 { func Sum[A Number](values ...A) (total A) {
var total float64
for _, v := range values { for _, v := range values {
total += v total += v
} }

View file

@ -292,7 +292,7 @@ func (m *Matrix) Copy() *Matrix {
// DiagonalVector returns a vector from the diagonal of a matrix. // DiagonalVector returns a vector from the diagonal of a matrix.
func (m *Matrix) DiagonalVector() Vector { func (m *Matrix) DiagonalVector() Vector {
rows, cols := m.Size() rows, cols := m.Size()
rank := minInt(rows, cols) rank := Min(rows, cols)
values := make([]float64, rank) values := make([]float64, rank)
for index := 0; index < rank; index++ { for index := 0; index < rank; index++ {
@ -304,7 +304,7 @@ func (m *Matrix) DiagonalVector() Vector {
// Diagonal returns a matrix from the diagonal of a matrix. // Diagonal returns a matrix from the diagonal of a matrix.
func (m *Matrix) Diagonal() *Matrix { func (m *Matrix) Diagonal() *Matrix {
rows, cols := m.Size() rows, cols := m.Size()
rank := minInt(rows, cols) rank := Min(rows, cols)
m2 := New(rank, rank) m2 := New(rank, rank)
for index := 0; index < rank; index++ { for index := 0; index < rank; index++ {

View file

@ -5,7 +5,7 @@ import (
"strconv" "strconv"
) )
func minInt(values ...int) int { func Min(values ...int) int {
min := math.MaxInt32 min := math.MaxInt32
for x := 0; x < len(values); x++ { for x := 0; x < len(values); x++ {
@ -16,7 +16,7 @@ func minInt(values ...int) int {
return min return min
} }
func maxInt(values ...int) int { func Max(values ...int) int {
max := math.MinInt32 max := math.MinInt32
for x := 0; x < len(values); x++ { for x := 0; x < len(values); x++ {

View file

@ -123,7 +123,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 := MinInt(canvasBox.Width(), canvasBox.Height()) diameter := Min(canvasBox.Width(), canvasBox.Height())
radius := float64(diameter >> 1) radius := float64(diameter >> 1)
labelRadius := (radius * 2.0) / 3.0 labelRadius := (radius * 2.0) / 3.0
@ -191,7 +191,7 @@ func (pc PieChart) getDefaultCanvasBox() Box {
} }
func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box { func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box {
circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height()) circleDiameter := Min(canvasBox.Width(), canvasBox.Height())
square := Box{ square := Box{
Right: circleDiameter, Right: circleDiameter,
@ -237,7 +237,7 @@ func (pc PieChart) stylePieChartValue(index int) Style {
} }
func (pc PieChart) getScaledFontSize() float64 { func (pc PieChart) getScaledFontSize() float64 {
effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) effectiveDimension := Min(pc.GetWidth(), pc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48.0 return 48.0
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {
@ -276,7 +276,7 @@ func (pc PieChart) styleDefaultsTitle() Style {
} }
func (pc PieChart) getTitleFontSize() float64 { func (pc PieChart) getTitleFontSize() float64 {
effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) effectiveDimension := Min(pc.GetWidth(), pc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48 return 48
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {

View file

@ -46,7 +46,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 MinInt(prs.GetLimit(), prs.InnerSeries.Len()-prs.GetOffset()) return Min(prs.GetLimit(), prs.InnerSeries.Len()-prs.GetOffset())
} }
// GetLimit returns the window size. // GetLimit returns the window size.
@ -61,7 +61,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 MinInt(windowEnd, innerSeriesLastIndex) return Min(windowEnd, innerSeriesLastIndex)
} }
// GetOffset returns the data offset. // GetOffset returns the data offset.
@ -101,7 +101,7 @@ func (prs *PolynomialRegressionSeries) GetValues(index int) (x, y float64) {
} }
offset := prs.GetOffset() offset := prs.GetOffset()
effectiveIndex := MinInt(index+offset, prs.InnerSeries.Len()) effectiveIndex := Min(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

View file

@ -7,17 +7,17 @@ import (
) )
var ( var (
_ Sequence = (*RandomSeq)(nil) _ Sequence[float64] = (*RandomSeq)(nil)
) )
// RandomValues returns an array of random values. // RandomValues returns an array of random values.
func RandomValues(count int) []float64 { func RandomValues(count int) []float64 {
return Seq{NewRandomSequence().WithLen(count)}.Values() return Seq[float64]{NewRandomSequence().WithLen(count)}.Values()
} }
// RandomValuesWithMax returns an array of random values with a given average. // RandomValuesWithMax returns an array of random values with a given average.
func RandomValuesWithMax(count int, max float64) []float64 { func RandomValuesWithMax(count int, max float64) []float64 {
return Seq{NewRandomSequence().WithMax(max).WithLen(count)}.Values() return Seq[float64]{NewRandomSequence().WithMax(max).WithLen(count)}.Values()
} }
// NewRandomSequence creates a new random seq. // NewRandomSequence creates a new random seq.

130
seq.go
View file

@ -5,29 +5,18 @@ import (
"sort" "sort"
) )
// ValueSequence returns a sequence for a given values set.
func ValueSequence(values ...float64) Seq {
return Seq{NewArray(values...)}
}
// Sequence is a provider for values for a seq.
type Sequence interface {
Len() int
GetValue(int) float64
}
// Seq is a utility wrapper for seq providers. // Seq is a utility wrapper for seq providers.
type Seq struct { type Seq[A Number] struct {
Sequence Sequence[A]
} }
// Values enumerates the seq into a slice. // Values enumerates the seq into a slice.
func (s Seq) Values() (output []float64) { func (s Seq[A]) Values() (output []A) {
if s.Len() == 0 { if s.Len() == 0 {
return return
} }
output = make([]float64, s.Len()) output = make([]A, s.Len())
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {
output[i] = s.GetValue(i) output[i] = s.GetValue(i)
} }
@ -35,7 +24,7 @@ func (s Seq) Values() (output []float64) {
} }
// 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[A]) Each(mapfn func(int, A)) {
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {
mapfn(i, s.GetValue(i)) mapfn(i, s.GetValue(i))
} }
@ -43,55 +32,56 @@ func (s Seq) Each(mapfn func(int, float64)) {
// Map applies the `mapfn` to all values in the value provider, // Map applies the `mapfn` to all values in the value provider,
// returning a new seq. // returning a new seq.
func (s Seq) Map(mapfn func(i int, v float64) float64) Seq { func (s Seq[A]) Map(mapfn func(i int, v A) A) Seq[A] {
output := make([]float64, s.Len()) output := make([]A, s.Len())
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {
mapfn(i, s.GetValue(i)) mapfn(i, s.GetValue(i))
} }
return Seq{Array(output)} return Seq[A]{Sequence: Array(output...)}
} }
// FoldLeft collapses a seq from left to right. // FoldLeft collapses a seq from left to right.
func (s Seq) FoldLeft(mapfn func(i int, v0, v float64) float64) (v0 float64) { func (s Seq[A]) FoldLeft(mapfn func(i int, v0, v A) A) (output A) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return
} }
if s.Len() == 1 { if s.Len() == 1 {
return s.GetValue(0) return s.GetValue(0)
} }
v0 = s.GetValue(0) output = s.GetValue(0)
for i := 1; i < s.Len(); i++ { for i := 1; i < s.Len(); i++ {
v0 = mapfn(i, v0, s.GetValue(i)) output = mapfn(i, output, s.GetValue(i))
} }
return return
} }
// FoldRight collapses a seq from right to left. // FoldRight collapses a seq from right to left.
func (s Seq) FoldRight(mapfn func(i int, v0, v float64) float64) (v0 float64) { func (s Seq[A]) FoldRight(mapfn func(i int, v0, v A) A) (output A) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return
} }
if s.Len() == 1 { if s.Len() == 1 {
return s.GetValue(0) return s.GetValue(0)
} }
v0 = s.GetValue(s.Len() - 1) output = s.GetValue(s.Len() - 1)
for i := s.Len() - 2; i >= 0; i-- { for i := s.Len() - 2; i >= 0; i-- {
v0 = mapfn(i, v0, s.GetValue(i)) output = mapfn(i, output, s.GetValue(i))
} }
return return
} }
// Min returns the minimum value in the seq. // Min returns the minimum value in the seq.
func (s Seq) Min() float64 { func (s Seq[A]) Min() (min A) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return
} }
min := s.GetValue(0)
var value float64 min = s.GetValue(0)
var value A
for i := 1; i < s.Len(); i++ { for i := 1; i < s.Len(); i++ {
value = s.GetValue(i) value = s.GetValue(i)
if value < min { if value < min {
@ -102,12 +92,12 @@ func (s Seq) Min() float64 {
} }
// Max returns the maximum value in the seq. // Max returns the maximum value in the seq.
func (s Seq) Max() float64 { func (s Seq[A]) Max() (max A) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return
} }
max := s.GetValue(0) max = s.GetValue(0)
var value float64 var value A
for i := 1; i < s.Len(); i++ { for i := 1; i < s.Len(); i++ {
value = s.GetValue(i) value = s.GetValue(i)
if value > max { if value > max {
@ -118,13 +108,13 @@ func (s Seq) Max() float64 {
} }
// MinMax returns the minimum and the maximum in one pass. // MinMax returns the minimum and the maximum in one pass.
func (s Seq) MinMax() (min, max float64) { func (s Seq[A]) MinMax() (min, max A) {
if s.Len() == 0 { if s.Len() == 0 {
return return
} }
min = s.GetValue(0) min = s.GetValue(0)
max = min max = min
var value float64 var value A
for i := 1; i < s.Len(); i++ { for i := 1; i < s.Len(); i++ {
value = s.GetValue(i) value = s.GetValue(i)
if value < min { if value < min {
@ -139,17 +129,19 @@ func (s Seq) MinMax() (min, max float64) {
// Sort returns the seq sorted in ascending order. // Sort returns the seq sorted in ascending order.
// This fully enumerates the seq. // This fully enumerates the seq.
func (s Seq) Sort() Seq { func (s Seq[A]) Sort() Seq[A] {
if s.Len() == 0 { if s.Len() == 0 {
return s return s
} }
values := s.Values() values := s.Values()
sort.Float64s(values) sort.Slice(values, func(i, j int) bool {
return Seq{Array(values)} return values[i] < values[j]
})
return Seq[A]{Array(values...)}
} }
// Reverse reverses the sequence // Reverse reverses the sequence
func (s Seq) Reverse() Seq { func (s Seq[A]) Reverse() Seq[A] {
if s.Len() == 0 { if s.Len() == 0 {
return s return s
} }
@ -158,7 +150,7 @@ func (s Seq) Reverse() Seq {
valuesLen := len(values) valuesLen := len(values)
valuesLen1 := len(values) - 1 valuesLen1 := len(values) - 1
valuesLen2 := valuesLen >> 1 valuesLen2 := valuesLen >> 1
var i, j float64 var i, j A
for index := 0; index < valuesLen2; index++ { for index := 0; index < valuesLen2; index++ {
i = values[index] i = values[index]
j = values[valuesLen1-index] j = values[valuesLen1-index]
@ -166,32 +158,13 @@ func (s Seq) Reverse() Seq {
values[valuesLen1-index] = i values[valuesLen1-index] = i
} }
return Seq{Array(values)} return Seq[A]{Array(values...)}
}
// Median returns the median or middle value in the sorted seq.
func (s Seq) Median() (median float64) {
l := s.Len()
if l == 0 {
return
}
sorted := s.Sort()
if l%2 == 0 {
v0 := sorted.GetValue(l/2 - 1)
v1 := sorted.GetValue(l/2 + 1)
median = (v0 + v1) / 2
} else {
median = float64(sorted.GetValue(l << 1))
}
return
} }
// Sum adds all the elements of a series together. // Sum adds all the elements of a series together.
func (s Seq) Sum() (accum float64) { func (s Seq[A]) Sum() (accum A) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return
} }
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {
@ -201,24 +174,25 @@ func (s Seq) Sum() (accum float64) {
} }
// Average returns the float average of the values in the buffer. // Average returns the float average of the values in the buffer.
func (s Seq) Average() float64 { func (s Seq[A]) Average() (avg float64) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return
} }
return s.Sum() / float64(s.Len()) avg = float64(s.Sum()) / float64((s.Len()))
return
} }
// Variance computes the variance of the buffer. // Variance computes the variance of the buffer.
func (s Seq) Variance() float64 { func (s Seq[A]) Variance() (variance float64) {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return 0
} }
m := s.Average() m := s.Average()
var variance, v float64 var v float64
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {
v = s.GetValue(i) v = float64(s.GetValue(i))
variance += (v - m) * (v - m) variance += (v - m) * (v - m)
} }
@ -226,24 +200,24 @@ func (s Seq) Variance() float64 {
} }
// StdDev returns the standard deviation. // StdDev returns the standard deviation.
func (s Seq) StdDev() float64 { func (s Seq[A]) StdDev() float64 {
if s.Len() == 0 { if s.Len() == 0 {
return 0 return 0
} }
return math.Pow(s.Variance(), 0.5) return math.Pow(float64(s.Variance()), 0.5)
} }
//Percentile finds the relative standing in a slice of floats. //Percentile finds the relative standing in a slice of floats.
// `percent` should be given on the interval [0,1.0). // `percent` should be given on the interval [0,1.0).
func (s Seq) Percentile(percent float64) (percentile float64) { func (s Seq[A]) Percentile(percent float64) (percentile A) {
l := s.Len() l := s.Len()
if l == 0 { if l == 0 {
return 0 return 0
} }
if percent < 0 || percent > 1.0 { if percent < 0 || percent > 1.0 {
panic("percent out of range [0.0, 1.0)") panic("percentile percent out of range [0.0, 1.0)")
} }
sorted := s.Sort() sorted := s.Sort()
@ -262,14 +236,14 @@ func (s Seq) Percentile(percent float64) (percentile float64) {
} }
// Normalize maps every value to the interval [0, 1.0]. // Normalize maps every value to the interval [0, 1.0].
func (s Seq) Normalize() Seq { func (s Seq[A]) Normalize() Seq[float64] {
min, max := s.MinMax() min, max := s.MinMax()
delta := max - min delta := float64(max - min)
output := make([]float64, s.Len()) output := make([]float64, s.Len())
for i := 0; i < s.Len(); i++ { for i := 0; i < s.Len(); i++ {
output[i] = (s.GetValue(i) - min) / delta output[i] = (float64(s.GetValue(i)) - float64(min)) / delta
} }
return Seq{Array(output)} return Seq[float64]{Array(output...)}
} }

View file

@ -6,95 +6,69 @@ import (
"github.com/wcharczuk/go-chart/v2/testutil" "github.com/wcharczuk/go-chart/v2/testutil"
) )
func TestSeqEach(t *testing.T) { func Test_Seq_Each(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4)}
values.Each(func(i int, v int) {
values := Seq{NewArray(1, 2, 3, 4)}
values.Each(func(i int, v float64) {
testutil.AssertEqual(t, i, v-1) testutil.AssertEqual(t, i, v-1)
}) })
} }
func TestSeqMap(t *testing.T) { func Test_Seq_Map(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4)}
mapped := values.Map(func(i int, v int) int {
values := Seq{NewArray(1, 2, 3, 4)}
mapped := values.Map(func(i int, v float64) float64 {
testutil.AssertEqual(t, i, v-1) testutil.AssertEqual(t, i, v-1)
return v * 2 return v * 2
}) })
testutil.AssertEqual(t, 4, mapped.Len()) testutil.AssertEqual(t, 4, mapped.Len())
} }
func TestSeqFoldLeft(t *testing.T) { func Test_Seq_FoldLeft(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4)}
ten := values.FoldLeft(func(_ int, vp, v int) int {
values := Seq{NewArray(1, 2, 3, 4)}
ten := values.FoldLeft(func(_ int, vp, v float64) float64 {
return vp + v return vp + v
}) })
testutil.AssertEqual(t, 10, ten) testutil.AssertEqual(t, 10, ten)
orderTest := Seq{NewArray(10, 3, 2, 1)} orderTest := Seq[int]{Array(10, 3, 2, 1)}
four := orderTest.FoldLeft(func(_ int, vp, v float64) float64 { four := orderTest.FoldLeft(func(_ int, vp, v int) int {
return vp - v return vp - v
}) })
testutil.AssertEqual(t, 4, four) testutil.AssertEqual(t, 4, four)
} }
func TestSeqFoldRight(t *testing.T) { func Test_Seq_FoldRight(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4)}
ten := values.FoldRight(func(_ int, vp, v int) int {
values := Seq{NewArray(1, 2, 3, 4)}
ten := values.FoldRight(func(_ int, vp, v float64) float64 {
return vp + v return vp + v
}) })
testutil.AssertEqual(t, 10, ten) testutil.AssertEqual(t, 10, ten)
orderTest := Seq{NewArray(10, 3, 2, 1)} orderTest := Seq[int]{Array(10, 3, 2, 1)}
notFour := orderTest.FoldRight(func(_ int, vp, v float64) float64 { notFour := orderTest.FoldRight(func(_ int, vp, v int) int {
return vp - v return vp - v
}) })
testutil.AssertEqual(t, -14, notFour) testutil.AssertEqual(t, -14, notFour)
} }
func TestSeqSum(t *testing.T) { func Test_Seq_Sum(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4)}
values := Seq{NewArray(1, 2, 3, 4)}
testutil.AssertEqual(t, 10, values.Sum()) testutil.AssertEqual(t, 10, values.Sum())
} }
func TestSeqAverage(t *testing.T) { func Test_Seq_Average(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4)}
values := Seq{NewArray(1, 2, 3, 4)}
testutil.AssertEqual(t, 2.5, values.Average()) testutil.AssertEqual(t, 2.5, values.Average())
valuesOdd := Seq{NewArray(1, 2, 3, 4, 5)} valuesOdd := Seq[int]{Array(1, 2, 3, 4, 5)}
testutil.AssertEqual(t, 3, valuesOdd.Average()) testutil.AssertEqual(t, 3, valuesOdd.Average())
} }
func TestSequenceVariance(t *testing.T) { func Test_Seq_Variance(t *testing.T) {
// replaced new assertions helper values := Seq[int]{Array(1, 2, 3, 4, 5)}
values := Seq{NewArray(1, 2, 3, 4, 5)}
testutil.AssertEqual(t, 2, values.Variance()) testutil.AssertEqual(t, 2, values.Variance())
} }
func TestSequenceNormalize(t *testing.T) { func Test_LinearRange(t *testing.T) {
// replaced new assertions helper
normalized := ValueSequence(1, 2, 3, 4, 5).Normalize().Values()
testutil.AssertNotEmpty(t, normalized)
testutil.AssertLen(t, normalized, 5)
testutil.AssertEqual(t, 0, normalized[0])
testutil.AssertEqual(t, 0.25, normalized[1])
testutil.AssertEqual(t, 1, normalized[4])
}
func TestLinearRange(t *testing.T) {
// replaced new assertions helper // replaced new assertions helper
values := LinearRange(1, 100) values := LinearRange(1, 100)
@ -103,7 +77,7 @@ func TestLinearRange(t *testing.T) {
testutil.AssertEqual(t, 100, values[99]) testutil.AssertEqual(t, 100, values[99])
} }
func TestLinearRangeWithStep(t *testing.T) { func Test_LinearRange_WithStep(t *testing.T) {
// replaced new assertions helper // replaced new assertions helper
values := LinearRangeWithStep(0, 100, 5) values := LinearRangeWithStep(0, 100, 5)
@ -111,7 +85,7 @@ func TestLinearRangeWithStep(t *testing.T) {
testutil.AssertLen(t, values, 21) testutil.AssertLen(t, values, 21)
} }
func TestLinearRangeReversed(t *testing.T) { func Test_LinearRange_reversed(t *testing.T) {
// replaced new assertions helper // replaced new assertions helper
values := LinearRange(10.0, 1.0) values := LinearRange(10.0, 1.0)
@ -120,7 +94,7 @@ func TestLinearRangeReversed(t *testing.T) {
testutil.AssertEqual(t, 1.0, values[9]) testutil.AssertEqual(t, 1.0, values[9])
} }
func TestLinearSequenceRegression(t *testing.T) { func Test_LinearSequence_Regression(t *testing.T) {
// replaced new assertions helper // replaced new assertions helper
// note; this assumes a 1.0 step is implicitly set in the constructor. // note; this assumes a 1.0 step is implicitly set in the constructor.
@ -129,7 +103,7 @@ func TestLinearSequenceRegression(t *testing.T) {
testutil.AssertEqual(t, 100, linearProvider.End()) testutil.AssertEqual(t, 100, linearProvider.End())
testutil.AssertEqual(t, 100, linearProvider.Len()) testutil.AssertEqual(t, 100, linearProvider.Len())
values := Seq{linearProvider}.Values() values := Seq[float64]{linearProvider}.Values()
testutil.AssertLen(t, values, 100) testutil.AssertLen(t, values, 100)
testutil.AssertEqual(t, 1.0, values[0]) testutil.AssertEqual(t, 1.0, values[0])
testutil.AssertEqual(t, 100, values[99]) testutil.AssertEqual(t, 100, values[99])

7
sequence.go Normal file
View file

@ -0,0 +1,7 @@
package chart
// Sequence is a provider for values for a seq.
type Sequence[A any] interface {
Len() int
GetValue(int) A
}

View file

@ -94,7 +94,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 := MaxInt(0, index-period) floor := Max(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-- {

View file

@ -171,7 +171,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: MinInt(yoffset+barHeight, canvasBox.Bottom-DefaultStrokeWidth), Bottom: Min(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
@ -220,7 +220,7 @@ func (sbc StackedBarChart) drawHorizontalBar(r Renderer, canvasBox Box, yoffset
barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width()))) barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width())))
barBox := Box{ barBox := Box{
Top: boxTop, Top: boxTop,
Left: MinInt(xOffset-barHeight, canvasBox.Left+DefaultStrokeWidth), Left: Min(xOffset-barHeight, canvasBox.Left+DefaultStrokeWidth),
Right: xOffset, Right: xOffset,
Bottom: boxBottom, Bottom: boxBottom,
} }
@ -455,7 +455,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 = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight) xaxisHeight = Max(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
} }
} }
return Box{ return Box{
@ -498,7 +498,7 @@ func (sbc StackedBarChart) getHorizontalAdjustedCanvasBox(r Renderer, canvasBox
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)
yAxisWidth = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), yAxisWidth) yAxisWidth = Max(linesBox.Height()+(2*DefaultXAxisMargin), yAxisWidth)
} }
} }
return Box{ return Box{
@ -552,7 +552,7 @@ func (sbc StackedBarChart) styleDefaultsTitle() Style {
} }
func (sbc StackedBarChart) getScaledFontSize() float64 { func (sbc StackedBarChart) getScaledFontSize() float64 {
effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight()) effectiveDimension := Min(sbc.GetWidth(), sbc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48.0 return 48.0
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {
@ -566,7 +566,7 @@ func (sbc StackedBarChart) getScaledFontSize() float64 {
} }
func (sbc StackedBarChart) getTitleFontSize() float64 { func (sbc StackedBarChart) getTitleFontSize() float64 {
effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight()) effectiveDimension := Min(sbc.GetWidth(), sbc.GetHeight())
if effectiveDimension >= 2048 { if effectiveDimension >= 2048 {
return 48 return 48
} else if effectiveDimension >= 1024 { } else if effectiveDimension >= 1024 {

View file

@ -27,7 +27,7 @@ func AssertNotNil(t *testing.T, actual interface{}, message ...interface{}) {
} }
// AssertEqual asserts two arguments are equal. // AssertEqual asserts two arguments are equal.
func AssertEqual(t *testing.T, expected, actual interface{}, message ...interface{}) { func AssertEqual[A any](t *testing.T, expected, actual A, message ...interface{}) {
if !equal(expected, actual) { if !equal(expected, actual) {
t.Errorf("assertion failed; expected %v to equal %v", actual, expected) t.Errorf("assertion failed; expected %v to equal %v", actual, expected)
if len(message) > 0 { if len(message) > 0 {
@ -38,7 +38,7 @@ func AssertEqual(t *testing.T, expected, actual interface{}, message ...interfac
} }
// AssertNotEqual asserts two arguments are not equal. // AssertNotEqual asserts two arguments are not equal.
func AssertNotEqual(t *testing.T, expected, actual interface{}, message ...interface{}) { func AssertNotEqual[A any](t *testing.T, expected, actual A, message ...interface{}) {
if equal(expected, actual) { if equal(expected, actual) {
t.Errorf("assertion failed; expected %v to not equal %v", actual, expected) t.Errorf("assertion failed; expected %v to not equal %v", actual, expected)
if len(message) > 0 { if len(message) > 0 {

View file

@ -147,7 +147,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 = MaxInt(lineBox.Right, output.Right) output.Right = Max(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()

View file

@ -84,7 +84,7 @@ func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style,
tickStep := rangeDelta / float64(intermediateTickCount) tickStep := rangeDelta / float64(intermediateTickCount)
roundTo := GetRoundToForDelta(rangeDelta) / 10 roundTo := GetRoundToForDelta(rangeDelta) / 10
intermediateTickCount = MinInt(intermediateTickCount, DefaultTickCountSanityCheck) intermediateTickCount = Min(intermediateTickCount, DefaultTickCountSanityCheck)
for x := 1; x < intermediateTickCount; x++ { for x := 1; x < intermediateTickCount; x++ {
var tickValue float64 var tickValue float64

View file

@ -7,7 +7,7 @@ import (
// Assert types implement interfaces. // Assert types implement interfaces.
var ( var (
_ Sequence = (*Times)(nil) _ Sequence[float64] = (*Times)(nil)
_ sort.Interface = (*Times)(nil) _ sort.Interface = (*Times)(nil)
) )

View file

@ -1,6 +1,7 @@
package chart package chart
import ( import (
"constraints"
"fmt" "fmt"
"strings" "strings"
) )
@ -13,14 +14,14 @@ const (
) )
// NewValueBuffer creates a new value buffer with an optional set of values. // NewValueBuffer creates a new value buffer with an optional set of values.
func NewValueBuffer(values ...float64) *ValueBuffer { func NewValueBuffer[A constraints.Ordered](values ...A) *ValueBuffer[A] {
var tail int var tail int
array := make([]float64, MaxInt(len(values), bufferDefaultCapacity)) array := make([]A, Max(len(values), bufferDefaultCapacity))
if len(values) > 0 { if len(values) > 0 {
copy(array, values) copy(array, values)
tail = len(values) tail = len(values)
} }
return &ValueBuffer{ return &ValueBuffer[A]{
array: array, array: array,
head: 0, head: 0,
tail: tail, tail: tail,
@ -29,9 +30,9 @@ func NewValueBuffer(values ...float64) *ValueBuffer {
} }
// NewValueBufferWithCapacity creates a new ValueBuffer pre-allocated with the given capacity. // NewValueBufferWithCapacity creates a new ValueBuffer pre-allocated with the given capacity.
func NewValueBufferWithCapacity(capacity int) *ValueBuffer { func NewValueBufferWithCapacity[A any](capacity int) *ValueBuffer[A] {
return &ValueBuffer{ return &ValueBuffer[A]{
array: make([]float64, capacity), array: make([]A, capacity),
head: 0, head: 0,
tail: 0, tail: 0,
size: 0, size: 0,
@ -42,8 +43,8 @@ func NewValueBufferWithCapacity(capacity int) *ValueBuffer {
// Instead of allocating a whole new node object for each element, array elements are re-used (which saves GC churn). // Instead of allocating a whole new node object for each element, array elements are re-used (which saves GC churn).
// Enqueue can be O(n), Dequeue is generally O(1). // Enqueue can be O(n), Dequeue is generally O(1).
// Buffer implements `seq.Provider` // Buffer implements `seq.Provider`
type ValueBuffer struct { type ValueBuffer[A any] struct {
array []float64 array []A
head int head int
tail int tail int
size int size int
@ -51,24 +52,24 @@ type ValueBuffer struct {
// Len returns the length of the Buffer (as it is currently populated). // Len returns the length of the Buffer (as it is currently populated).
// Actual memory footprint may be different. // Actual memory footprint may be different.
func (b *ValueBuffer) Len() int { func (b *ValueBuffer[A]) Len() int {
return b.size return b.size
} }
// GetValue implements seq provider. // GetValue implements seq provider.
func (b *ValueBuffer) GetValue(index int) float64 { func (b *ValueBuffer[A]) GetValue(index int) A {
effectiveIndex := (b.head + index) % len(b.array) effectiveIndex := (b.head + index) % len(b.array)
return b.array[effectiveIndex] return b.array[effectiveIndex]
} }
// Capacity returns the total size of the Buffer, including empty elements. // Capacity returns the total size of the Buffer, including empty elements.
func (b *ValueBuffer) Capacity() int { func (b *ValueBuffer[A]) Capacity() int {
return len(b.array) return len(b.array)
} }
// SetCapacity sets the capacity of the Buffer. // SetCapacity sets the capacity of the Buffer.
func (b *ValueBuffer) SetCapacity(capacity int) { func (b *ValueBuffer[A]) SetCapacity(capacity int) {
newArray := make([]float64, capacity) newArray := make([]A, capacity)
if b.size > 0 { if b.size > 0 {
if b.head < b.tail { if b.head < b.tail {
arrayCopy(b.array, b.head, newArray, 0, b.size) arrayCopy(b.array, b.head, newArray, 0, b.size)
@ -87,15 +88,15 @@ func (b *ValueBuffer) SetCapacity(capacity int) {
} }
// Clear removes all objects from the Buffer. // Clear removes all objects from the Buffer.
func (b *ValueBuffer) Clear() { func (b *ValueBuffer[A]) Clear() {
b.array = make([]float64, bufferDefaultCapacity) b.array = make([]A, bufferDefaultCapacity)
b.head = 0 b.head = 0
b.tail = 0 b.tail = 0
b.size = 0 b.size = 0
} }
// Enqueue adds an element to the "back" of the Buffer. // Enqueue adds an element to the "back" of the Buffer.
func (b *ValueBuffer) Enqueue(value float64) { func (b *ValueBuffer[A]) Enqueue(value A) {
if b.size == len(b.array) { if b.size == len(b.array) {
newCapacity := int(len(b.array) * int(bufferGrowFactor/100)) newCapacity := int(len(b.array) * int(bufferGrowFactor/100))
if newCapacity < (len(b.array) + bufferMinimumGrow) { if newCapacity < (len(b.array) + bufferMinimumGrow) {
@ -110,38 +111,41 @@ func (b *ValueBuffer) Enqueue(value float64) {
} }
// Dequeue removes the first element from the RingBuffer. // Dequeue removes the first element from the RingBuffer.
func (b *ValueBuffer) Dequeue() float64 { func (b *ValueBuffer[A]) Dequeue() (output A) {
if b.size == 0 { if b.size == 0 {
return 0 return
} }
removed := b.array[b.head] output = b.array[b.head]
b.head = (b.head + 1) % len(b.array) b.head = (b.head + 1) % len(b.array)
b.size-- b.size--
return removed return
} }
// Peek returns but does not remove the first element. // Peek returns but does not remove the first element.
func (b *ValueBuffer) Peek() float64 { func (b *ValueBuffer[A]) Peek() (output A) {
if b.size == 0 { if b.size == 0 {
return 0 return
} }
return b.array[b.head] output = b.array[b.head]
return
} }
// PeekBack returns but does not remove the last element. // PeekBack returns but does not remove the last element.
func (b *ValueBuffer) PeekBack() float64 { func (b *ValueBuffer[A]) PeekBack() (output A) {
if b.size == 0 { if b.size == 0 {
return 0 return
} }
if b.tail == 0 { if b.tail == 0 {
return b.array[len(b.array)-1] output = b.array[len(b.array)-1]
return
} }
return b.array[b.tail-1] output = b.array[b.tail-1]
return
} }
// TrimExcess resizes the capacity of the buffer to better fit the contents. // TrimExcess resizes the capacity of the buffer to better fit the contents.
func (b *ValueBuffer) TrimExcess() { func (b *ValueBuffer[A]) TrimExcess() {
threshold := float64(len(b.array)) * 0.9 threshold := float64(len(b.array)) * 0.9
if b.size < int(threshold) { if b.size < int(threshold) {
b.SetCapacity(b.size) b.SetCapacity(b.size)
@ -149,8 +153,8 @@ func (b *ValueBuffer) TrimExcess() {
} }
// Array returns the ring buffer, in order, as an array. // Array returns the ring buffer, in order, as an array.
func (b *ValueBuffer) Array() Array { func (b *ValueBuffer[A]) Array() []A {
newArray := make([]float64, b.size) newArray := make([]A, b.size)
if b.size == 0 { if b.size == 0 {
return newArray return newArray
@ -163,11 +167,11 @@ func (b *ValueBuffer) Array() Array {
arrayCopy(b.array, 0, newArray, len(b.array)-b.head, b.tail) arrayCopy(b.array, 0, newArray, len(b.array)-b.head, b.tail)
} }
return Array(newArray) return newArray
} }
// Each calls the consumer for each element in the buffer. // Each calls the consumer for each element in the buffer.
func (b *ValueBuffer) Each(mapfn func(int, float64)) { func (b *ValueBuffer[A]) Each(mapfn func(int, A)) {
if b.size == 0 { if b.size == 0 {
return return
} }
@ -191,10 +195,10 @@ func (b *ValueBuffer) Each(mapfn func(int, float64)) {
} }
// String returns a string representation for value buffers. // String returns a string representation for value buffers.
func (b *ValueBuffer) String() string { func (b *ValueBuffer[A]) String() string {
var values []string var values []string
for _, elem := range b.Array() { for _, elem := range b.Array() {
values = append(values, fmt.Sprintf("%v", elem)) values = append(values, fmt.Sprint(elem))
} }
return strings.Join(values, " <= ") return strings.Join(values, " <= ")
} }
@ -203,14 +207,15 @@ func (b *ValueBuffer) String() string {
// Util methods // Util methods
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
func arrayClear(source []float64, index, length int) { func arrayClear[A any](source []A, index, length int) {
var zero A
for x := 0; x < length; x++ { for x := 0; x < length; x++ {
absoluteIndex := x + index absoluteIndex := x + index
source[absoluteIndex] = 0 source[absoluteIndex] = zero
} }
} }
func arrayCopy(source []float64, sourceIndex int, destination []float64, destinationIndex, length int) { func arrayCopy[A any](source []A, sourceIndex int, destination []A, destinationIndex, length int) {
for x := 0; x < length; x++ { for x := 0; x < length; x++ {
from := sourceIndex + x from := sourceIndex + x
to := destinationIndex + x to := destinationIndex + x

View file

@ -6,10 +6,8 @@ import (
"github.com/wcharczuk/go-chart/v2/testutil" "github.com/wcharczuk/go-chart/v2/testutil"
) )
func TestBuffer(t *testing.T) { func Test_ValueBuffer(t *testing.T) {
// replaced new assertions helper buffer := NewValueBuffer[int]()
buffer := NewValueBuffer()
buffer.Enqueue(1) buffer.Enqueue(1)
testutil.AssertEqual(t, 1, buffer.Len()) testutil.AssertEqual(t, 1, buffer.Len())
@ -100,10 +98,8 @@ func TestBuffer(t *testing.T) {
testutil.AssertZero(t, buffer.PeekBack()) testutil.AssertZero(t, buffer.PeekBack())
} }
func TestBufferClear(t *testing.T) { func Test_ValueBufferClear(t *testing.T) {
// replaced new assertions helper buffer := NewValueBuffer[int]()
buffer := NewValueBuffer()
buffer.Enqueue(1) buffer.Enqueue(1)
buffer.Enqueue(1) buffer.Enqueue(1)
buffer.Enqueue(1) buffer.Enqueue(1)
@ -121,10 +117,8 @@ func TestBufferClear(t *testing.T) {
testutil.AssertZero(t, buffer.PeekBack()) testutil.AssertZero(t, buffer.PeekBack())
} }
func TestBufferArray(t *testing.T) { func Test_ValueBuffer_Array(t *testing.T) {
// replaced new assertions helper buffer := NewValueBuffer[int]()
buffer := NewValueBuffer()
buffer.Enqueue(1) buffer.Enqueue(1)
buffer.Enqueue(2) buffer.Enqueue(2)
buffer.Enqueue(3) buffer.Enqueue(3)
@ -140,10 +134,8 @@ func TestBufferArray(t *testing.T) {
testutil.AssertEqual(t, 5, contents[4]) testutil.AssertEqual(t, 5, contents[4])
} }
func TestBufferEach(t *testing.T) { func Test_ValueBuffer_Each(t *testing.T) {
// replaced new assertions helper buffer := NewValueBuffer[float64]()
buffer := NewValueBuffer()
for x := 1; x < 17; x++ { for x := 1; x < 17; x++ {
buffer.Enqueue(float64(x)) buffer.Enqueue(float64(x))
@ -159,10 +151,8 @@ func TestBufferEach(t *testing.T) {
testutil.AssertEqual(t, 16, called) testutil.AssertEqual(t, 16, called)
} }
func TestNewBuffer(t *testing.T) { func Test_NewValueBuffer(t *testing.T) {
// replaced new assertions helper empty := NewValueBuffer[int]()
empty := NewValueBuffer()
testutil.AssertNotNil(t, empty) testutil.AssertNotNil(t, empty)
testutil.AssertZero(t, empty.Len()) testutil.AssertZero(t, empty.Len())
testutil.AssertEqual(t, bufferDefaultCapacity, empty.Capacity()) testutil.AssertEqual(t, bufferDefaultCapacity, empty.Capacity())
@ -170,9 +160,7 @@ func TestNewBuffer(t *testing.T) {
testutil.AssertZero(t, empty.PeekBack()) testutil.AssertZero(t, empty.PeekBack())
} }
func TestNewBufferWithValues(t *testing.T) { func Test_NewBuffer_withValues(t *testing.T) {
// replaced new assertions helper
values := NewValueBuffer(1, 2, 3, 4, 5) values := NewValueBuffer(1, 2, 3, 4, 5)
testutil.AssertNotNil(t, values) testutil.AssertNotNil(t, values)
testutil.AssertEqual(t, 5, values.Len()) testutil.AssertEqual(t, 5, values.Len())
@ -180,10 +168,8 @@ func TestNewBufferWithValues(t *testing.T) {
testutil.AssertEqual(t, 5, values.PeekBack()) testutil.AssertEqual(t, 5, values.PeekBack())
} }
func TestBufferGrowth(t *testing.T) { func Test_ValueBuffer_growth(t *testing.T) {
// replaced new assertions helper values := NewValueBuffer[float64](1, 2, 3, 4, 5)
values := NewValueBuffer(1, 2, 3, 4, 5)
for i := 0; i < 1<<10; i++ { for i := 0; i < 1<<10; i++ {
values.Enqueue(float64(i)) values.Enqueue(float64(i))
} }

8
value_sequence.go Normal file
View file

@ -0,0 +1,8 @@
package chart
// ValueSequence returns a sequence for a given values set.
func ValueSequence(values ...float64) Seq[float64] {
return Seq[float64]{
Array(values...),
}
}

19
value_sequence_test.go Normal file
View file

@ -0,0 +1,19 @@
package chart
import (
"testing"
"github.com/wcharczuk/go-chart/v2/testutil"
)
func Test_ValueSequence_Normalize(t *testing.T) {
// replaced new assertions helper
normalized := ValueSequence(1, 2, 3, 4, 5).Normalize().Values()
testutil.AssertNotEmpty(t, normalized)
testutil.AssertLen(t, normalized, 5)
testutil.AssertEqual(t, 0, normalized[0])
testutil.AssertEqual(t, 0.25, normalized[1])
testutil.AssertEqual(t, 1, normalized[4])
}

View file

@ -3,262 +3,262 @@ package chart
import "github.com/wcharczuk/go-chart/v2/drawing" import "github.com/wcharczuk/go-chart/v2/drawing"
var viridisColors = [256]drawing.Color{ var viridisColors = [256]drawing.Color{
drawing.Color{R: 0x44, G: 0x1, B: 0x54, A: 0xff}, {R: 0x44, G: 0x1, B: 0x54, A: 0xff},
drawing.Color{R: 0x44, G: 0x2, B: 0x55, A: 0xff}, {R: 0x44, G: 0x2, B: 0x55, A: 0xff},
drawing.Color{R: 0x45, G: 0x3, B: 0x57, A: 0xff}, {R: 0x45, G: 0x3, B: 0x57, A: 0xff},
drawing.Color{R: 0x45, G: 0x5, B: 0x58, A: 0xff}, {R: 0x45, G: 0x5, B: 0x58, A: 0xff},
drawing.Color{R: 0x45, G: 0x6, B: 0x5a, A: 0xff}, {R: 0x45, G: 0x6, B: 0x5a, A: 0xff},
drawing.Color{R: 0x46, G: 0x8, B: 0x5b, A: 0xff}, {R: 0x46, G: 0x8, B: 0x5b, A: 0xff},
drawing.Color{R: 0x46, G: 0x9, B: 0x5d, A: 0xff}, {R: 0x46, G: 0x9, B: 0x5d, A: 0xff},
drawing.Color{R: 0x46, G: 0xb, B: 0x5e, A: 0xff}, {R: 0x46, G: 0xb, B: 0x5e, A: 0xff},
drawing.Color{R: 0x46, G: 0xc, B: 0x60, A: 0xff}, {R: 0x46, G: 0xc, B: 0x60, A: 0xff},
drawing.Color{R: 0x47, G: 0xe, B: 0x61, A: 0xff}, {R: 0x47, G: 0xe, B: 0x61, A: 0xff},
drawing.Color{R: 0x47, G: 0xf, B: 0x62, A: 0xff}, {R: 0x47, G: 0xf, B: 0x62, A: 0xff},
drawing.Color{R: 0x47, G: 0x11, B: 0x64, A: 0xff}, {R: 0x47, G: 0x11, B: 0x64, A: 0xff},
drawing.Color{R: 0x47, G: 0x12, B: 0x65, A: 0xff}, {R: 0x47, G: 0x12, B: 0x65, A: 0xff},
drawing.Color{R: 0x47, G: 0x14, B: 0x66, A: 0xff}, {R: 0x47, G: 0x14, B: 0x66, A: 0xff},
drawing.Color{R: 0x48, G: 0x15, B: 0x68, A: 0xff}, {R: 0x48, G: 0x15, B: 0x68, A: 0xff},
drawing.Color{R: 0x48, G: 0x16, B: 0x69, A: 0xff}, {R: 0x48, G: 0x16, B: 0x69, A: 0xff},
drawing.Color{R: 0x48, G: 0x18, B: 0x6a, A: 0xff}, {R: 0x48, G: 0x18, B: 0x6a, A: 0xff},
drawing.Color{R: 0x48, G: 0x19, B: 0x6c, A: 0xff}, {R: 0x48, G: 0x19, B: 0x6c, A: 0xff},
drawing.Color{R: 0x48, G: 0x1a, B: 0x6d, A: 0xff}, {R: 0x48, G: 0x1a, B: 0x6d, A: 0xff},
drawing.Color{R: 0x48, G: 0x1c, B: 0x6e, A: 0xff}, {R: 0x48, G: 0x1c, B: 0x6e, A: 0xff},
drawing.Color{R: 0x48, G: 0x1d, B: 0x6f, A: 0xff}, {R: 0x48, G: 0x1d, B: 0x6f, A: 0xff},
drawing.Color{R: 0x48, G: 0x1e, B: 0x70, A: 0xff}, {R: 0x48, G: 0x1e, B: 0x70, A: 0xff},
drawing.Color{R: 0x48, G: 0x20, B: 0x71, A: 0xff}, {R: 0x48, G: 0x20, B: 0x71, A: 0xff},
drawing.Color{R: 0x48, G: 0x21, B: 0x73, A: 0xff}, {R: 0x48, G: 0x21, B: 0x73, A: 0xff},
drawing.Color{R: 0x48, G: 0x22, B: 0x74, A: 0xff}, {R: 0x48, G: 0x22, B: 0x74, A: 0xff},
drawing.Color{R: 0x48, G: 0x24, B: 0x75, A: 0xff}, {R: 0x48, G: 0x24, B: 0x75, A: 0xff},
drawing.Color{R: 0x48, G: 0x25, B: 0x76, A: 0xff}, {R: 0x48, G: 0x25, B: 0x76, A: 0xff},
drawing.Color{R: 0x48, G: 0x26, B: 0x77, A: 0xff}, {R: 0x48, G: 0x26, B: 0x77, A: 0xff},
drawing.Color{R: 0x48, G: 0x27, B: 0x78, A: 0xff}, {R: 0x48, G: 0x27, B: 0x78, A: 0xff},
drawing.Color{R: 0x47, G: 0x29, B: 0x79, A: 0xff}, {R: 0x47, G: 0x29, B: 0x79, A: 0xff},
drawing.Color{R: 0x47, G: 0x2a, B: 0x79, A: 0xff}, {R: 0x47, G: 0x2a, B: 0x79, A: 0xff},
drawing.Color{R: 0x47, G: 0x2b, B: 0x7a, A: 0xff}, {R: 0x47, G: 0x2b, B: 0x7a, A: 0xff},
drawing.Color{R: 0x47, G: 0x2c, B: 0x7b, A: 0xff}, {R: 0x47, G: 0x2c, B: 0x7b, A: 0xff},
drawing.Color{R: 0x47, G: 0x2e, B: 0x7c, A: 0xff}, {R: 0x47, G: 0x2e, B: 0x7c, A: 0xff},
drawing.Color{R: 0x46, G: 0x2f, B: 0x7d, A: 0xff}, {R: 0x46, G: 0x2f, B: 0x7d, A: 0xff},
drawing.Color{R: 0x46, G: 0x30, B: 0x7e, A: 0xff}, {R: 0x46, G: 0x30, B: 0x7e, A: 0xff},
drawing.Color{R: 0x46, G: 0x31, B: 0x7e, A: 0xff}, {R: 0x46, G: 0x31, B: 0x7e, A: 0xff},
drawing.Color{R: 0x46, G: 0x33, B: 0x7f, A: 0xff}, {R: 0x46, G: 0x33, B: 0x7f, A: 0xff},
drawing.Color{R: 0x45, G: 0x34, B: 0x80, A: 0xff}, {R: 0x45, G: 0x34, B: 0x80, A: 0xff},
drawing.Color{R: 0x45, G: 0x35, B: 0x81, A: 0xff}, {R: 0x45, G: 0x35, B: 0x81, A: 0xff},
drawing.Color{R: 0x45, G: 0x36, B: 0x81, A: 0xff}, {R: 0x45, G: 0x36, B: 0x81, A: 0xff},
drawing.Color{R: 0x44, G: 0x38, B: 0x82, A: 0xff}, {R: 0x44, G: 0x38, B: 0x82, A: 0xff},
drawing.Color{R: 0x44, G: 0x39, B: 0x83, A: 0xff}, {R: 0x44, G: 0x39, B: 0x83, A: 0xff},
drawing.Color{R: 0x44, G: 0x3a, B: 0x83, A: 0xff}, {R: 0x44, G: 0x3a, B: 0x83, A: 0xff},
drawing.Color{R: 0x43, G: 0x3b, B: 0x84, A: 0xff}, {R: 0x43, G: 0x3b, B: 0x84, A: 0xff},
drawing.Color{R: 0x43, G: 0x3c, B: 0x84, A: 0xff}, {R: 0x43, G: 0x3c, B: 0x84, A: 0xff},
drawing.Color{R: 0x43, G: 0x3e, B: 0x85, A: 0xff}, {R: 0x43, G: 0x3e, B: 0x85, A: 0xff},
drawing.Color{R: 0x42, G: 0x3f, B: 0x85, A: 0xff}, {R: 0x42, G: 0x3f, B: 0x85, A: 0xff},
drawing.Color{R: 0x42, G: 0x40, B: 0x86, A: 0xff}, {R: 0x42, G: 0x40, B: 0x86, A: 0xff},
drawing.Color{R: 0x41, G: 0x41, B: 0x86, A: 0xff}, {R: 0x41, G: 0x41, B: 0x86, A: 0xff},
drawing.Color{R: 0x41, G: 0x42, B: 0x87, A: 0xff}, {R: 0x41, G: 0x42, B: 0x87, A: 0xff},
drawing.Color{R: 0x41, G: 0x43, B: 0x87, A: 0xff}, {R: 0x41, G: 0x43, B: 0x87, A: 0xff},
drawing.Color{R: 0x40, G: 0x45, B: 0x88, A: 0xff}, {R: 0x40, G: 0x45, B: 0x88, A: 0xff},
drawing.Color{R: 0x40, G: 0x46, B: 0x88, A: 0xff}, {R: 0x40, G: 0x46, B: 0x88, A: 0xff},
drawing.Color{R: 0x3f, G: 0x47, B: 0x88, A: 0xff}, {R: 0x3f, G: 0x47, B: 0x88, A: 0xff},
drawing.Color{R: 0x3f, G: 0x48, B: 0x89, A: 0xff}, {R: 0x3f, G: 0x48, B: 0x89, A: 0xff},
drawing.Color{R: 0x3e, G: 0x49, B: 0x89, A: 0xff}, {R: 0x3e, G: 0x49, B: 0x89, A: 0xff},
drawing.Color{R: 0x3e, G: 0x4a, B: 0x89, A: 0xff}, {R: 0x3e, G: 0x4a, B: 0x89, A: 0xff},
drawing.Color{R: 0x3d, G: 0x4b, B: 0x8a, A: 0xff}, {R: 0x3d, G: 0x4b, B: 0x8a, A: 0xff},
drawing.Color{R: 0x3d, G: 0x4d, B: 0x8a, A: 0xff}, {R: 0x3d, G: 0x4d, B: 0x8a, A: 0xff},
drawing.Color{R: 0x3c, G: 0x4e, B: 0x8a, A: 0xff}, {R: 0x3c, G: 0x4e, B: 0x8a, A: 0xff},
drawing.Color{R: 0x3c, G: 0x4f, B: 0x8a, A: 0xff}, {R: 0x3c, G: 0x4f, B: 0x8a, A: 0xff},
drawing.Color{R: 0x3b, G: 0x50, B: 0x8b, A: 0xff}, {R: 0x3b, G: 0x50, B: 0x8b, A: 0xff},
drawing.Color{R: 0x3b, G: 0x51, B: 0x8b, A: 0xff}, {R: 0x3b, G: 0x51, B: 0x8b, A: 0xff},
drawing.Color{R: 0x3a, G: 0x52, B: 0x8b, A: 0xff}, {R: 0x3a, G: 0x52, B: 0x8b, A: 0xff},
drawing.Color{R: 0x3a, G: 0x53, B: 0x8b, A: 0xff}, {R: 0x3a, G: 0x53, B: 0x8b, A: 0xff},
drawing.Color{R: 0x39, G: 0x54, B: 0x8c, A: 0xff}, {R: 0x39, G: 0x54, B: 0x8c, A: 0xff},
drawing.Color{R: 0x39, G: 0x55, B: 0x8c, A: 0xff}, {R: 0x39, G: 0x55, B: 0x8c, A: 0xff},
drawing.Color{R: 0x38, G: 0x56, B: 0x8c, A: 0xff}, {R: 0x38, G: 0x56, B: 0x8c, A: 0xff},
drawing.Color{R: 0x38, G: 0x57, B: 0x8c, A: 0xff}, {R: 0x38, G: 0x57, B: 0x8c, A: 0xff},
drawing.Color{R: 0x37, G: 0x58, B: 0x8c, A: 0xff}, {R: 0x37, G: 0x58, B: 0x8c, A: 0xff},
drawing.Color{R: 0x37, G: 0x59, B: 0x8c, A: 0xff}, {R: 0x37, G: 0x59, B: 0x8c, A: 0xff},
drawing.Color{R: 0x36, G: 0x5b, B: 0x8d, A: 0xff}, {R: 0x36, G: 0x5b, B: 0x8d, A: 0xff},
drawing.Color{R: 0x36, G: 0x5c, B: 0x8d, A: 0xff}, {R: 0x36, G: 0x5c, B: 0x8d, A: 0xff},
drawing.Color{R: 0x35, G: 0x5d, B: 0x8d, A: 0xff}, {R: 0x35, G: 0x5d, B: 0x8d, A: 0xff},
drawing.Color{R: 0x35, G: 0x5e, B: 0x8d, A: 0xff}, {R: 0x35, G: 0x5e, B: 0x8d, A: 0xff},
drawing.Color{R: 0x34, G: 0x5f, B: 0x8d, A: 0xff}, {R: 0x34, G: 0x5f, B: 0x8d, A: 0xff},
drawing.Color{R: 0x34, G: 0x60, B: 0x8d, A: 0xff}, {R: 0x34, G: 0x60, B: 0x8d, A: 0xff},
drawing.Color{R: 0x33, G: 0x61, B: 0x8d, A: 0xff}, {R: 0x33, G: 0x61, B: 0x8d, A: 0xff},
drawing.Color{R: 0x33, G: 0x62, B: 0x8d, A: 0xff}, {R: 0x33, G: 0x62, B: 0x8d, A: 0xff},
drawing.Color{R: 0x33, G: 0x63, B: 0x8d, A: 0xff}, {R: 0x33, G: 0x63, B: 0x8d, A: 0xff},
drawing.Color{R: 0x32, G: 0x64, B: 0x8e, A: 0xff}, {R: 0x32, G: 0x64, B: 0x8e, A: 0xff},
drawing.Color{R: 0x32, G: 0x65, B: 0x8e, A: 0xff}, {R: 0x32, G: 0x65, B: 0x8e, A: 0xff},
drawing.Color{R: 0x31, G: 0x66, B: 0x8e, A: 0xff}, {R: 0x31, G: 0x66, B: 0x8e, A: 0xff},
drawing.Color{R: 0x31, G: 0x67, B: 0x8e, A: 0xff}, {R: 0x31, G: 0x67, B: 0x8e, A: 0xff},
drawing.Color{R: 0x30, G: 0x68, B: 0x8e, A: 0xff}, {R: 0x30, G: 0x68, B: 0x8e, A: 0xff},
drawing.Color{R: 0x30, G: 0x69, B: 0x8e, A: 0xff}, {R: 0x30, G: 0x69, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2f, G: 0x6a, B: 0x8e, A: 0xff}, {R: 0x2f, G: 0x6a, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2f, G: 0x6b, B: 0x8e, A: 0xff}, {R: 0x2f, G: 0x6b, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2f, G: 0x6c, B: 0x8e, A: 0xff}, {R: 0x2f, G: 0x6c, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2e, G: 0x6d, B: 0x8e, A: 0xff}, {R: 0x2e, G: 0x6d, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2e, G: 0x6e, B: 0x8e, A: 0xff}, {R: 0x2e, G: 0x6e, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2d, G: 0x6f, B: 0x8e, A: 0xff}, {R: 0x2d, G: 0x6f, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2d, G: 0x70, B: 0x8e, A: 0xff}, {R: 0x2d, G: 0x70, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2d, G: 0x70, B: 0x8e, A: 0xff}, {R: 0x2d, G: 0x70, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2c, G: 0x71, B: 0x8e, A: 0xff}, {R: 0x2c, G: 0x71, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2c, G: 0x72, B: 0x8e, A: 0xff}, {R: 0x2c, G: 0x72, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2b, G: 0x73, B: 0x8e, A: 0xff}, {R: 0x2b, G: 0x73, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2b, G: 0x74, B: 0x8e, A: 0xff}, {R: 0x2b, G: 0x74, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2b, G: 0x75, B: 0x8e, A: 0xff}, {R: 0x2b, G: 0x75, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2a, G: 0x76, B: 0x8e, A: 0xff}, {R: 0x2a, G: 0x76, B: 0x8e, A: 0xff},
drawing.Color{R: 0x2a, G: 0x77, B: 0x8e, A: 0xff}, {R: 0x2a, G: 0x77, B: 0x8e, A: 0xff},
drawing.Color{R: 0x29, G: 0x78, B: 0x8e, A: 0xff}, {R: 0x29, G: 0x78, B: 0x8e, A: 0xff},
drawing.Color{R: 0x29, G: 0x79, B: 0x8e, A: 0xff}, {R: 0x29, G: 0x79, B: 0x8e, A: 0xff},
drawing.Color{R: 0x29, G: 0x7a, B: 0x8e, A: 0xff}, {R: 0x29, G: 0x7a, B: 0x8e, A: 0xff},
drawing.Color{R: 0x28, G: 0x7b, B: 0x8e, A: 0xff}, {R: 0x28, G: 0x7b, B: 0x8e, A: 0xff},
drawing.Color{R: 0x28, G: 0x7c, B: 0x8e, A: 0xff}, {R: 0x28, G: 0x7c, B: 0x8e, A: 0xff},
drawing.Color{R: 0x28, G: 0x7d, B: 0x8e, A: 0xff}, {R: 0x28, G: 0x7d, B: 0x8e, A: 0xff},
drawing.Color{R: 0x27, G: 0x7e, B: 0x8e, A: 0xff}, {R: 0x27, G: 0x7e, B: 0x8e, A: 0xff},
drawing.Color{R: 0x27, G: 0x7f, B: 0x8e, A: 0xff}, {R: 0x27, G: 0x7f, B: 0x8e, A: 0xff},
drawing.Color{R: 0x26, G: 0x80, B: 0x8e, A: 0xff}, {R: 0x26, G: 0x80, B: 0x8e, A: 0xff},
drawing.Color{R: 0x26, G: 0x81, B: 0x8e, A: 0xff}, {R: 0x26, G: 0x81, B: 0x8e, A: 0xff},
drawing.Color{R: 0x26, G: 0x82, B: 0x8e, A: 0xff}, {R: 0x26, G: 0x82, B: 0x8e, A: 0xff},
drawing.Color{R: 0x25, G: 0x83, B: 0x8e, A: 0xff}, {R: 0x25, G: 0x83, B: 0x8e, A: 0xff},
drawing.Color{R: 0x25, G: 0x83, B: 0x8e, A: 0xff}, {R: 0x25, G: 0x83, B: 0x8e, A: 0xff},
drawing.Color{R: 0x25, G: 0x84, B: 0x8e, A: 0xff}, {R: 0x25, G: 0x84, B: 0x8e, A: 0xff},
drawing.Color{R: 0x24, G: 0x85, B: 0x8e, A: 0xff}, {R: 0x24, G: 0x85, B: 0x8e, A: 0xff},
drawing.Color{R: 0x24, G: 0x86, B: 0x8e, A: 0xff}, {R: 0x24, G: 0x86, B: 0x8e, A: 0xff},
drawing.Color{R: 0x23, G: 0x87, B: 0x8e, A: 0xff}, {R: 0x23, G: 0x87, B: 0x8e, A: 0xff},
drawing.Color{R: 0x23, G: 0x88, B: 0x8e, A: 0xff}, {R: 0x23, G: 0x88, B: 0x8e, A: 0xff},
drawing.Color{R: 0x23, G: 0x89, B: 0x8e, A: 0xff}, {R: 0x23, G: 0x89, B: 0x8e, A: 0xff},
drawing.Color{R: 0x22, G: 0x8a, B: 0x8d, A: 0xff}, {R: 0x22, G: 0x8a, B: 0x8d, A: 0xff},
drawing.Color{R: 0x22, G: 0x8b, B: 0x8d, A: 0xff}, {R: 0x22, G: 0x8b, B: 0x8d, A: 0xff},
drawing.Color{R: 0x22, G: 0x8c, B: 0x8d, A: 0xff}, {R: 0x22, G: 0x8c, B: 0x8d, A: 0xff},
drawing.Color{R: 0x21, G: 0x8d, B: 0x8d, A: 0xff}, {R: 0x21, G: 0x8d, B: 0x8d, A: 0xff},
drawing.Color{R: 0x21, G: 0x8e, B: 0x8d, A: 0xff}, {R: 0x21, G: 0x8e, B: 0x8d, A: 0xff},
drawing.Color{R: 0x21, G: 0x8f, B: 0x8d, A: 0xff}, {R: 0x21, G: 0x8f, B: 0x8d, A: 0xff},
drawing.Color{R: 0x20, G: 0x90, B: 0x8d, A: 0xff}, {R: 0x20, G: 0x90, B: 0x8d, A: 0xff},
drawing.Color{R: 0x20, G: 0x91, B: 0x8c, A: 0xff}, {R: 0x20, G: 0x91, B: 0x8c, A: 0xff},
drawing.Color{R: 0x20, G: 0x92, B: 0x8c, A: 0xff}, {R: 0x20, G: 0x92, B: 0x8c, A: 0xff},
drawing.Color{R: 0x20, G: 0x93, B: 0x8c, A: 0xff}, {R: 0x20, G: 0x93, B: 0x8c, A: 0xff},
drawing.Color{R: 0x1f, G: 0x93, B: 0x8c, A: 0xff}, {R: 0x1f, G: 0x93, B: 0x8c, A: 0xff},
drawing.Color{R: 0x1f, G: 0x94, B: 0x8c, A: 0xff}, {R: 0x1f, G: 0x94, B: 0x8c, A: 0xff},
drawing.Color{R: 0x1f, G: 0x95, B: 0x8b, A: 0xff}, {R: 0x1f, G: 0x95, B: 0x8b, A: 0xff},
drawing.Color{R: 0x1f, G: 0x96, B: 0x8b, A: 0xff}, {R: 0x1f, G: 0x96, B: 0x8b, A: 0xff},
drawing.Color{R: 0x1f, G: 0x97, B: 0x8b, A: 0xff}, {R: 0x1f, G: 0x97, B: 0x8b, A: 0xff},
drawing.Color{R: 0x1e, G: 0x98, B: 0x8b, A: 0xff}, {R: 0x1e, G: 0x98, B: 0x8b, A: 0xff},
drawing.Color{R: 0x1e, G: 0x99, B: 0x8a, A: 0xff}, {R: 0x1e, G: 0x99, B: 0x8a, A: 0xff},
drawing.Color{R: 0x1e, G: 0x9a, B: 0x8a, A: 0xff}, {R: 0x1e, G: 0x9a, B: 0x8a, A: 0xff},
drawing.Color{R: 0x1e, G: 0x9b, B: 0x8a, A: 0xff}, {R: 0x1e, G: 0x9b, B: 0x8a, A: 0xff},
drawing.Color{R: 0x1e, G: 0x9c, B: 0x89, A: 0xff}, {R: 0x1e, G: 0x9c, B: 0x89, A: 0xff},
drawing.Color{R: 0x1e, G: 0x9d, B: 0x89, A: 0xff}, {R: 0x1e, G: 0x9d, B: 0x89, A: 0xff},
drawing.Color{R: 0x1e, G: 0x9e, B: 0x89, A: 0xff}, {R: 0x1e, G: 0x9e, B: 0x89, A: 0xff},
drawing.Color{R: 0x1e, G: 0x9f, B: 0x88, A: 0xff}, {R: 0x1e, G: 0x9f, B: 0x88, A: 0xff},
drawing.Color{R: 0x1e, G: 0xa0, B: 0x88, A: 0xff}, {R: 0x1e, G: 0xa0, B: 0x88, A: 0xff},
drawing.Color{R: 0x1f, G: 0xa1, B: 0x88, A: 0xff}, {R: 0x1f, G: 0xa1, B: 0x88, A: 0xff},
drawing.Color{R: 0x1f, G: 0xa2, B: 0x87, A: 0xff}, {R: 0x1f, G: 0xa2, B: 0x87, A: 0xff},
drawing.Color{R: 0x1f, G: 0xa3, B: 0x87, A: 0xff}, {R: 0x1f, G: 0xa3, B: 0x87, A: 0xff},
drawing.Color{R: 0x1f, G: 0xa3, B: 0x86, A: 0xff}, {R: 0x1f, G: 0xa3, B: 0x86, A: 0xff},
drawing.Color{R: 0x20, G: 0xa4, B: 0x86, A: 0xff}, {R: 0x20, G: 0xa4, B: 0x86, A: 0xff},
drawing.Color{R: 0x20, G: 0xa5, B: 0x86, A: 0xff}, {R: 0x20, G: 0xa5, B: 0x86, A: 0xff},
drawing.Color{R: 0x21, G: 0xa6, B: 0x85, A: 0xff}, {R: 0x21, G: 0xa6, B: 0x85, A: 0xff},
drawing.Color{R: 0x21, G: 0xa7, B: 0x85, A: 0xff}, {R: 0x21, G: 0xa7, B: 0x85, A: 0xff},
drawing.Color{R: 0x22, G: 0xa8, B: 0x84, A: 0xff}, {R: 0x22, G: 0xa8, B: 0x84, A: 0xff},
drawing.Color{R: 0x23, G: 0xa9, B: 0x83, A: 0xff}, {R: 0x23, G: 0xa9, B: 0x83, A: 0xff},
drawing.Color{R: 0x23, G: 0xaa, B: 0x83, A: 0xff}, {R: 0x23, G: 0xaa, B: 0x83, A: 0xff},
drawing.Color{R: 0x24, G: 0xab, B: 0x82, A: 0xff}, {R: 0x24, G: 0xab, B: 0x82, A: 0xff},
drawing.Color{R: 0x25, G: 0xac, B: 0x82, A: 0xff}, {R: 0x25, G: 0xac, B: 0x82, A: 0xff},
drawing.Color{R: 0x26, G: 0xad, B: 0x81, A: 0xff}, {R: 0x26, G: 0xad, B: 0x81, A: 0xff},
drawing.Color{R: 0x27, G: 0xae, B: 0x81, A: 0xff}, {R: 0x27, G: 0xae, B: 0x81, A: 0xff},
drawing.Color{R: 0x28, G: 0xaf, B: 0x80, A: 0xff}, {R: 0x28, G: 0xaf, B: 0x80, A: 0xff},
drawing.Color{R: 0x29, G: 0xaf, B: 0x7f, A: 0xff}, {R: 0x29, G: 0xaf, B: 0x7f, A: 0xff},
drawing.Color{R: 0x2a, G: 0xb0, B: 0x7f, A: 0xff}, {R: 0x2a, G: 0xb0, B: 0x7f, A: 0xff},
drawing.Color{R: 0x2b, G: 0xb1, B: 0x7e, A: 0xff}, {R: 0x2b, G: 0xb1, B: 0x7e, A: 0xff},
drawing.Color{R: 0x2c, G: 0xb2, B: 0x7d, A: 0xff}, {R: 0x2c, G: 0xb2, B: 0x7d, A: 0xff},
drawing.Color{R: 0x2e, G: 0xb3, B: 0x7c, A: 0xff}, {R: 0x2e, G: 0xb3, B: 0x7c, A: 0xff},
drawing.Color{R: 0x2f, G: 0xb4, B: 0x7c, A: 0xff}, {R: 0x2f, G: 0xb4, B: 0x7c, A: 0xff},
drawing.Color{R: 0x30, G: 0xb5, B: 0x7b, A: 0xff}, {R: 0x30, G: 0xb5, B: 0x7b, A: 0xff},
drawing.Color{R: 0x32, G: 0xb6, B: 0x7a, A: 0xff}, {R: 0x32, G: 0xb6, B: 0x7a, A: 0xff},
drawing.Color{R: 0x33, G: 0xb7, B: 0x79, A: 0xff}, {R: 0x33, G: 0xb7, B: 0x79, A: 0xff},
drawing.Color{R: 0x35, G: 0xb7, B: 0x79, A: 0xff}, {R: 0x35, G: 0xb7, B: 0x79, A: 0xff},
drawing.Color{R: 0x36, G: 0xb8, B: 0x78, A: 0xff}, {R: 0x36, G: 0xb8, B: 0x78, A: 0xff},
drawing.Color{R: 0x38, G: 0xb9, B: 0x77, A: 0xff}, {R: 0x38, G: 0xb9, B: 0x77, A: 0xff},
drawing.Color{R: 0x39, G: 0xba, B: 0x76, A: 0xff}, {R: 0x39, G: 0xba, B: 0x76, A: 0xff},
drawing.Color{R: 0x3b, G: 0xbb, B: 0x75, A: 0xff}, {R: 0x3b, G: 0xbb, B: 0x75, A: 0xff},
drawing.Color{R: 0x3d, G: 0xbc, B: 0x74, A: 0xff}, {R: 0x3d, G: 0xbc, B: 0x74, A: 0xff},
drawing.Color{R: 0x3e, G: 0xbd, B: 0x73, A: 0xff}, {R: 0x3e, G: 0xbd, B: 0x73, A: 0xff},
drawing.Color{R: 0x40, G: 0xbe, B: 0x72, A: 0xff}, {R: 0x40, G: 0xbe, B: 0x72, A: 0xff},
drawing.Color{R: 0x42, G: 0xbe, B: 0x71, A: 0xff}, {R: 0x42, G: 0xbe, B: 0x71, A: 0xff},
drawing.Color{R: 0x44, G: 0xbf, B: 0x70, A: 0xff}, {R: 0x44, G: 0xbf, B: 0x70, A: 0xff},
drawing.Color{R: 0x46, G: 0xc0, B: 0x6f, A: 0xff}, {R: 0x46, G: 0xc0, B: 0x6f, A: 0xff},
drawing.Color{R: 0x48, G: 0xc1, B: 0x6e, A: 0xff}, {R: 0x48, G: 0xc1, B: 0x6e, A: 0xff},
drawing.Color{R: 0x49, G: 0xc2, B: 0x6d, A: 0xff}, {R: 0x49, G: 0xc2, B: 0x6d, A: 0xff},
drawing.Color{R: 0x4b, G: 0xc2, B: 0x6c, A: 0xff}, {R: 0x4b, G: 0xc2, B: 0x6c, A: 0xff},
drawing.Color{R: 0x4d, G: 0xc3, B: 0x6b, A: 0xff}, {R: 0x4d, G: 0xc3, B: 0x6b, A: 0xff},
drawing.Color{R: 0x4f, G: 0xc4, B: 0x6a, A: 0xff}, {R: 0x4f, G: 0xc4, B: 0x6a, A: 0xff},
drawing.Color{R: 0x51, G: 0xc5, B: 0x69, A: 0xff}, {R: 0x51, G: 0xc5, B: 0x69, A: 0xff},
drawing.Color{R: 0x53, G: 0xc6, B: 0x68, A: 0xff}, {R: 0x53, G: 0xc6, B: 0x68, A: 0xff},
drawing.Color{R: 0x55, G: 0xc6, B: 0x66, A: 0xff}, {R: 0x55, G: 0xc6, B: 0x66, A: 0xff},
drawing.Color{R: 0x58, G: 0xc7, B: 0x65, A: 0xff}, {R: 0x58, G: 0xc7, B: 0x65, A: 0xff},
drawing.Color{R: 0x5a, G: 0xc8, B: 0x64, A: 0xff}, {R: 0x5a, G: 0xc8, B: 0x64, A: 0xff},
drawing.Color{R: 0x5c, G: 0xc9, B: 0x63, A: 0xff}, {R: 0x5c, G: 0xc9, B: 0x63, A: 0xff},
drawing.Color{R: 0x5e, G: 0xc9, B: 0x62, A: 0xff}, {R: 0x5e, G: 0xc9, B: 0x62, A: 0xff},
drawing.Color{R: 0x60, G: 0xca, B: 0x60, A: 0xff}, {R: 0x60, G: 0xca, B: 0x60, A: 0xff},
drawing.Color{R: 0x62, G: 0xcb, B: 0x5f, A: 0xff}, {R: 0x62, G: 0xcb, B: 0x5f, A: 0xff},
drawing.Color{R: 0x65, G: 0xcc, B: 0x5e, A: 0xff}, {R: 0x65, G: 0xcc, B: 0x5e, A: 0xff},
drawing.Color{R: 0x67, G: 0xcc, B: 0x5c, A: 0xff}, {R: 0x67, G: 0xcc, B: 0x5c, A: 0xff},
drawing.Color{R: 0x69, G: 0xcd, B: 0x5b, A: 0xff}, {R: 0x69, G: 0xcd, B: 0x5b, A: 0xff},
drawing.Color{R: 0x6c, G: 0xce, B: 0x5a, A: 0xff}, {R: 0x6c, G: 0xce, B: 0x5a, A: 0xff},
drawing.Color{R: 0x6e, G: 0xce, B: 0x58, A: 0xff}, {R: 0x6e, G: 0xce, B: 0x58, A: 0xff},
drawing.Color{R: 0x70, G: 0xcf, B: 0x57, A: 0xff}, {R: 0x70, G: 0xcf, B: 0x57, A: 0xff},
drawing.Color{R: 0x73, G: 0xd0, B: 0x55, A: 0xff}, {R: 0x73, G: 0xd0, B: 0x55, A: 0xff},
drawing.Color{R: 0x75, G: 0xd0, B: 0x54, A: 0xff}, {R: 0x75, G: 0xd0, B: 0x54, A: 0xff},
drawing.Color{R: 0x77, G: 0xd1, B: 0x52, A: 0xff}, {R: 0x77, G: 0xd1, B: 0x52, A: 0xff},
drawing.Color{R: 0x7a, G: 0xd2, B: 0x51, A: 0xff}, {R: 0x7a, G: 0xd2, B: 0x51, A: 0xff},
drawing.Color{R: 0x7c, G: 0xd2, B: 0x4f, A: 0xff}, {R: 0x7c, G: 0xd2, B: 0x4f, A: 0xff},
drawing.Color{R: 0x7f, G: 0xd3, B: 0x4e, A: 0xff}, {R: 0x7f, G: 0xd3, B: 0x4e, A: 0xff},
drawing.Color{R: 0x81, G: 0xd4, B: 0x4c, A: 0xff}, {R: 0x81, G: 0xd4, B: 0x4c, A: 0xff},
drawing.Color{R: 0x84, G: 0xd4, B: 0x4b, A: 0xff}, {R: 0x84, G: 0xd4, B: 0x4b, A: 0xff},
drawing.Color{R: 0x86, G: 0xd5, B: 0x49, A: 0xff}, {R: 0x86, G: 0xd5, B: 0x49, A: 0xff},
drawing.Color{R: 0x89, G: 0xd5, B: 0x48, A: 0xff}, {R: 0x89, G: 0xd5, B: 0x48, A: 0xff},
drawing.Color{R: 0x8b, G: 0xd6, B: 0x46, A: 0xff}, {R: 0x8b, G: 0xd6, B: 0x46, A: 0xff},
drawing.Color{R: 0x8e, G: 0xd7, B: 0x44, A: 0xff}, {R: 0x8e, G: 0xd7, B: 0x44, A: 0xff},
drawing.Color{R: 0x90, G: 0xd7, B: 0x43, A: 0xff}, {R: 0x90, G: 0xd7, B: 0x43, A: 0xff},
drawing.Color{R: 0x93, G: 0xd8, B: 0x41, A: 0xff}, {R: 0x93, G: 0xd8, B: 0x41, A: 0xff},
drawing.Color{R: 0x95, G: 0xd8, B: 0x3f, A: 0xff}, {R: 0x95, G: 0xd8, B: 0x3f, A: 0xff},
drawing.Color{R: 0x98, G: 0xd9, B: 0x3e, A: 0xff}, {R: 0x98, G: 0xd9, B: 0x3e, A: 0xff},
drawing.Color{R: 0x9b, G: 0xd9, B: 0x3c, A: 0xff}, {R: 0x9b, G: 0xd9, B: 0x3c, A: 0xff},
drawing.Color{R: 0x9d, G: 0xda, B: 0x3a, A: 0xff}, {R: 0x9d, G: 0xda, B: 0x3a, A: 0xff},
drawing.Color{R: 0xa0, G: 0xda, B: 0x39, A: 0xff}, {R: 0xa0, G: 0xda, B: 0x39, A: 0xff},
drawing.Color{R: 0xa3, G: 0xdb, B: 0x37, A: 0xff}, {R: 0xa3, G: 0xdb, B: 0x37, A: 0xff},
drawing.Color{R: 0xa5, G: 0xdb, B: 0x35, A: 0xff}, {R: 0xa5, G: 0xdb, B: 0x35, A: 0xff},
drawing.Color{R: 0xa8, G: 0xdc, B: 0x33, A: 0xff}, {R: 0xa8, G: 0xdc, B: 0x33, A: 0xff},
drawing.Color{R: 0xab, G: 0xdc, B: 0x32, A: 0xff}, {R: 0xab, G: 0xdc, B: 0x32, A: 0xff},
drawing.Color{R: 0xad, G: 0xdd, B: 0x30, A: 0xff}, {R: 0xad, G: 0xdd, B: 0x30, A: 0xff},
drawing.Color{R: 0xb0, G: 0xdd, B: 0x2e, A: 0xff}, {R: 0xb0, G: 0xdd, B: 0x2e, A: 0xff},
drawing.Color{R: 0xb3, G: 0xdd, B: 0x2d, A: 0xff}, {R: 0xb3, G: 0xdd, B: 0x2d, A: 0xff},
drawing.Color{R: 0xb5, G: 0xde, B: 0x2b, A: 0xff}, {R: 0xb5, G: 0xde, B: 0x2b, A: 0xff},
drawing.Color{R: 0xb8, G: 0xde, B: 0x29, A: 0xff}, {R: 0xb8, G: 0xde, B: 0x29, A: 0xff},
drawing.Color{R: 0xbb, G: 0xdf, B: 0x27, A: 0xff}, {R: 0xbb, G: 0xdf, B: 0x27, A: 0xff},
drawing.Color{R: 0xbd, G: 0xdf, B: 0x26, A: 0xff}, {R: 0xbd, G: 0xdf, B: 0x26, A: 0xff},
drawing.Color{R: 0xc0, G: 0xdf, B: 0x24, A: 0xff}, {R: 0xc0, G: 0xdf, B: 0x24, A: 0xff},
drawing.Color{R: 0xc3, G: 0xe0, B: 0x23, A: 0xff}, {R: 0xc3, G: 0xe0, B: 0x23, A: 0xff},
drawing.Color{R: 0xc5, G: 0xe0, B: 0x21, A: 0xff}, {R: 0xc5, G: 0xe0, B: 0x21, A: 0xff},
drawing.Color{R: 0xc8, G: 0xe1, B: 0x20, A: 0xff}, {R: 0xc8, G: 0xe1, B: 0x20, A: 0xff},
drawing.Color{R: 0xcb, G: 0xe1, B: 0x1e, A: 0xff}, {R: 0xcb, G: 0xe1, B: 0x1e, A: 0xff},
drawing.Color{R: 0xcd, G: 0xe1, B: 0x1d, A: 0xff}, {R: 0xcd, G: 0xe1, B: 0x1d, A: 0xff},
drawing.Color{R: 0xd0, G: 0xe2, B: 0x1c, A: 0xff}, {R: 0xd0, G: 0xe2, B: 0x1c, A: 0xff},
drawing.Color{R: 0xd3, G: 0xe2, B: 0x1b, A: 0xff}, {R: 0xd3, G: 0xe2, B: 0x1b, A: 0xff},
drawing.Color{R: 0xd5, G: 0xe2, B: 0x1a, A: 0xff}, {R: 0xd5, G: 0xe2, B: 0x1a, A: 0xff},
drawing.Color{R: 0xd8, G: 0xe3, B: 0x19, A: 0xff}, {R: 0xd8, G: 0xe3, B: 0x19, A: 0xff},
drawing.Color{R: 0xdb, G: 0xe3, B: 0x18, A: 0xff}, {R: 0xdb, G: 0xe3, B: 0x18, A: 0xff},
drawing.Color{R: 0xdd, G: 0xe3, B: 0x18, A: 0xff}, {R: 0xdd, G: 0xe3, B: 0x18, A: 0xff},
drawing.Color{R: 0xe0, G: 0xe4, B: 0x18, A: 0xff}, {R: 0xe0, G: 0xe4, B: 0x18, A: 0xff},
drawing.Color{R: 0xe2, G: 0xe4, B: 0x18, A: 0xff}, {R: 0xe2, G: 0xe4, B: 0x18, A: 0xff},
drawing.Color{R: 0xe5, G: 0xe4, B: 0x18, A: 0xff}, {R: 0xe5, G: 0xe4, B: 0x18, A: 0xff},
drawing.Color{R: 0xe8, G: 0xe5, B: 0x19, A: 0xff}, {R: 0xe8, G: 0xe5, B: 0x19, A: 0xff},
drawing.Color{R: 0xea, G: 0xe5, B: 0x19, A: 0xff}, {R: 0xea, G: 0xe5, B: 0x19, A: 0xff},
drawing.Color{R: 0xed, G: 0xe5, B: 0x1a, A: 0xff}, {R: 0xed, G: 0xe5, B: 0x1a, A: 0xff},
drawing.Color{R: 0xef, G: 0xe6, B: 0x1b, A: 0xff}, {R: 0xef, G: 0xe6, B: 0x1b, A: 0xff},
drawing.Color{R: 0xf2, G: 0xe6, B: 0x1c, A: 0xff}, {R: 0xf2, G: 0xe6, B: 0x1c, A: 0xff},
drawing.Color{R: 0xf4, G: 0xe6, B: 0x1e, A: 0xff}, {R: 0xf4, G: 0xe6, B: 0x1e, A: 0xff},
drawing.Color{R: 0xf7, G: 0xe6, B: 0x1f, A: 0xff}, {R: 0xf7, G: 0xe6, B: 0x1f, A: 0xff},
drawing.Color{R: 0xf9, G: 0xe7, B: 0x21, A: 0xff}, {R: 0xf9, G: 0xe7, B: 0x21, A: 0xff},
drawing.Color{R: 0xfb, G: 0xe7, B: 0x23, A: 0xff}, {R: 0xfb, G: 0xe7, B: 0x23, A: 0xff},
drawing.Color{R: 0xfe, G: 0xe7, B: 0x24, A: 0xff}, {R: 0xfe, G: 0xe7, B: 0x24, A: 0xff},
} }
// Viridis creates a color map provider. // Viridis creates a color map provider.

View file

@ -110,9 +110,9 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
break break
} }
left = MinInt(left, ltx) left = Min(left, ltx)
right = MaxInt(right, rtx) right = Max(right, rtx)
bottom = MaxInt(bottom, ty) bottom = Max(bottom, ty)
} }
if !xa.NameStyle.Hidden && len(xa.Name) > 0 { if !xa.NameStyle.Hidden && len(xa.Name) > 0 {
@ -164,7 +164,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 = MaxInt(maxTextHeight, tb.Height()) maxTextHeight = Max(maxTextHeight, tb.Height())
break break
case TickPositionBetweenTicks: case TickPositionBetweenTicks:
if index > 0 { if index > 0 {
@ -180,7 +180,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 = MaxInt(maxTextHeight, ftb.Height()) maxTextHeight = Max(maxTextHeight, ftb.Height())
} }
break break
} }

View file

@ -110,18 +110,18 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
finalTextX = tx - tb.Width() finalTextX = tx - tb.Width()
} }
maxTextHeight = MaxInt(tb.Height(), maxTextHeight) maxTextHeight = Max(tb.Height(), maxTextHeight)
if ya.AxisType == YAxisPrimary { if ya.AxisType == YAxisPrimary {
minx = canvasBox.Right minx = canvasBox.Right
maxx = MaxInt(maxx, tx+tb.Width()) maxx = Max(maxx, tx+tb.Width())
} else if ya.AxisType == YAxisSecondary { } else if ya.AxisType == YAxisSecondary {
minx = MinInt(minx, finalTextX) minx = Min(minx, finalTextX)
maxx = MaxInt(maxx, tx) maxx = Max(maxx, tx)
} }
miny = MinInt(miny, ly-tbh2) miny = Min(miny, ly-tbh2)
maxy = MaxInt(maxy, ly+tbh2) maxy = Max(maxy, ly+tbh2)
} }
if !ya.NameStyle.Hidden && len(ya.Name) > 0 { if !ya.NameStyle.Hidden && len(ya.Name) > 0 {