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)
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

View file

@ -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]
}

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)
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 {

View file

@ -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)

44
box.go
View file

@ -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

View file

@ -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 {

View file

@ -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()
}

View file

@ -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(),
},
},
}

View file

@ -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.

View file

@ -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{

View file

@ -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{

View file

@ -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(),
},
},
}

View file

@ -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.

View file

@ -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++
}
}

View file

@ -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++ {

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.
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.

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
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
}

View file

@ -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++ {

View file

@ -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++ {

View file

@ -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 {

View file

@ -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

View file

@ -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.

130
seq.go
View file

@ -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...)}
}

View file

@ -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])

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 {
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-- {

View file

@ -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 {

View file

@ -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 {

View file

@ -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()

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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))
}

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"
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.

View file

@ -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
}

View file

@ -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 {