From f23b63bae498a5bf279c4ab49ed72ac02386cf03 Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Mon, 10 Jan 2022 15:52:32 -0800 Subject: [PATCH] snapshot ahead of tests --- annotation_series.go | 8 +- array.go | 12 +- bar_chart.go | 4 +- bollinger_band_series.go | 14 +- box.go | 44 +-- donut_chart.go | 8 +- draw.go | 4 +- examples/custom_padding/main.go | 4 +- examples/linear_regression/main.go | 4 +- examples/min_max/main.go | 4 +- examples/poly_regression/main.go | 4 +- examples/scatter/main.go | 8 +- examples/simple_moving_average/main.go | 4 +- legend.go | 8 +- linear_regression_series.go | 12 +- linear_sequence.go | 4 +- linear_sequence_test.go | 48 +++ mathutil.go | 39 +- matrix/matrix.go | 4 +- matrix/util.go | 4 +- pie_chart.go | 8 +- polynomial_regression_series.go | 6 +- random_sequence.go | 6 +- seq.go | 130 +++---- seq_test.go | 82 ++-- sequence.go | 7 + sma_series.go | 2 +- stacked_bar_chart.go | 12 +- testutil/helpers.go | 4 +- text.go | 2 +- tick.go | 2 +- times.go | 4 +- value_buffer.go | 81 ++-- value_buffer_test.go | 40 +- value_sequence.go | 8 + value_sequence_test.go | 19 + viridis.go | 512 ++++++++++++------------- xaxis.go | 10 +- yaxis.go | 12 +- 39 files changed, 606 insertions(+), 582 deletions(-) create mode 100644 linear_sequence_test.go create mode 100644 sequence.go create mode 100644 value_sequence.go create mode 100644 value_sequence_test.go diff --git a/annotation_series.go b/annotation_series.go index 96e78f9..7d54b9a 100644 --- a/annotation_series.go +++ b/annotation_series.go @@ -60,10 +60,10 @@ func (as AnnotationSeries) Measure(r Renderer, canvasBox Box, xrange, yrange Ran lx := canvasBox.Left + xrange.Translate(a.XValue) ly := canvasBox.Bottom - yrange.Translate(a.YValue) ab := Draw.MeasureAnnotation(r, canvasBox, style, lx, ly, a.Label) - box.Top = MinInt(box.Top, ab.Top) - box.Left = MinInt(box.Left, ab.Left) - box.Right = MaxInt(box.Right, ab.Right) - box.Bottom = MaxInt(box.Bottom, ab.Bottom) + box.Top = Min(box.Top, ab.Top) + box.Left = Min(box.Left, ab.Left) + box.Right = Max(box.Right, ab.Right) + box.Bottom = Max(box.Bottom, ab.Bottom) } } return box diff --git a/array.go b/array.go index 71b3ee7..8192c7b 100644 --- a/array.go +++ b/array.go @@ -1,24 +1,24 @@ package chart var ( - _ Sequence = (*Array)(nil) + _ Sequence[int] = (*array[int])(nil) ) // NewArray returns a new array from a given set of values. // Array implements Sequence, which allows it to be used with the sequence helpers. -func NewArray(values ...float64) Array { - return Array(values) +func Array[A any](values ...A) Sequence[A] { + return array[A](values) } // 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. -func (a Array) Len() int { +func (a array[A]) Len() int { return len(a) } // 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] } diff --git a/bar_chart.go b/bar_chart.go index d61f3db..1be877e 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -409,7 +409,7 @@ func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range, lines := Text.WrapFit(r, bar.Label, barLabelBox.Width(), 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 { - effectiveDimension := MinInt(bc.GetWidth(), bc.GetHeight()) + effectiveDimension := Min(bc.GetWidth(), bc.GetHeight()) if effectiveDimension >= 2048 { return 48 } else if effectiveDimension >= 1024 { diff --git a/bollinger_band_series.go b/bollinger_band_series.go index 728b232..544b800 100644 --- a/bollinger_band_series.go +++ b/bollinger_band_series.go @@ -20,7 +20,7 @@ type BollingerBandsSeries struct { K float64 InnerSeries ValuesProvider - valueBuffer *ValueBuffer + valueBuffer *ValueBuffer[float64] } // GetName returns the name of the time series. @@ -70,7 +70,7 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64) return } if bbs.valueBuffer == nil || index == 0 { - bbs.valueBuffer = NewValueBufferWithCapacity(bbs.GetPeriod()) + bbs.valueBuffer = NewValueBufferWithCapacity[float64](bbs.GetPeriod()) } if bbs.valueBuffer.Len() >= bbs.GetPeriod() { bbs.valueBuffer.Dequeue() @@ -79,8 +79,8 @@ func (bbs *BollingerBandsSeries) GetBoundedValues(index int) (x, y1, y2 float64) bbs.valueBuffer.Enqueue(py) x = px - ay := Seq{bbs.valueBuffer}.Average() - std := Seq{bbs.valueBuffer}.StdDev() + ay := Seq[float64]{bbs.valueBuffer}.Average() + std := Seq[float64]{bbs.valueBuffer}.StdDev() y1 = ay + (bbs.GetK() * std) y2 = ay - (bbs.GetK() * std) @@ -99,15 +99,15 @@ func (bbs *BollingerBandsSeries) GetBoundedLastValues() (x, y1, y2 float64) { startAt = 0 } - vb := NewValueBufferWithCapacity(period) + vb := NewValueBufferWithCapacity[float64](period) for index := startAt; index < seriesLength; index++ { xn, yn := bbs.InnerSeries.GetValues(index) vb.Enqueue(yn) x = xn } - ay := Seq{vb}.Average() - std := Seq{vb}.StdDev() + ay := Seq[float64]{vb}.Average() + std := Seq[float64]{vb}.StdDev() y1 = ay + (bbs.GetK() * std) y2 = ay - (bbs.GetK() * std) diff --git a/box.go b/box.go index 9611ff9..6faedcb 100644 --- a/box.go +++ b/box.go @@ -89,12 +89,12 @@ func (b Box) GetBottom(defaults ...int) int { // Width returns the width func (b Box) Width() int { - return AbsInt(b.Right - b.Left) + return Abs(b.Right - b.Left) } // Height returns the height func (b Box) Height() int { - return AbsInt(b.Bottom - b.Top) + return Abs(b.Bottom - b.Top) } // 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. func (b Box) Grow(other Box) Box { return Box{ - Top: MinInt(b.Top, other.Top), - Left: MinInt(b.Left, other.Left), - Right: MaxInt(b.Right, other.Right), - Bottom: MaxInt(b.Bottom, other.Bottom), + Top: Min(b.Top, other.Top), + Left: Min(b.Left, other.Left), + Right: Max(b.Right, other.Right), + Bottom: Max(b.Bottom, other.Bottom), } } @@ -220,10 +220,10 @@ func (b Box) Fit(other Box) Box { func (b Box) Constrain(other Box) Box { newBox := b.Clone() - newBox.Top = MaxInt(newBox.Top, other.Top) - newBox.Left = MaxInt(newBox.Left, other.Left) - newBox.Right = MinInt(newBox.Right, other.Right) - newBox.Bottom = MinInt(newBox.Bottom, other.Bottom) + newBox.Top = Max(newBox.Top, other.Top) + newBox.Left = Max(newBox.Left, other.Left) + newBox.Right = Min(newBox.Right, other.Right) + newBox.Bottom = Min(newBox.Bottom, other.Bottom) return newBox } @@ -262,36 +262,36 @@ type BoxCorners struct { // Box return the BoxCorners as a regular box. func (bc BoxCorners) Box() Box { return Box{ - Top: MinInt(bc.TopLeft.Y, bc.TopRight.Y), - Left: MinInt(bc.TopLeft.X, bc.BottomLeft.X), - Right: MaxInt(bc.TopRight.X, bc.BottomRight.X), - Bottom: MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y), + Top: Min(bc.TopLeft.Y, bc.TopRight.Y), + Left: Min(bc.TopLeft.X, bc.BottomLeft.X), + Right: Max(bc.TopRight.X, bc.BottomRight.X), + Bottom: Max(bc.BottomLeft.Y, bc.BottomRight.Y), } } // Width returns the width func (bc BoxCorners) Width() int { - minLeft := MinInt(bc.TopLeft.X, bc.BottomLeft.X) - maxRight := MaxInt(bc.TopRight.X, bc.BottomRight.X) + minLeft := Min(bc.TopLeft.X, bc.BottomLeft.X) + maxRight := Max(bc.TopRight.X, bc.BottomRight.X) return maxRight - minLeft } // Height returns the height func (bc BoxCorners) Height() int { - minTop := MinInt(bc.TopLeft.Y, bc.TopRight.Y) - maxBottom := MaxInt(bc.BottomLeft.Y, bc.BottomRight.Y) + minTop := Min(bc.TopLeft.Y, bc.TopRight.Y) + maxBottom := Max(bc.BottomLeft.Y, bc.BottomRight.Y) return maxBottom - minTop } // Center returns the center of the box func (bc BoxCorners) Center() (x, y int) { - left := MeanInt(bc.TopLeft.X, bc.BottomLeft.X) - right := MeanInt(bc.TopRight.X, bc.BottomRight.X) + left := Mean(bc.TopLeft.X, bc.BottomLeft.X) + right := Mean(bc.TopRight.X, bc.BottomRight.X) x = ((right - left) >> 1) + left - top := MeanInt(bc.TopLeft.Y, bc.TopRight.Y) - bottom := MeanInt(bc.BottomLeft.Y, bc.BottomRight.Y) + top := Mean(bc.TopLeft.Y, bc.TopRight.Y) + bottom := Mean(bc.BottomLeft.Y, bc.BottomRight.Y) y = ((bottom - top) >> 1) + top return diff --git a/donut_chart.go b/donut_chart.go index f5a7854..dc2d79f 100644 --- a/donut_chart.go +++ b/donut_chart.go @@ -123,7 +123,7 @@ func (pc DonutChart) drawTitle(r Renderer) { func (pc DonutChart) drawSlices(r Renderer, canvasBox Box, values []Value) { cx, cy := canvasBox.Center() - diameter := MinInt(canvasBox.Width(), canvasBox.Height()) + diameter := Min(canvasBox.Width(), canvasBox.Height()) radius := float64(diameter>>1) / 1.1 labelRadius := (radius * 2.83) / 3.0 @@ -195,7 +195,7 @@ func (pc DonutChart) getDefaultCanvasBox() Box { } func (pc DonutChart) getCircleAdjustedCanvasBox(canvasBox Box) Box { - circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height()) + circleDiameter := Min(canvasBox.Width(), canvasBox.Height()) square := Box{ Right: circleDiameter, @@ -241,7 +241,7 @@ func (pc DonutChart) styleDonutChartValue(index int) Style { } func (pc DonutChart) getScaledFontSize() float64 { - effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) + effectiveDimension := Min(pc.GetWidth(), pc.GetHeight()) if effectiveDimension >= 2048 { return 48.0 } else if effectiveDimension >= 1024 { @@ -280,7 +280,7 @@ func (pc DonutChart) styleDefaultsTitle() Style { } func (pc DonutChart) getTitleFontSize() float64 { - effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) + effectiveDimension := Min(pc.GetWidth(), pc.GetHeight()) if effectiveDimension >= 2048 { return 48 } else if effectiveDimension >= 1024 { diff --git a/draw.go b/draw.go index e188079..0bd5364 100644 --- a/draw.go +++ b/draw.go @@ -38,8 +38,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, MinInt(cb, cb-yv0)) - r.LineTo(x0, MinInt(cb, cb-yv0)) + r.LineTo(x, Min(cb, cb-yv0)) + r.LineTo(x0, Min(cb, cb-yv0)) r.LineTo(x0, y0) r.Fill() } diff --git a/examples/custom_padding/main.go b/examples/custom_padding/main.go index 14c23ec..981187f 100644 --- a/examples/custom_padding/main.go +++ b/examples/custom_padding/main.go @@ -22,8 +22,8 @@ func main() { }, Series: []chart.Series{ chart.ContinuousSeries{ - XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), - YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(100).WithMax(512)}.Values(), + XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), + YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(100).WithMax(512)}.Values(), }, }, } diff --git a/examples/linear_regression/main.go b/examples/linear_regression/main.go index ebe0125..c48be1a 100644 --- a/examples/linear_regression/main.go +++ b/examples/linear_regression/main.go @@ -17,8 +17,8 @@ func main() { mainSeries := chart.ContinuousSeries{ 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. - YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 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[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. diff --git a/examples/min_max/main.go b/examples/min_max/main.go index f2c5479..eef4ce2 100644 --- a/examples/min_max/main.go +++ b/examples/min_max/main.go @@ -11,8 +11,8 @@ import ( func main() { mainSeries := chart.ContinuousSeries{ Name: "A test series", - XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), - YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(50).WithMax(150)}.Values(), + XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(1.0).WithEnd(100.0)}.Values(), + YValues: chart.Seq[float64]{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(50).WithMax(150)}.Values(), } minSeries := &chart.MinSeries{ diff --git a/examples/poly_regression/main.go b/examples/poly_regression/main.go index 040fcc0..2ea7852 100644 --- a/examples/poly_regression/main.go +++ b/examples/poly_regression/main.go @@ -17,8 +17,8 @@ func main() { mainSeries := chart.ContinuousSeries{ 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. - YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 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[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{ diff --git a/examples/scatter/main.go b/examples/scatter/main.go index b9ce850..b99d2ce 100644 --- a/examples/scatter/main.go +++ b/examples/scatter/main.go @@ -24,8 +24,8 @@ func drawChart(res http.ResponseWriter, req *http.Request) { DotWidth: 5, DotColorProvider: viridisByY, }, - XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(127)}.Values(), - YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(128).WithMin(0).WithMax(1024)}.Values(), + XValues: chart.Seq[float64]{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(127)}.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{ chart.ContinuousSeries{ - XValues: chart.Seq{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(4)}.Values(), - YValues: 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[float64]{Sequence: chart.NewLinearSequence().WithStart(0).WithEnd(4)}.Values(), }, }, } diff --git a/examples/simple_moving_average/main.go b/examples/simple_moving_average/main.go index 742d15c..d3277f3 100644 --- a/examples/simple_moving_average/main.go +++ b/examples/simple_moving_average/main.go @@ -11,8 +11,8 @@ import ( func main() { mainSeries := chart.ContinuousSeries{ 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. - YValues: chart.Seq{Sequence: chart.NewRandomSequence().WithLen(100).WithMin(0).WithMax(100)}.Values(), //generates a []float64 randomly from 0 to 100 with 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[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. diff --git a/legend.go b/legend.go index fbd48ed..0d60076 100644 --- a/legend.go +++ b/legend.go @@ -68,7 +68,7 @@ func Legend(c *Chart, userDefaults ...Style) Renderable { } legendContent.Bottom += tb.Height() right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum - legendContent.Right = MaxInt(legendContent.Right, right) + legendContent.Right = Max(legendContent.Right, right) labelCount++ } } @@ -163,8 +163,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 = MaxInt(textBox.Height(), textHeight) - textWidth = MaxInt(textBox.Width(), textWidth) + textHeight = Max(textBox.Height(), textHeight) + textWidth = Max(textBox.Width(), textWidth) } } @@ -280,7 +280,7 @@ func LegendLeft(c *Chart, userDefaults ...Style) Renderable { } legendContent.Bottom += tb.Height() right := legendContent.Left + tb.Width() + lineTextGap + lineLengthMinimum - legendContent.Right = MaxInt(legendContent.Right, right) + legendContent.Right = Max(legendContent.Right, right) labelCount++ } } diff --git a/linear_regression_series.go b/linear_regression_series.go index 8ff8b1a..eb0eacb 100644 --- a/linear_regression_series.go +++ b/linear_regression_series.go @@ -59,7 +59,7 @@ func (lrs LinearRegressionSeries) GetYAxis() YAxisType { // Len returns the number of elements in the series. 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. @@ -74,7 +74,7 @@ func (lrs LinearRegressionSeries) GetLimit() int { func (lrs LinearRegressionSeries) GetEndIndex() int { windowEnd := lrs.GetOffset() + lrs.GetLimit() innerSeriesLastIndex := lrs.InnerSeries.Len() - 1 - return MinInt(windowEnd, innerSeriesLastIndex) + return Min(windowEnd, innerSeriesLastIndex) } // GetOffset returns the data offset. @@ -94,7 +94,7 @@ func (lrs *LinearRegressionSeries) GetValues(index int) (x, y float64) { lrs.computeCoefficients() } offset := lrs.GetOffset() - effectiveIndex := MinInt(index+offset, lrs.InnerSeries.Len()) + effectiveIndex := Min(index+offset, lrs.InnerSeries.Len()) x, y = lrs.InnerSeries.GetValues(effectiveIndex) y = (lrs.m * lrs.normalize(x)) + lrs.b return @@ -161,14 +161,14 @@ func (lrs *LinearRegressionSeries) computeCoefficients() { p := float64(endIndex - startIndex) - xvalues := NewValueBufferWithCapacity(lrs.Len()) + xvalues := NewValueBufferWithCapacity[float64](lrs.Len()) for index := startIndex; index < endIndex; index++ { x, _ := lrs.InnerSeries.GetValues(index) xvalues.Enqueue(x) } - lrs.avgx = Seq{xvalues}.Average() - lrs.stddevx = Seq{xvalues}.StdDev() + lrs.avgx = Seq[float64]{xvalues}.Average() + lrs.stddevx = Seq[float64]{xvalues}.StdDev() var sumx, sumy, sumxx, sumxy float64 for index := startIndex; index < endIndex; index++ { diff --git a/linear_sequence.go b/linear_sequence.go index dda761b..cab046c 100644 --- a/linear_sequence.go +++ b/linear_sequence.go @@ -2,12 +2,12 @@ package chart // LinearRange returns an array of values representing the range from start to end, incremented by 1.0. 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. 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. diff --git a/linear_sequence_test.go b/linear_sequence_test.go new file mode 100644 index 0000000..181b0a2 --- /dev/null +++ b/linear_sequence_test.go @@ -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]) +} diff --git a/mathutil.go b/mathutil.go index d1f07f9..4ef52d1 100644 --- a/mathutil.go +++ b/mathutil.go @@ -1,6 +1,9 @@ package chart -import "math" +import ( + "constraints" + "math" +) const ( _pi = math.Pi @@ -17,14 +20,14 @@ const ( ) // 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 { return } max = values[0] min = values[0] - var value float64 + var value A for index := 1; index < len(values); index++ { value = values[index] if value < min { @@ -38,13 +41,13 @@ func MinMax(values ...float64) (min, max float64) { } // MinInt returns the minimum int. -func MinInt(values ...int) (min int) { +func Min[A constraints.Ordered](values ...A) (min A) { if len(values) == 0 { return } min = values[0] - var value int + var value A for index := 1; index < len(values); index++ { value = values[index] if value < min { @@ -55,13 +58,13 @@ func MinInt(values ...int) (min 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 { return } max = values[0] - var value int + var value A for index := 1; index < len(values); index++ { value = values[index] if value > max { @@ -71,9 +74,15 @@ func MaxInt(values ...int) (max int) { return } +// Number is a type that is a number. +type Number interface { + ~int | ~uint | ~float64 +} + // AbsInt returns the absolute value of an int. -func AbsInt(value int) int { - if value < 0 { +func Abs[A Number](value A) A { + var zero A + if value < zero { return -value } return value @@ -173,18 +182,12 @@ func Normalize(values ...float64) []float64 { } // Mean returns the mean of a set of values -func Mean(values ...float64) float64 { - return Sum(values...) / float64(len(values)) -} - -// MeanInt returns the mean of a set of integer values. -func MeanInt(values ...int) int { - return SumInt(values...) / len(values) +func Mean[A Number](values ...A) A { + return Sum(values...) / A(len(values)) } // Sum sums a set of values. -func Sum(values ...float64) float64 { - var total float64 +func Sum[A Number](values ...A) (total A) { for _, v := range values { total += v } diff --git a/matrix/matrix.go b/matrix/matrix.go index 2bb4ba4..f26feaf 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -292,7 +292,7 @@ func (m *Matrix) Copy() *Matrix { // DiagonalVector returns a vector from the diagonal of a matrix. func (m *Matrix) DiagonalVector() Vector { rows, cols := m.Size() - rank := minInt(rows, cols) + rank := Min(rows, cols) values := make([]float64, rank) 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. func (m *Matrix) Diagonal() *Matrix { rows, cols := m.Size() - rank := minInt(rows, cols) + rank := Min(rows, cols) m2 := New(rank, rank) for index := 0; index < rank; index++ { diff --git a/matrix/util.go b/matrix/util.go index 5f15779..4806ede 100644 --- a/matrix/util.go +++ b/matrix/util.go @@ -5,7 +5,7 @@ import ( "strconv" ) -func minInt(values ...int) int { +func Min(values ...int) int { min := math.MaxInt32 for x := 0; x < len(values); x++ { @@ -16,7 +16,7 @@ func minInt(values ...int) int { return min } -func maxInt(values ...int) int { +func Max(values ...int) int { max := math.MinInt32 for x := 0; x < len(values); x++ { diff --git a/pie_chart.go b/pie_chart.go index 49b551f..c981e35 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -123,7 +123,7 @@ func (pc PieChart) drawTitle(r Renderer) { func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []Value) { cx, cy := canvasBox.Center() - diameter := MinInt(canvasBox.Width(), canvasBox.Height()) + diameter := Min(canvasBox.Width(), canvasBox.Height()) radius := float64(diameter >> 1) labelRadius := (radius * 2.0) / 3.0 @@ -191,7 +191,7 @@ func (pc PieChart) getDefaultCanvasBox() Box { } func (pc PieChart) getCircleAdjustedCanvasBox(canvasBox Box) Box { - circleDiameter := MinInt(canvasBox.Width(), canvasBox.Height()) + circleDiameter := Min(canvasBox.Width(), canvasBox.Height()) square := Box{ Right: circleDiameter, @@ -237,7 +237,7 @@ func (pc PieChart) stylePieChartValue(index int) Style { } func (pc PieChart) getScaledFontSize() float64 { - effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) + effectiveDimension := Min(pc.GetWidth(), pc.GetHeight()) if effectiveDimension >= 2048 { return 48.0 } else if effectiveDimension >= 1024 { @@ -276,7 +276,7 @@ func (pc PieChart) styleDefaultsTitle() Style { } func (pc PieChart) getTitleFontSize() float64 { - effectiveDimension := MinInt(pc.GetWidth(), pc.GetHeight()) + effectiveDimension := Min(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 22cd3c1..d0ec101 100644 --- a/polynomial_regression_series.go +++ b/polynomial_regression_series.go @@ -46,7 +46,7 @@ func (prs PolynomialRegressionSeries) GetYAxis() YAxisType { // Len returns the number of elements in the series. 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. @@ -61,7 +61,7 @@ func (prs PolynomialRegressionSeries) GetLimit() int { func (prs PolynomialRegressionSeries) GetEndIndex() int { windowEnd := prs.GetOffset() + prs.GetLimit() innerSeriesLastIndex := prs.InnerSeries.Len() - 1 - return MinInt(windowEnd, innerSeriesLastIndex) + return Min(windowEnd, innerSeriesLastIndex) } // GetOffset returns the data offset. @@ -101,7 +101,7 @@ func (prs *PolynomialRegressionSeries) GetValues(index int) (x, y float64) { } offset := prs.GetOffset() - effectiveIndex := MinInt(index+offset, prs.InnerSeries.Len()) + effectiveIndex := Min(index+offset, prs.InnerSeries.Len()) x, y = prs.InnerSeries.GetValues(effectiveIndex) y = prs.apply(x) return diff --git a/random_sequence.go b/random_sequence.go index 45c9971..9e87833 100644 --- a/random_sequence.go +++ b/random_sequence.go @@ -7,17 +7,17 @@ import ( ) var ( - _ Sequence = (*RandomSeq)(nil) + _ Sequence[float64] = (*RandomSeq)(nil) ) // RandomValues returns an array of random values. 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. 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. diff --git a/seq.go b/seq.go index 76ac40d..4c41f45 100644 --- a/seq.go +++ b/seq.go @@ -5,29 +5,18 @@ import ( "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. -type Seq struct { - Sequence +type Seq[A Number] struct { + Sequence[A] } // Values enumerates the seq into a slice. -func (s Seq) Values() (output []float64) { +func (s Seq[A]) Values() (output []A) { if s.Len() == 0 { return } - output = make([]float64, s.Len()) + output = make([]A, s.Len()) for i := 0; i < s.Len(); 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. -func (s Seq) Each(mapfn func(int, float64)) { +func (s Seq[A]) Each(mapfn func(int, A)) { for i := 0; i < s.Len(); 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, // returning a new seq. -func (s Seq) Map(mapfn func(i int, v float64) float64) Seq { - output := make([]float64, s.Len()) +func (s Seq[A]) Map(mapfn func(i int, v A) A) Seq[A] { + output := make([]A, s.Len()) for i := 0; i < s.Len(); i++ { mapfn(i, s.GetValue(i)) } - return Seq{Array(output)} + return Seq[A]{Sequence: Array(output...)} } // 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 { - return 0 + return } if s.Len() == 1 { return s.GetValue(0) } - v0 = s.GetValue(0) + output = s.GetValue(0) for i := 1; i < s.Len(); i++ { - v0 = mapfn(i, v0, s.GetValue(i)) + output = mapfn(i, output, s.GetValue(i)) } return } // 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 { - return 0 + return } if s.Len() == 1 { return s.GetValue(0) } - v0 = s.GetValue(s.Len() - 1) + output = s.GetValue(s.Len() - 1) for i := s.Len() - 2; i >= 0; i-- { - v0 = mapfn(i, v0, s.GetValue(i)) + output = mapfn(i, output, s.GetValue(i)) } return } // Min returns the minimum value in the seq. -func (s Seq) Min() float64 { +func (s Seq[A]) Min() (min A) { 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++ { value = s.GetValue(i) if value < min { @@ -102,12 +92,12 @@ func (s Seq) Min() float64 { } // Max returns the maximum value in the seq. -func (s Seq) Max() float64 { +func (s Seq[A]) Max() (max A) { if s.Len() == 0 { - return 0 + return } - max := s.GetValue(0) - var value float64 + max = s.GetValue(0) + var value A for i := 1; i < s.Len(); i++ { value = s.GetValue(i) if value > max { @@ -118,13 +108,13 @@ func (s Seq) Max() float64 { } // 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 { return } min = s.GetValue(0) max = min - var value float64 + var value A for i := 1; i < s.Len(); i++ { value = s.GetValue(i) if value < min { @@ -139,17 +129,19 @@ func (s Seq) MinMax() (min, max float64) { // Sort returns the seq sorted in ascending order. // This fully enumerates the seq. -func (s Seq) Sort() Seq { +func (s Seq[A]) Sort() Seq[A] { if s.Len() == 0 { return s } values := s.Values() - sort.Float64s(values) - return Seq{Array(values)} + sort.Slice(values, func(i, j int) bool { + return values[i] < values[j] + }) + return Seq[A]{Array(values...)} } // Reverse reverses the sequence -func (s Seq) Reverse() Seq { +func (s Seq[A]) Reverse() Seq[A] { if s.Len() == 0 { return s } @@ -158,7 +150,7 @@ func (s Seq) Reverse() Seq { valuesLen := len(values) valuesLen1 := len(values) - 1 valuesLen2 := valuesLen >> 1 - var i, j float64 + var i, j A for index := 0; index < valuesLen2; index++ { i = values[index] j = values[valuesLen1-index] @@ -166,32 +158,13 @@ func (s Seq) Reverse() Seq { values[valuesLen1-index] = i } - return Seq{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 + return Seq[A]{Array(values...)} } // 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 { - return 0 + return } 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. -func (s Seq) Average() float64 { +func (s Seq[A]) Average() (avg float64) { 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. -func (s Seq) Variance() float64 { +func (s Seq[A]) Variance() (variance float64) { if s.Len() == 0 { return 0 } m := s.Average() - var variance, v float64 + var v float64 for i := 0; i < s.Len(); i++ { - v = s.GetValue(i) + v = float64(s.GetValue(i)) variance += (v - m) * (v - m) } @@ -226,24 +200,24 @@ func (s Seq) Variance() float64 { } // StdDev returns the standard deviation. -func (s Seq) StdDev() float64 { +func (s Seq[A]) StdDev() float64 { if s.Len() == 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. // `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() if l == 0 { return 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() @@ -262,14 +236,14 @@ func (s Seq) Percentile(percent float64) (percentile float64) { } // 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() - delta := max - min + delta := float64(max - min) output := make([]float64, s.Len()) 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...)} } diff --git a/seq_test.go b/seq_test.go index 3cd7f9c..12ebf89 100644 --- a/seq_test.go +++ b/seq_test.go @@ -6,95 +6,69 @@ import ( "github.com/wcharczuk/go-chart/v2/testutil" ) -func TestSeqEach(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4)} - values.Each(func(i int, v float64) { +func Test_Seq_Each(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4)} + values.Each(func(i int, v int) { testutil.AssertEqual(t, i, v-1) }) } -func TestSeqMap(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4)} - mapped := values.Map(func(i int, v float64) float64 { +func Test_Seq_Map(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4)} + mapped := values.Map(func(i int, v int) int { testutil.AssertEqual(t, i, v-1) return v * 2 }) testutil.AssertEqual(t, 4, mapped.Len()) } -func TestSeqFoldLeft(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4)} - ten := values.FoldLeft(func(_ int, vp, v float64) float64 { +func Test_Seq_FoldLeft(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4)} + ten := values.FoldLeft(func(_ int, vp, v int) int { return vp + v }) testutil.AssertEqual(t, 10, ten) - orderTest := Seq{NewArray(10, 3, 2, 1)} - four := orderTest.FoldLeft(func(_ int, vp, v float64) float64 { + orderTest := Seq[int]{Array(10, 3, 2, 1)} + four := orderTest.FoldLeft(func(_ int, vp, v int) int { return vp - v }) testutil.AssertEqual(t, 4, four) } -func TestSeqFoldRight(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4)} - ten := values.FoldRight(func(_ int, vp, v float64) float64 { +func Test_Seq_FoldRight(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4)} + ten := values.FoldRight(func(_ int, vp, v int) int { return vp + v }) testutil.AssertEqual(t, 10, ten) - orderTest := Seq{NewArray(10, 3, 2, 1)} - notFour := orderTest.FoldRight(func(_ int, vp, v float64) float64 { + orderTest := Seq[int]{Array(10, 3, 2, 1)} + notFour := orderTest.FoldRight(func(_ int, vp, v int) int { return vp - v }) testutil.AssertEqual(t, -14, notFour) } -func TestSeqSum(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4)} +func Test_Seq_Sum(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4)} testutil.AssertEqual(t, 10, values.Sum()) } -func TestSeqAverage(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4)} +func Test_Seq_Average(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4)} 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()) } -func TestSequenceVariance(t *testing.T) { - // replaced new assertions helper - - values := Seq{NewArray(1, 2, 3, 4, 5)} +func Test_Seq_Variance(t *testing.T) { + values := Seq[int]{Array(1, 2, 3, 4, 5)} testutil.AssertEqual(t, 2, values.Variance()) } -func TestSequenceNormalize(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) { +func Test_LinearRange(t *testing.T) { // replaced new assertions helper values := LinearRange(1, 100) @@ -103,7 +77,7 @@ func TestLinearRange(t *testing.T) { testutil.AssertEqual(t, 100, values[99]) } -func TestLinearRangeWithStep(t *testing.T) { +func Test_LinearRange_WithStep(t *testing.T) { // replaced new assertions helper values := LinearRangeWithStep(0, 100, 5) @@ -111,7 +85,7 @@ func TestLinearRangeWithStep(t *testing.T) { testutil.AssertLen(t, values, 21) } -func TestLinearRangeReversed(t *testing.T) { +func Test_LinearRange_reversed(t *testing.T) { // replaced new assertions helper values := LinearRange(10.0, 1.0) @@ -120,7 +94,7 @@ func TestLinearRangeReversed(t *testing.T) { testutil.AssertEqual(t, 1.0, values[9]) } -func TestLinearSequenceRegression(t *testing.T) { +func Test_LinearSequence_Regression(t *testing.T) { // replaced new assertions helper // 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.Len()) - values := Seq{linearProvider}.Values() + values := Seq[float64]{linearProvider}.Values() testutil.AssertLen(t, values, 100) testutil.AssertEqual(t, 1.0, values[0]) testutil.AssertEqual(t, 100, values[99]) diff --git a/sequence.go b/sequence.go new file mode 100644 index 0000000..012f5ee --- /dev/null +++ b/sequence.go @@ -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 +} diff --git a/sma_series.go b/sma_series.go index b952c0a..c0ede2c 100644 --- a/sma_series.go +++ b/sma_series.go @@ -94,7 +94,7 @@ func (sma SMASeries) GetLastValues() (x, y float64) { func (sma SMASeries) getAverage(index int) float64 { period := sma.GetPeriod() - floor := MaxInt(0, index-period) + floor := Max(0, index-period) var accum float64 var count float64 for x := index; x >= floor; x-- { diff --git a/stacked_bar_chart.go b/stacked_bar_chart.go index 10aa545..c85a2eb 100644 --- a/stacked_bar_chart.go +++ b/stacked_bar_chart.go @@ -171,7 +171,7 @@ func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar S Top: yoffset, Left: bxl, 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))) yoffset += barHeight @@ -220,7 +220,7 @@ func (sbc StackedBarChart) drawHorizontalBar(r Renderer, canvasBox Box, yoffset barHeight := int(math.Ceil(bv.Value * float64(canvasBox.Width()))) barBox := Box{ Top: boxTop, - Left: MinInt(xOffset-barHeight, canvasBox.Left+DefaultStrokeWidth), + Left: Min(xOffset-barHeight, canvasBox.Left+DefaultStrokeWidth), Right: xOffset, Bottom: boxBottom, } @@ -455,7 +455,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 = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight) + xaxisHeight = Max(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight) } } return Box{ @@ -498,7 +498,7 @@ func (sbc StackedBarChart) getHorizontalAdjustedCanvasBox(r Renderer, canvasBox lines := Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle) linesBox := Text.MeasureLines(r, lines, axisStyle) - yAxisWidth = MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), yAxisWidth) + yAxisWidth = Max(linesBox.Height()+(2*DefaultXAxisMargin), yAxisWidth) } } return Box{ @@ -552,7 +552,7 @@ func (sbc StackedBarChart) styleDefaultsTitle() Style { } func (sbc StackedBarChart) getScaledFontSize() float64 { - effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight()) + effectiveDimension := Min(sbc.GetWidth(), sbc.GetHeight()) if effectiveDimension >= 2048 { return 48.0 } else if effectiveDimension >= 1024 { @@ -566,7 +566,7 @@ func (sbc StackedBarChart) getScaledFontSize() float64 { } func (sbc StackedBarChart) getTitleFontSize() float64 { - effectiveDimension := MinInt(sbc.GetWidth(), sbc.GetHeight()) + effectiveDimension := Min(sbc.GetWidth(), sbc.GetHeight()) if effectiveDimension >= 2048 { return 48 } else if effectiveDimension >= 1024 { diff --git a/testutil/helpers.go b/testutil/helpers.go index a5774a6..0b7f1f3 100644 --- a/testutil/helpers.go +++ b/testutil/helpers.go @@ -27,7 +27,7 @@ func AssertNotNil(t *testing.T, actual interface{}, message ...interface{}) { } // 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) { t.Errorf("assertion failed; expected %v to equal %v", actual, expected) 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. -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) { t.Errorf("assertion failed; expected %v to not equal %v", actual, expected) if len(message) > 0 { diff --git a/text.go b/text.go index 0a9dfd0..4249290 100644 --- a/text.go +++ b/text.go @@ -147,7 +147,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 = MaxInt(lineBox.Right, output.Right) + output.Right = Max(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 1732c60..68dad5f 100644 --- a/tick.go +++ b/tick.go @@ -84,7 +84,7 @@ func GenerateContinuousTicks(r Renderer, ra Range, isVertical bool, style Style, tickStep := rangeDelta / float64(intermediateTickCount) roundTo := GetRoundToForDelta(rangeDelta) / 10 - intermediateTickCount = MinInt(intermediateTickCount, DefaultTickCountSanityCheck) + intermediateTickCount = Min(intermediateTickCount, DefaultTickCountSanityCheck) for x := 1; x < intermediateTickCount; x++ { var tickValue float64 diff --git a/times.go b/times.go index 95e2acd..b2de9b1 100644 --- a/times.go +++ b/times.go @@ -7,8 +7,8 @@ import ( // Assert types implement interfaces. var ( - _ Sequence = (*Times)(nil) - _ sort.Interface = (*Times)(nil) + _ Sequence[float64] = (*Times)(nil) + _ sort.Interface = (*Times)(nil) ) // Times are an array of times. diff --git a/value_buffer.go b/value_buffer.go index d544bd3..26fbe0a 100644 --- a/value_buffer.go +++ b/value_buffer.go @@ -1,6 +1,7 @@ package chart import ( + "constraints" "fmt" "strings" ) @@ -13,14 +14,14 @@ const ( ) // 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 - array := make([]float64, MaxInt(len(values), bufferDefaultCapacity)) + array := make([]A, Max(len(values), bufferDefaultCapacity)) if len(values) > 0 { copy(array, values) tail = len(values) } - return &ValueBuffer{ + return &ValueBuffer[A]{ array: array, head: 0, tail: tail, @@ -29,21 +30,21 @@ func NewValueBuffer(values ...float64) *ValueBuffer { } // NewValueBufferWithCapacity creates a new ValueBuffer pre-allocated with the given capacity. -func NewValueBufferWithCapacity(capacity int) *ValueBuffer { - return &ValueBuffer{ - array: make([]float64, capacity), +func NewValueBufferWithCapacity[A any](capacity int) *ValueBuffer[A] { + return &ValueBuffer[A]{ + array: make([]A, capacity), head: 0, tail: 0, size: 0, } } -// ValueBuffer is a fifo datastructure that is backed by a pre-allocated array. +// ValueBuffer is a fifo data structure that is backed by a pre-allocated array. // 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). // Buffer implements `seq.Provider` -type ValueBuffer struct { - array []float64 +type ValueBuffer[A any] struct { + array []A head int tail int size int @@ -51,24 +52,24 @@ type ValueBuffer struct { // Len returns the length of the Buffer (as it is currently populated). // Actual memory footprint may be different. -func (b *ValueBuffer) Len() int { +func (b *ValueBuffer[A]) Len() int { return b.size } // 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) return b.array[effectiveIndex] } // 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) } // SetCapacity sets the capacity of the Buffer. -func (b *ValueBuffer) SetCapacity(capacity int) { - newArray := make([]float64, capacity) +func (b *ValueBuffer[A]) SetCapacity(capacity int) { + newArray := make([]A, capacity) if b.size > 0 { if b.head < b.tail { 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. -func (b *ValueBuffer) Clear() { - b.array = make([]float64, bufferDefaultCapacity) +func (b *ValueBuffer[A]) Clear() { + b.array = make([]A, bufferDefaultCapacity) b.head = 0 b.tail = 0 b.size = 0 } // 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) { newCapacity := int(len(b.array) * int(bufferGrowFactor/100)) if newCapacity < (len(b.array) + bufferMinimumGrow) { @@ -110,38 +111,41 @@ func (b *ValueBuffer) Enqueue(value float64) { } // Dequeue removes the first element from the RingBuffer. -func (b *ValueBuffer) Dequeue() float64 { +func (b *ValueBuffer[A]) Dequeue() (output A) { 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.size-- - return removed + return } // 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 { - return 0 + return } - return b.array[b.head] + output = b.array[b.head] + return } // 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 { - return 0 + return } 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. -func (b *ValueBuffer) TrimExcess() { +func (b *ValueBuffer[A]) TrimExcess() { threshold := float64(len(b.array)) * 0.9 if b.size < int(threshold) { b.SetCapacity(b.size) @@ -149,8 +153,8 @@ func (b *ValueBuffer) TrimExcess() { } // Array returns the ring buffer, in order, as an array. -func (b *ValueBuffer) Array() Array { - newArray := make([]float64, b.size) +func (b *ValueBuffer[A]) Array() []A { + newArray := make([]A, b.size) if b.size == 0 { return newArray @@ -163,11 +167,11 @@ func (b *ValueBuffer) Array() Array { 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. -func (b *ValueBuffer) Each(mapfn func(int, float64)) { +func (b *ValueBuffer[A]) Each(mapfn func(int, A)) { if b.size == 0 { return } @@ -191,10 +195,10 @@ func (b *ValueBuffer) Each(mapfn func(int, float64)) { } // String returns a string representation for value buffers. -func (b *ValueBuffer) String() string { +func (b *ValueBuffer[A]) String() string { var values []string for _, elem := range b.Array() { - values = append(values, fmt.Sprintf("%v", elem)) + values = append(values, fmt.Sprint(elem)) } return strings.Join(values, " <= ") } @@ -203,14 +207,15 @@ func (b *ValueBuffer) String() string { // 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++ { 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++ { from := sourceIndex + x to := destinationIndex + x diff --git a/value_buffer_test.go b/value_buffer_test.go index e8aa9a0..ecb0ff6 100644 --- a/value_buffer_test.go +++ b/value_buffer_test.go @@ -6,10 +6,8 @@ import ( "github.com/wcharczuk/go-chart/v2/testutil" ) -func TestBuffer(t *testing.T) { - // replaced new assertions helper - - buffer := NewValueBuffer() +func Test_ValueBuffer(t *testing.T) { + buffer := NewValueBuffer[int]() buffer.Enqueue(1) testutil.AssertEqual(t, 1, buffer.Len()) @@ -100,10 +98,8 @@ func TestBuffer(t *testing.T) { testutil.AssertZero(t, buffer.PeekBack()) } -func TestBufferClear(t *testing.T) { - // replaced new assertions helper - - buffer := NewValueBuffer() +func Test_ValueBufferClear(t *testing.T) { + buffer := NewValueBuffer[int]() buffer.Enqueue(1) buffer.Enqueue(1) buffer.Enqueue(1) @@ -121,10 +117,8 @@ func TestBufferClear(t *testing.T) { testutil.AssertZero(t, buffer.PeekBack()) } -func TestBufferArray(t *testing.T) { - // replaced new assertions helper - - buffer := NewValueBuffer() +func Test_ValueBuffer_Array(t *testing.T) { + buffer := NewValueBuffer[int]() buffer.Enqueue(1) buffer.Enqueue(2) buffer.Enqueue(3) @@ -140,10 +134,8 @@ func TestBufferArray(t *testing.T) { testutil.AssertEqual(t, 5, contents[4]) } -func TestBufferEach(t *testing.T) { - // replaced new assertions helper - - buffer := NewValueBuffer() +func Test_ValueBuffer_Each(t *testing.T) { + buffer := NewValueBuffer[float64]() for x := 1; x < 17; x++ { buffer.Enqueue(float64(x)) @@ -159,10 +151,8 @@ func TestBufferEach(t *testing.T) { testutil.AssertEqual(t, 16, called) } -func TestNewBuffer(t *testing.T) { - // replaced new assertions helper - - empty := NewValueBuffer() +func Test_NewValueBuffer(t *testing.T) { + empty := NewValueBuffer[int]() testutil.AssertNotNil(t, empty) testutil.AssertZero(t, empty.Len()) testutil.AssertEqual(t, bufferDefaultCapacity, empty.Capacity()) @@ -170,9 +160,7 @@ func TestNewBuffer(t *testing.T) { testutil.AssertZero(t, empty.PeekBack()) } -func TestNewBufferWithValues(t *testing.T) { - // replaced new assertions helper - +func Test_NewBuffer_withValues(t *testing.T) { values := NewValueBuffer(1, 2, 3, 4, 5) testutil.AssertNotNil(t, values) testutil.AssertEqual(t, 5, values.Len()) @@ -180,10 +168,8 @@ func TestNewBufferWithValues(t *testing.T) { testutil.AssertEqual(t, 5, values.PeekBack()) } -func TestBufferGrowth(t *testing.T) { - // replaced new assertions helper - - values := NewValueBuffer(1, 2, 3, 4, 5) +func Test_ValueBuffer_growth(t *testing.T) { + values := NewValueBuffer[float64](1, 2, 3, 4, 5) for i := 0; i < 1<<10; i++ { values.Enqueue(float64(i)) } diff --git a/value_sequence.go b/value_sequence.go new file mode 100644 index 0000000..6ffd72a --- /dev/null +++ b/value_sequence.go @@ -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...), + } +} diff --git a/value_sequence_test.go b/value_sequence_test.go new file mode 100644 index 0000000..a345c30 --- /dev/null +++ b/value_sequence_test.go @@ -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]) +} diff --git a/viridis.go b/viridis.go index 4234319..0611850 100644 --- a/viridis.go +++ b/viridis.go @@ -3,262 +3,262 @@ package chart import "github.com/wcharczuk/go-chart/v2/drawing" var viridisColors = [256]drawing.Color{ - drawing.Color{R: 0x44, G: 0x1, B: 0x54, A: 0xff}, - drawing.Color{R: 0x44, G: 0x2, B: 0x55, A: 0xff}, - drawing.Color{R: 0x45, G: 0x3, B: 0x57, A: 0xff}, - drawing.Color{R: 0x45, G: 0x5, B: 0x58, A: 0xff}, - drawing.Color{R: 0x45, G: 0x6, B: 0x5a, A: 0xff}, - drawing.Color{R: 0x46, G: 0x8, B: 0x5b, A: 0xff}, - drawing.Color{R: 0x46, G: 0x9, B: 0x5d, A: 0xff}, - drawing.Color{R: 0x46, G: 0xb, B: 0x5e, A: 0xff}, - drawing.Color{R: 0x46, G: 0xc, B: 0x60, A: 0xff}, - drawing.Color{R: 0x47, G: 0xe, B: 0x61, A: 0xff}, - drawing.Color{R: 0x47, G: 0xf, B: 0x62, A: 0xff}, - drawing.Color{R: 0x47, G: 0x11, B: 0x64, A: 0xff}, - drawing.Color{R: 0x47, G: 0x12, B: 0x65, A: 0xff}, - drawing.Color{R: 0x47, G: 0x14, B: 0x66, A: 0xff}, - drawing.Color{R: 0x48, G: 0x15, B: 0x68, A: 0xff}, - drawing.Color{R: 0x48, G: 0x16, B: 0x69, A: 0xff}, - drawing.Color{R: 0x48, G: 0x18, B: 0x6a, A: 0xff}, - drawing.Color{R: 0x48, G: 0x19, B: 0x6c, A: 0xff}, - drawing.Color{R: 0x48, G: 0x1a, B: 0x6d, A: 0xff}, - drawing.Color{R: 0x48, G: 0x1c, B: 0x6e, A: 0xff}, - drawing.Color{R: 0x48, G: 0x1d, B: 0x6f, A: 0xff}, - drawing.Color{R: 0x48, G: 0x1e, B: 0x70, A: 0xff}, - drawing.Color{R: 0x48, G: 0x20, B: 0x71, A: 0xff}, - drawing.Color{R: 0x48, G: 0x21, B: 0x73, A: 0xff}, - drawing.Color{R: 0x48, G: 0x22, B: 0x74, A: 0xff}, - drawing.Color{R: 0x48, G: 0x24, B: 0x75, A: 0xff}, - drawing.Color{R: 0x48, G: 0x25, B: 0x76, A: 0xff}, - drawing.Color{R: 0x48, G: 0x26, B: 0x77, A: 0xff}, - drawing.Color{R: 0x48, G: 0x27, B: 0x78, A: 0xff}, - drawing.Color{R: 0x47, G: 0x29, B: 0x79, A: 0xff}, - drawing.Color{R: 0x47, G: 0x2a, B: 0x79, A: 0xff}, - drawing.Color{R: 0x47, G: 0x2b, B: 0x7a, A: 0xff}, - drawing.Color{R: 0x47, G: 0x2c, B: 0x7b, A: 0xff}, - drawing.Color{R: 0x47, G: 0x2e, B: 0x7c, A: 0xff}, - drawing.Color{R: 0x46, G: 0x2f, B: 0x7d, A: 0xff}, - drawing.Color{R: 0x46, G: 0x30, B: 0x7e, A: 0xff}, - drawing.Color{R: 0x46, G: 0x31, B: 0x7e, A: 0xff}, - drawing.Color{R: 0x46, G: 0x33, B: 0x7f, A: 0xff}, - drawing.Color{R: 0x45, G: 0x34, B: 0x80, A: 0xff}, - drawing.Color{R: 0x45, G: 0x35, B: 0x81, A: 0xff}, - drawing.Color{R: 0x45, G: 0x36, B: 0x81, A: 0xff}, - drawing.Color{R: 0x44, G: 0x38, B: 0x82, A: 0xff}, - drawing.Color{R: 0x44, G: 0x39, B: 0x83, A: 0xff}, - drawing.Color{R: 0x44, G: 0x3a, B: 0x83, A: 0xff}, - drawing.Color{R: 0x43, G: 0x3b, B: 0x84, A: 0xff}, - drawing.Color{R: 0x43, G: 0x3c, B: 0x84, A: 0xff}, - drawing.Color{R: 0x43, G: 0x3e, B: 0x85, A: 0xff}, - drawing.Color{R: 0x42, G: 0x3f, B: 0x85, A: 0xff}, - drawing.Color{R: 0x42, G: 0x40, B: 0x86, A: 0xff}, - drawing.Color{R: 0x41, G: 0x41, B: 0x86, A: 0xff}, - drawing.Color{R: 0x41, G: 0x42, B: 0x87, A: 0xff}, - drawing.Color{R: 0x41, G: 0x43, B: 0x87, A: 0xff}, - drawing.Color{R: 0x40, G: 0x45, B: 0x88, A: 0xff}, - drawing.Color{R: 0x40, G: 0x46, B: 0x88, A: 0xff}, - drawing.Color{R: 0x3f, G: 0x47, B: 0x88, A: 0xff}, - drawing.Color{R: 0x3f, G: 0x48, B: 0x89, A: 0xff}, - drawing.Color{R: 0x3e, G: 0x49, B: 0x89, A: 0xff}, - drawing.Color{R: 0x3e, G: 0x4a, B: 0x89, A: 0xff}, - drawing.Color{R: 0x3d, G: 0x4b, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x3d, G: 0x4d, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x3c, G: 0x4e, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x3c, G: 0x4f, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x3b, G: 0x50, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x3b, G: 0x51, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x3a, G: 0x52, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x3a, G: 0x53, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x39, G: 0x54, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x39, G: 0x55, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x38, G: 0x56, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x38, G: 0x57, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x37, G: 0x58, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x37, G: 0x59, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x36, G: 0x5b, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x36, G: 0x5c, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x35, G: 0x5d, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x35, G: 0x5e, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x34, G: 0x5f, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x34, G: 0x60, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x33, G: 0x61, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x33, G: 0x62, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x33, G: 0x63, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x32, G: 0x64, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x32, G: 0x65, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x31, G: 0x66, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x31, G: 0x67, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x30, G: 0x68, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x30, G: 0x69, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2f, G: 0x6a, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2f, G: 0x6b, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2f, G: 0x6c, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2e, G: 0x6d, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2e, G: 0x6e, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2d, G: 0x6f, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2d, G: 0x70, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2d, G: 0x70, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2c, G: 0x71, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2c, G: 0x72, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2b, G: 0x73, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2b, G: 0x74, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2b, G: 0x75, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2a, G: 0x76, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x2a, G: 0x77, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x29, G: 0x78, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x29, G: 0x79, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x29, G: 0x7a, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x28, G: 0x7b, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x28, G: 0x7c, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x28, G: 0x7d, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x27, G: 0x7e, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x27, G: 0x7f, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x26, G: 0x80, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x26, G: 0x81, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x26, G: 0x82, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x25, G: 0x83, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x25, G: 0x83, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x25, G: 0x84, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x24, G: 0x85, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x24, G: 0x86, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x23, G: 0x87, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x23, G: 0x88, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x23, G: 0x89, B: 0x8e, A: 0xff}, - drawing.Color{R: 0x22, G: 0x8a, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x22, G: 0x8b, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x22, G: 0x8c, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x21, G: 0x8d, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x21, G: 0x8e, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x21, G: 0x8f, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x20, G: 0x90, B: 0x8d, A: 0xff}, - drawing.Color{R: 0x20, G: 0x91, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x20, G: 0x92, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x20, G: 0x93, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x1f, G: 0x93, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x1f, G: 0x94, B: 0x8c, A: 0xff}, - drawing.Color{R: 0x1f, G: 0x95, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x1f, G: 0x96, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x1f, G: 0x97, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x98, B: 0x8b, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x99, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x9a, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x9b, B: 0x8a, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x9c, B: 0x89, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x9d, B: 0x89, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x9e, B: 0x89, A: 0xff}, - drawing.Color{R: 0x1e, G: 0x9f, B: 0x88, A: 0xff}, - drawing.Color{R: 0x1e, G: 0xa0, B: 0x88, A: 0xff}, - drawing.Color{R: 0x1f, G: 0xa1, B: 0x88, A: 0xff}, - drawing.Color{R: 0x1f, G: 0xa2, B: 0x87, A: 0xff}, - drawing.Color{R: 0x1f, G: 0xa3, B: 0x87, A: 0xff}, - drawing.Color{R: 0x1f, G: 0xa3, B: 0x86, A: 0xff}, - drawing.Color{R: 0x20, G: 0xa4, B: 0x86, A: 0xff}, - drawing.Color{R: 0x20, G: 0xa5, B: 0x86, A: 0xff}, - drawing.Color{R: 0x21, G: 0xa6, B: 0x85, A: 0xff}, - drawing.Color{R: 0x21, G: 0xa7, B: 0x85, A: 0xff}, - drawing.Color{R: 0x22, G: 0xa8, B: 0x84, A: 0xff}, - drawing.Color{R: 0x23, G: 0xa9, B: 0x83, A: 0xff}, - drawing.Color{R: 0x23, G: 0xaa, B: 0x83, A: 0xff}, - drawing.Color{R: 0x24, G: 0xab, B: 0x82, A: 0xff}, - drawing.Color{R: 0x25, G: 0xac, B: 0x82, A: 0xff}, - drawing.Color{R: 0x26, G: 0xad, B: 0x81, A: 0xff}, - drawing.Color{R: 0x27, G: 0xae, B: 0x81, A: 0xff}, - drawing.Color{R: 0x28, G: 0xaf, B: 0x80, A: 0xff}, - drawing.Color{R: 0x29, G: 0xaf, B: 0x7f, A: 0xff}, - drawing.Color{R: 0x2a, G: 0xb0, B: 0x7f, A: 0xff}, - drawing.Color{R: 0x2b, G: 0xb1, B: 0x7e, A: 0xff}, - drawing.Color{R: 0x2c, G: 0xb2, B: 0x7d, A: 0xff}, - drawing.Color{R: 0x2e, G: 0xb3, B: 0x7c, A: 0xff}, - drawing.Color{R: 0x2f, G: 0xb4, B: 0x7c, A: 0xff}, - drawing.Color{R: 0x30, G: 0xb5, B: 0x7b, A: 0xff}, - drawing.Color{R: 0x32, G: 0xb6, B: 0x7a, A: 0xff}, - drawing.Color{R: 0x33, G: 0xb7, B: 0x79, A: 0xff}, - drawing.Color{R: 0x35, G: 0xb7, B: 0x79, A: 0xff}, - drawing.Color{R: 0x36, G: 0xb8, B: 0x78, A: 0xff}, - drawing.Color{R: 0x38, G: 0xb9, B: 0x77, A: 0xff}, - drawing.Color{R: 0x39, G: 0xba, B: 0x76, A: 0xff}, - drawing.Color{R: 0x3b, G: 0xbb, B: 0x75, A: 0xff}, - drawing.Color{R: 0x3d, G: 0xbc, B: 0x74, A: 0xff}, - drawing.Color{R: 0x3e, G: 0xbd, B: 0x73, A: 0xff}, - drawing.Color{R: 0x40, G: 0xbe, B: 0x72, A: 0xff}, - drawing.Color{R: 0x42, G: 0xbe, B: 0x71, A: 0xff}, - drawing.Color{R: 0x44, G: 0xbf, B: 0x70, A: 0xff}, - drawing.Color{R: 0x46, G: 0xc0, B: 0x6f, A: 0xff}, - drawing.Color{R: 0x48, G: 0xc1, B: 0x6e, A: 0xff}, - drawing.Color{R: 0x49, G: 0xc2, B: 0x6d, A: 0xff}, - drawing.Color{R: 0x4b, G: 0xc2, B: 0x6c, A: 0xff}, - drawing.Color{R: 0x4d, G: 0xc3, B: 0x6b, A: 0xff}, - drawing.Color{R: 0x4f, G: 0xc4, B: 0x6a, A: 0xff}, - drawing.Color{R: 0x51, G: 0xc5, B: 0x69, A: 0xff}, - drawing.Color{R: 0x53, G: 0xc6, B: 0x68, A: 0xff}, - drawing.Color{R: 0x55, G: 0xc6, B: 0x66, A: 0xff}, - drawing.Color{R: 0x58, G: 0xc7, B: 0x65, A: 0xff}, - drawing.Color{R: 0x5a, G: 0xc8, B: 0x64, A: 0xff}, - drawing.Color{R: 0x5c, G: 0xc9, B: 0x63, A: 0xff}, - drawing.Color{R: 0x5e, G: 0xc9, B: 0x62, A: 0xff}, - drawing.Color{R: 0x60, G: 0xca, B: 0x60, A: 0xff}, - drawing.Color{R: 0x62, G: 0xcb, B: 0x5f, A: 0xff}, - drawing.Color{R: 0x65, G: 0xcc, B: 0x5e, A: 0xff}, - drawing.Color{R: 0x67, G: 0xcc, B: 0x5c, A: 0xff}, - drawing.Color{R: 0x69, G: 0xcd, B: 0x5b, A: 0xff}, - drawing.Color{R: 0x6c, G: 0xce, B: 0x5a, A: 0xff}, - drawing.Color{R: 0x6e, G: 0xce, B: 0x58, A: 0xff}, - drawing.Color{R: 0x70, G: 0xcf, B: 0x57, A: 0xff}, - drawing.Color{R: 0x73, G: 0xd0, B: 0x55, A: 0xff}, - drawing.Color{R: 0x75, G: 0xd0, B: 0x54, A: 0xff}, - drawing.Color{R: 0x77, G: 0xd1, B: 0x52, A: 0xff}, - drawing.Color{R: 0x7a, G: 0xd2, B: 0x51, A: 0xff}, - drawing.Color{R: 0x7c, G: 0xd2, B: 0x4f, A: 0xff}, - drawing.Color{R: 0x7f, G: 0xd3, B: 0x4e, A: 0xff}, - drawing.Color{R: 0x81, G: 0xd4, B: 0x4c, A: 0xff}, - drawing.Color{R: 0x84, G: 0xd4, B: 0x4b, A: 0xff}, - drawing.Color{R: 0x86, G: 0xd5, B: 0x49, A: 0xff}, - drawing.Color{R: 0x89, G: 0xd5, B: 0x48, A: 0xff}, - drawing.Color{R: 0x8b, G: 0xd6, B: 0x46, A: 0xff}, - drawing.Color{R: 0x8e, G: 0xd7, B: 0x44, A: 0xff}, - drawing.Color{R: 0x90, G: 0xd7, B: 0x43, A: 0xff}, - drawing.Color{R: 0x93, G: 0xd8, B: 0x41, A: 0xff}, - drawing.Color{R: 0x95, G: 0xd8, B: 0x3f, A: 0xff}, - drawing.Color{R: 0x98, G: 0xd9, B: 0x3e, A: 0xff}, - drawing.Color{R: 0x9b, G: 0xd9, B: 0x3c, A: 0xff}, - drawing.Color{R: 0x9d, G: 0xda, B: 0x3a, A: 0xff}, - drawing.Color{R: 0xa0, G: 0xda, B: 0x39, A: 0xff}, - drawing.Color{R: 0xa3, G: 0xdb, B: 0x37, A: 0xff}, - drawing.Color{R: 0xa5, G: 0xdb, B: 0x35, A: 0xff}, - drawing.Color{R: 0xa8, G: 0xdc, B: 0x33, A: 0xff}, - drawing.Color{R: 0xab, G: 0xdc, B: 0x32, A: 0xff}, - drawing.Color{R: 0xad, G: 0xdd, B: 0x30, A: 0xff}, - drawing.Color{R: 0xb0, G: 0xdd, B: 0x2e, A: 0xff}, - drawing.Color{R: 0xb3, G: 0xdd, B: 0x2d, A: 0xff}, - drawing.Color{R: 0xb5, G: 0xde, B: 0x2b, A: 0xff}, - drawing.Color{R: 0xb8, G: 0xde, B: 0x29, A: 0xff}, - drawing.Color{R: 0xbb, G: 0xdf, B: 0x27, A: 0xff}, - drawing.Color{R: 0xbd, G: 0xdf, B: 0x26, A: 0xff}, - drawing.Color{R: 0xc0, G: 0xdf, B: 0x24, A: 0xff}, - drawing.Color{R: 0xc3, G: 0xe0, B: 0x23, A: 0xff}, - drawing.Color{R: 0xc5, G: 0xe0, B: 0x21, A: 0xff}, - drawing.Color{R: 0xc8, G: 0xe1, B: 0x20, A: 0xff}, - drawing.Color{R: 0xcb, G: 0xe1, B: 0x1e, A: 0xff}, - drawing.Color{R: 0xcd, G: 0xe1, B: 0x1d, A: 0xff}, - drawing.Color{R: 0xd0, G: 0xe2, B: 0x1c, A: 0xff}, - drawing.Color{R: 0xd3, G: 0xe2, B: 0x1b, A: 0xff}, - drawing.Color{R: 0xd5, G: 0xe2, B: 0x1a, A: 0xff}, - drawing.Color{R: 0xd8, G: 0xe3, B: 0x19, A: 0xff}, - drawing.Color{R: 0xdb, G: 0xe3, B: 0x18, A: 0xff}, - drawing.Color{R: 0xdd, G: 0xe3, B: 0x18, A: 0xff}, - drawing.Color{R: 0xe0, G: 0xe4, B: 0x18, A: 0xff}, - drawing.Color{R: 0xe2, G: 0xe4, B: 0x18, A: 0xff}, - drawing.Color{R: 0xe5, G: 0xe4, B: 0x18, A: 0xff}, - drawing.Color{R: 0xe8, G: 0xe5, B: 0x19, A: 0xff}, - drawing.Color{R: 0xea, G: 0xe5, B: 0x19, A: 0xff}, - drawing.Color{R: 0xed, G: 0xe5, B: 0x1a, A: 0xff}, - drawing.Color{R: 0xef, G: 0xe6, B: 0x1b, A: 0xff}, - drawing.Color{R: 0xf2, G: 0xe6, B: 0x1c, A: 0xff}, - drawing.Color{R: 0xf4, G: 0xe6, B: 0x1e, A: 0xff}, - drawing.Color{R: 0xf7, G: 0xe6, B: 0x1f, A: 0xff}, - drawing.Color{R: 0xf9, G: 0xe7, B: 0x21, A: 0xff}, - drawing.Color{R: 0xfb, G: 0xe7, B: 0x23, A: 0xff}, - drawing.Color{R: 0xfe, G: 0xe7, B: 0x24, A: 0xff}, + {R: 0x44, G: 0x1, B: 0x54, A: 0xff}, + {R: 0x44, G: 0x2, B: 0x55, A: 0xff}, + {R: 0x45, G: 0x3, B: 0x57, A: 0xff}, + {R: 0x45, G: 0x5, B: 0x58, A: 0xff}, + {R: 0x45, G: 0x6, B: 0x5a, A: 0xff}, + {R: 0x46, G: 0x8, B: 0x5b, A: 0xff}, + {R: 0x46, G: 0x9, B: 0x5d, A: 0xff}, + {R: 0x46, G: 0xb, B: 0x5e, A: 0xff}, + {R: 0x46, G: 0xc, B: 0x60, A: 0xff}, + {R: 0x47, G: 0xe, B: 0x61, A: 0xff}, + {R: 0x47, G: 0xf, B: 0x62, A: 0xff}, + {R: 0x47, G: 0x11, B: 0x64, A: 0xff}, + {R: 0x47, G: 0x12, B: 0x65, A: 0xff}, + {R: 0x47, G: 0x14, B: 0x66, A: 0xff}, + {R: 0x48, G: 0x15, B: 0x68, A: 0xff}, + {R: 0x48, G: 0x16, B: 0x69, A: 0xff}, + {R: 0x48, G: 0x18, B: 0x6a, A: 0xff}, + {R: 0x48, G: 0x19, B: 0x6c, A: 0xff}, + {R: 0x48, G: 0x1a, B: 0x6d, A: 0xff}, + {R: 0x48, G: 0x1c, B: 0x6e, A: 0xff}, + {R: 0x48, G: 0x1d, B: 0x6f, A: 0xff}, + {R: 0x48, G: 0x1e, B: 0x70, A: 0xff}, + {R: 0x48, G: 0x20, B: 0x71, A: 0xff}, + {R: 0x48, G: 0x21, B: 0x73, A: 0xff}, + {R: 0x48, G: 0x22, B: 0x74, A: 0xff}, + {R: 0x48, G: 0x24, B: 0x75, A: 0xff}, + {R: 0x48, G: 0x25, B: 0x76, A: 0xff}, + {R: 0x48, G: 0x26, B: 0x77, A: 0xff}, + {R: 0x48, G: 0x27, B: 0x78, A: 0xff}, + {R: 0x47, G: 0x29, B: 0x79, A: 0xff}, + {R: 0x47, G: 0x2a, B: 0x79, A: 0xff}, + {R: 0x47, G: 0x2b, B: 0x7a, A: 0xff}, + {R: 0x47, G: 0x2c, B: 0x7b, A: 0xff}, + {R: 0x47, G: 0x2e, B: 0x7c, A: 0xff}, + {R: 0x46, G: 0x2f, B: 0x7d, A: 0xff}, + {R: 0x46, G: 0x30, B: 0x7e, A: 0xff}, + {R: 0x46, G: 0x31, B: 0x7e, A: 0xff}, + {R: 0x46, G: 0x33, B: 0x7f, A: 0xff}, + {R: 0x45, G: 0x34, B: 0x80, A: 0xff}, + {R: 0x45, G: 0x35, B: 0x81, A: 0xff}, + {R: 0x45, G: 0x36, B: 0x81, A: 0xff}, + {R: 0x44, G: 0x38, B: 0x82, A: 0xff}, + {R: 0x44, G: 0x39, B: 0x83, A: 0xff}, + {R: 0x44, G: 0x3a, B: 0x83, A: 0xff}, + {R: 0x43, G: 0x3b, B: 0x84, A: 0xff}, + {R: 0x43, G: 0x3c, B: 0x84, A: 0xff}, + {R: 0x43, G: 0x3e, B: 0x85, A: 0xff}, + {R: 0x42, G: 0x3f, B: 0x85, A: 0xff}, + {R: 0x42, G: 0x40, B: 0x86, A: 0xff}, + {R: 0x41, G: 0x41, B: 0x86, A: 0xff}, + {R: 0x41, G: 0x42, B: 0x87, A: 0xff}, + {R: 0x41, G: 0x43, B: 0x87, A: 0xff}, + {R: 0x40, G: 0x45, B: 0x88, A: 0xff}, + {R: 0x40, G: 0x46, B: 0x88, A: 0xff}, + {R: 0x3f, G: 0x47, B: 0x88, A: 0xff}, + {R: 0x3f, G: 0x48, B: 0x89, A: 0xff}, + {R: 0x3e, G: 0x49, B: 0x89, A: 0xff}, + {R: 0x3e, G: 0x4a, B: 0x89, A: 0xff}, + {R: 0x3d, G: 0x4b, B: 0x8a, A: 0xff}, + {R: 0x3d, G: 0x4d, B: 0x8a, A: 0xff}, + {R: 0x3c, G: 0x4e, B: 0x8a, A: 0xff}, + {R: 0x3c, G: 0x4f, B: 0x8a, A: 0xff}, + {R: 0x3b, G: 0x50, B: 0x8b, A: 0xff}, + {R: 0x3b, G: 0x51, B: 0x8b, A: 0xff}, + {R: 0x3a, G: 0x52, B: 0x8b, A: 0xff}, + {R: 0x3a, G: 0x53, B: 0x8b, A: 0xff}, + {R: 0x39, G: 0x54, B: 0x8c, A: 0xff}, + {R: 0x39, G: 0x55, B: 0x8c, A: 0xff}, + {R: 0x38, G: 0x56, B: 0x8c, A: 0xff}, + {R: 0x38, G: 0x57, B: 0x8c, A: 0xff}, + {R: 0x37, G: 0x58, B: 0x8c, A: 0xff}, + {R: 0x37, G: 0x59, B: 0x8c, A: 0xff}, + {R: 0x36, G: 0x5b, B: 0x8d, A: 0xff}, + {R: 0x36, G: 0x5c, B: 0x8d, A: 0xff}, + {R: 0x35, G: 0x5d, B: 0x8d, A: 0xff}, + {R: 0x35, G: 0x5e, B: 0x8d, A: 0xff}, + {R: 0x34, G: 0x5f, B: 0x8d, A: 0xff}, + {R: 0x34, G: 0x60, B: 0x8d, A: 0xff}, + {R: 0x33, G: 0x61, B: 0x8d, A: 0xff}, + {R: 0x33, G: 0x62, B: 0x8d, A: 0xff}, + {R: 0x33, G: 0x63, B: 0x8d, A: 0xff}, + {R: 0x32, G: 0x64, B: 0x8e, A: 0xff}, + {R: 0x32, G: 0x65, B: 0x8e, A: 0xff}, + {R: 0x31, G: 0x66, B: 0x8e, A: 0xff}, + {R: 0x31, G: 0x67, B: 0x8e, A: 0xff}, + {R: 0x30, G: 0x68, B: 0x8e, A: 0xff}, + {R: 0x30, G: 0x69, B: 0x8e, A: 0xff}, + {R: 0x2f, G: 0x6a, B: 0x8e, A: 0xff}, + {R: 0x2f, G: 0x6b, B: 0x8e, A: 0xff}, + {R: 0x2f, G: 0x6c, B: 0x8e, A: 0xff}, + {R: 0x2e, G: 0x6d, B: 0x8e, A: 0xff}, + {R: 0x2e, G: 0x6e, B: 0x8e, A: 0xff}, + {R: 0x2d, G: 0x6f, B: 0x8e, A: 0xff}, + {R: 0x2d, G: 0x70, B: 0x8e, A: 0xff}, + {R: 0x2d, G: 0x70, B: 0x8e, A: 0xff}, + {R: 0x2c, G: 0x71, B: 0x8e, A: 0xff}, + {R: 0x2c, G: 0x72, B: 0x8e, A: 0xff}, + {R: 0x2b, G: 0x73, B: 0x8e, A: 0xff}, + {R: 0x2b, G: 0x74, B: 0x8e, A: 0xff}, + {R: 0x2b, G: 0x75, B: 0x8e, A: 0xff}, + {R: 0x2a, G: 0x76, B: 0x8e, A: 0xff}, + {R: 0x2a, G: 0x77, B: 0x8e, A: 0xff}, + {R: 0x29, G: 0x78, B: 0x8e, A: 0xff}, + {R: 0x29, G: 0x79, B: 0x8e, A: 0xff}, + {R: 0x29, G: 0x7a, B: 0x8e, A: 0xff}, + {R: 0x28, G: 0x7b, B: 0x8e, A: 0xff}, + {R: 0x28, G: 0x7c, B: 0x8e, A: 0xff}, + {R: 0x28, G: 0x7d, B: 0x8e, A: 0xff}, + {R: 0x27, G: 0x7e, B: 0x8e, A: 0xff}, + {R: 0x27, G: 0x7f, B: 0x8e, A: 0xff}, + {R: 0x26, G: 0x80, B: 0x8e, A: 0xff}, + {R: 0x26, G: 0x81, B: 0x8e, A: 0xff}, + {R: 0x26, G: 0x82, B: 0x8e, A: 0xff}, + {R: 0x25, G: 0x83, B: 0x8e, A: 0xff}, + {R: 0x25, G: 0x83, B: 0x8e, A: 0xff}, + {R: 0x25, G: 0x84, B: 0x8e, A: 0xff}, + {R: 0x24, G: 0x85, B: 0x8e, A: 0xff}, + {R: 0x24, G: 0x86, B: 0x8e, A: 0xff}, + {R: 0x23, G: 0x87, B: 0x8e, A: 0xff}, + {R: 0x23, G: 0x88, B: 0x8e, A: 0xff}, + {R: 0x23, G: 0x89, B: 0x8e, A: 0xff}, + {R: 0x22, G: 0x8a, B: 0x8d, A: 0xff}, + {R: 0x22, G: 0x8b, B: 0x8d, A: 0xff}, + {R: 0x22, G: 0x8c, B: 0x8d, A: 0xff}, + {R: 0x21, G: 0x8d, B: 0x8d, A: 0xff}, + {R: 0x21, G: 0x8e, B: 0x8d, A: 0xff}, + {R: 0x21, G: 0x8f, B: 0x8d, A: 0xff}, + {R: 0x20, G: 0x90, B: 0x8d, A: 0xff}, + {R: 0x20, G: 0x91, B: 0x8c, A: 0xff}, + {R: 0x20, G: 0x92, B: 0x8c, A: 0xff}, + {R: 0x20, G: 0x93, B: 0x8c, A: 0xff}, + {R: 0x1f, G: 0x93, B: 0x8c, A: 0xff}, + {R: 0x1f, G: 0x94, B: 0x8c, A: 0xff}, + {R: 0x1f, G: 0x95, B: 0x8b, A: 0xff}, + {R: 0x1f, G: 0x96, B: 0x8b, A: 0xff}, + {R: 0x1f, G: 0x97, B: 0x8b, A: 0xff}, + {R: 0x1e, G: 0x98, B: 0x8b, A: 0xff}, + {R: 0x1e, G: 0x99, B: 0x8a, A: 0xff}, + {R: 0x1e, G: 0x9a, B: 0x8a, A: 0xff}, + {R: 0x1e, G: 0x9b, B: 0x8a, A: 0xff}, + {R: 0x1e, G: 0x9c, B: 0x89, A: 0xff}, + {R: 0x1e, G: 0x9d, B: 0x89, A: 0xff}, + {R: 0x1e, G: 0x9e, B: 0x89, A: 0xff}, + {R: 0x1e, G: 0x9f, B: 0x88, A: 0xff}, + {R: 0x1e, G: 0xa0, B: 0x88, A: 0xff}, + {R: 0x1f, G: 0xa1, B: 0x88, A: 0xff}, + {R: 0x1f, G: 0xa2, B: 0x87, A: 0xff}, + {R: 0x1f, G: 0xa3, B: 0x87, A: 0xff}, + {R: 0x1f, G: 0xa3, B: 0x86, A: 0xff}, + {R: 0x20, G: 0xa4, B: 0x86, A: 0xff}, + {R: 0x20, G: 0xa5, B: 0x86, A: 0xff}, + {R: 0x21, G: 0xa6, B: 0x85, A: 0xff}, + {R: 0x21, G: 0xa7, B: 0x85, A: 0xff}, + {R: 0x22, G: 0xa8, B: 0x84, A: 0xff}, + {R: 0x23, G: 0xa9, B: 0x83, A: 0xff}, + {R: 0x23, G: 0xaa, B: 0x83, A: 0xff}, + {R: 0x24, G: 0xab, B: 0x82, A: 0xff}, + {R: 0x25, G: 0xac, B: 0x82, A: 0xff}, + {R: 0x26, G: 0xad, B: 0x81, A: 0xff}, + {R: 0x27, G: 0xae, B: 0x81, A: 0xff}, + {R: 0x28, G: 0xaf, B: 0x80, A: 0xff}, + {R: 0x29, G: 0xaf, B: 0x7f, A: 0xff}, + {R: 0x2a, G: 0xb0, B: 0x7f, A: 0xff}, + {R: 0x2b, G: 0xb1, B: 0x7e, A: 0xff}, + {R: 0x2c, G: 0xb2, B: 0x7d, A: 0xff}, + {R: 0x2e, G: 0xb3, B: 0x7c, A: 0xff}, + {R: 0x2f, G: 0xb4, B: 0x7c, A: 0xff}, + {R: 0x30, G: 0xb5, B: 0x7b, A: 0xff}, + {R: 0x32, G: 0xb6, B: 0x7a, A: 0xff}, + {R: 0x33, G: 0xb7, B: 0x79, A: 0xff}, + {R: 0x35, G: 0xb7, B: 0x79, A: 0xff}, + {R: 0x36, G: 0xb8, B: 0x78, A: 0xff}, + {R: 0x38, G: 0xb9, B: 0x77, A: 0xff}, + {R: 0x39, G: 0xba, B: 0x76, A: 0xff}, + {R: 0x3b, G: 0xbb, B: 0x75, A: 0xff}, + {R: 0x3d, G: 0xbc, B: 0x74, A: 0xff}, + {R: 0x3e, G: 0xbd, B: 0x73, A: 0xff}, + {R: 0x40, G: 0xbe, B: 0x72, A: 0xff}, + {R: 0x42, G: 0xbe, B: 0x71, A: 0xff}, + {R: 0x44, G: 0xbf, B: 0x70, A: 0xff}, + {R: 0x46, G: 0xc0, B: 0x6f, A: 0xff}, + {R: 0x48, G: 0xc1, B: 0x6e, A: 0xff}, + {R: 0x49, G: 0xc2, B: 0x6d, A: 0xff}, + {R: 0x4b, G: 0xc2, B: 0x6c, A: 0xff}, + {R: 0x4d, G: 0xc3, B: 0x6b, A: 0xff}, + {R: 0x4f, G: 0xc4, B: 0x6a, A: 0xff}, + {R: 0x51, G: 0xc5, B: 0x69, A: 0xff}, + {R: 0x53, G: 0xc6, B: 0x68, A: 0xff}, + {R: 0x55, G: 0xc6, B: 0x66, A: 0xff}, + {R: 0x58, G: 0xc7, B: 0x65, A: 0xff}, + {R: 0x5a, G: 0xc8, B: 0x64, A: 0xff}, + {R: 0x5c, G: 0xc9, B: 0x63, A: 0xff}, + {R: 0x5e, G: 0xc9, B: 0x62, A: 0xff}, + {R: 0x60, G: 0xca, B: 0x60, A: 0xff}, + {R: 0x62, G: 0xcb, B: 0x5f, A: 0xff}, + {R: 0x65, G: 0xcc, B: 0x5e, A: 0xff}, + {R: 0x67, G: 0xcc, B: 0x5c, A: 0xff}, + {R: 0x69, G: 0xcd, B: 0x5b, A: 0xff}, + {R: 0x6c, G: 0xce, B: 0x5a, A: 0xff}, + {R: 0x6e, G: 0xce, B: 0x58, A: 0xff}, + {R: 0x70, G: 0xcf, B: 0x57, A: 0xff}, + {R: 0x73, G: 0xd0, B: 0x55, A: 0xff}, + {R: 0x75, G: 0xd0, B: 0x54, A: 0xff}, + {R: 0x77, G: 0xd1, B: 0x52, A: 0xff}, + {R: 0x7a, G: 0xd2, B: 0x51, A: 0xff}, + {R: 0x7c, G: 0xd2, B: 0x4f, A: 0xff}, + {R: 0x7f, G: 0xd3, B: 0x4e, A: 0xff}, + {R: 0x81, G: 0xd4, B: 0x4c, A: 0xff}, + {R: 0x84, G: 0xd4, B: 0x4b, A: 0xff}, + {R: 0x86, G: 0xd5, B: 0x49, A: 0xff}, + {R: 0x89, G: 0xd5, B: 0x48, A: 0xff}, + {R: 0x8b, G: 0xd6, B: 0x46, A: 0xff}, + {R: 0x8e, G: 0xd7, B: 0x44, A: 0xff}, + {R: 0x90, G: 0xd7, B: 0x43, A: 0xff}, + {R: 0x93, G: 0xd8, B: 0x41, A: 0xff}, + {R: 0x95, G: 0xd8, B: 0x3f, A: 0xff}, + {R: 0x98, G: 0xd9, B: 0x3e, A: 0xff}, + {R: 0x9b, G: 0xd9, B: 0x3c, A: 0xff}, + {R: 0x9d, G: 0xda, B: 0x3a, A: 0xff}, + {R: 0xa0, G: 0xda, B: 0x39, A: 0xff}, + {R: 0xa3, G: 0xdb, B: 0x37, A: 0xff}, + {R: 0xa5, G: 0xdb, B: 0x35, A: 0xff}, + {R: 0xa8, G: 0xdc, B: 0x33, A: 0xff}, + {R: 0xab, G: 0xdc, B: 0x32, A: 0xff}, + {R: 0xad, G: 0xdd, B: 0x30, A: 0xff}, + {R: 0xb0, G: 0xdd, B: 0x2e, A: 0xff}, + {R: 0xb3, G: 0xdd, B: 0x2d, A: 0xff}, + {R: 0xb5, G: 0xde, B: 0x2b, A: 0xff}, + {R: 0xb8, G: 0xde, B: 0x29, A: 0xff}, + {R: 0xbb, G: 0xdf, B: 0x27, A: 0xff}, + {R: 0xbd, G: 0xdf, B: 0x26, A: 0xff}, + {R: 0xc0, G: 0xdf, B: 0x24, A: 0xff}, + {R: 0xc3, G: 0xe0, B: 0x23, A: 0xff}, + {R: 0xc5, G: 0xe0, B: 0x21, A: 0xff}, + {R: 0xc8, G: 0xe1, B: 0x20, A: 0xff}, + {R: 0xcb, G: 0xe1, B: 0x1e, A: 0xff}, + {R: 0xcd, G: 0xe1, B: 0x1d, A: 0xff}, + {R: 0xd0, G: 0xe2, B: 0x1c, A: 0xff}, + {R: 0xd3, G: 0xe2, B: 0x1b, A: 0xff}, + {R: 0xd5, G: 0xe2, B: 0x1a, A: 0xff}, + {R: 0xd8, G: 0xe3, B: 0x19, A: 0xff}, + {R: 0xdb, G: 0xe3, B: 0x18, A: 0xff}, + {R: 0xdd, G: 0xe3, B: 0x18, A: 0xff}, + {R: 0xe0, G: 0xe4, B: 0x18, A: 0xff}, + {R: 0xe2, G: 0xe4, B: 0x18, A: 0xff}, + {R: 0xe5, G: 0xe4, B: 0x18, A: 0xff}, + {R: 0xe8, G: 0xe5, B: 0x19, A: 0xff}, + {R: 0xea, G: 0xe5, B: 0x19, A: 0xff}, + {R: 0xed, G: 0xe5, B: 0x1a, A: 0xff}, + {R: 0xef, G: 0xe6, B: 0x1b, A: 0xff}, + {R: 0xf2, G: 0xe6, B: 0x1c, A: 0xff}, + {R: 0xf4, G: 0xe6, B: 0x1e, A: 0xff}, + {R: 0xf7, G: 0xe6, B: 0x1f, A: 0xff}, + {R: 0xf9, G: 0xe7, B: 0x21, A: 0xff}, + {R: 0xfb, G: 0xe7, B: 0x23, A: 0xff}, + {R: 0xfe, G: 0xe7, B: 0x24, A: 0xff}, } // Viridis creates a color map provider. diff --git a/xaxis.go b/xaxis.go index 4fcb569..06086b1 100644 --- a/xaxis.go +++ b/xaxis.go @@ -110,9 +110,9 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic break } - left = MinInt(left, ltx) - right = MaxInt(right, rtx) - bottom = MaxInt(bottom, ty) + left = Min(left, ltx) + right = Max(right, rtx) + bottom = Max(bottom, ty) } 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) } Draw.Text(r, t.Label, tx, ty, tickWithAxisStyle) - maxTextHeight = MaxInt(maxTextHeight, tb.Height()) + maxTextHeight = Max(maxTextHeight, tb.Height()) break case TickPositionBetweenTicks: if index > 0 { @@ -180,7 +180,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 = MaxInt(maxTextHeight, ftb.Height()) + maxTextHeight = Max(maxTextHeight, ftb.Height()) } break } diff --git a/yaxis.go b/yaxis.go index 841fc86..84ba79d 100644 --- a/yaxis.go +++ b/yaxis.go @@ -110,18 +110,18 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic finalTextX = tx - tb.Width() } - maxTextHeight = MaxInt(tb.Height(), maxTextHeight) + maxTextHeight = Max(tb.Height(), maxTextHeight) if ya.AxisType == YAxisPrimary { minx = canvasBox.Right - maxx = MaxInt(maxx, tx+tb.Width()) + maxx = Max(maxx, tx+tb.Width()) } else if ya.AxisType == YAxisSecondary { - minx = MinInt(minx, finalTextX) - maxx = MaxInt(maxx, tx) + minx = Min(minx, finalTextX) + maxx = Max(maxx, tx) } - miny = MinInt(miny, ly-tbh2) - maxy = MaxInt(maxy, ly+tbh2) + miny = Min(miny, ly-tbh2) + maxy = Max(maxy, ly+tbh2) } if !ya.NameStyle.Hidden && len(ya.Name) > 0 {