Compare commits

...

21 commits

Author SHA1 Message Date
63f0be79dd add web server sample 2024-10-27 22:48:01 -05:00
1f6d76e8b5 fix paths 2024-10-27 22:24:27 -05:00
cc8d36edd9 update url
Some checks failed
Continuous Integration / Tests (push) Has been cancelled
2024-10-27 21:52:38 -05:00
bd2b44aa38 Start copy from https://github.com/wcharczuk/go-chart 2024-10-27 16:38:55 -05:00
Will Charczuk
b46667ea80
Update README.md (#230) 2024-08-23 08:50:22 -07:00
Will Charczuk
218e744a87 narrow fix for CVE-2024-40060 2024-08-23 08:41:44 -07:00
dependabot[bot]
a334e8e43a
Bump golang.org/x/image from 0.12.0 to 0.18.0 (#225)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.12.0 to 0.18.0.
- [Commits](https://github.com/golang/image/compare/v0.12.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-26 12:37:07 -07:00
Will Charczuk
c9c9042154 add transparent 2023-11-04 15:44:23 -07:00
Will Charczuk
2f3402adfb getting ready to add extended colors but stopping at basic colors for now 2023-11-04 13:30:06 -07:00
Will Charczuk
dd823617d9 color from hex handles # now 2023-11-04 09:44:19 -07:00
Will Charczuk
7281bbdfa8 tweaking how svg renders itself 2023-10-03 08:43:15 -07:00
Will Charczuk
b3fc6cba9f make it not impossible to set a transparent background color 2023-10-03 08:23:15 -07:00
Will Charczuk
0d3588f719 updates to readme 2023-09-11 16:19:37 -04:00
Will Charczuk
d9bba03c8f status badge 2023-09-11 16:17:58 -04:00
Will Charczuk
c6416e0757 updates to ci, fixing busts in examples 2023-09-11 16:10:03 -04:00
Will Charczuk
f6d9d1edca fixing some issues with examples 2023-09-11 12:27:27 -04:00
Will Charczuk
2c2fb0651b removing go-sdk 2023-09-11 12:21:40 -04:00
Will Charczuk
2ff54048b8
Update README.md (#212) 2023-07-13 10:08:18 -07:00
guangwu
e781e0cd22
chore: unnecessary use of fmt.Sprintf (#211) 2023-07-10 08:53:10 -07:00
Thomas Lambert
54fc699377
fix(bar_charts): use YAxis.Render (#209) 2023-06-12 16:04:52 +02:00
twessling-icas
1ccfbb0172
add logarithmic axes support, with tests. Supports positive Y-values only. (#141)
Co-authored-by: Ton Wessling <twessling@ebay.com>
2023-05-22 08:37:57 -07:00
107 changed files with 1139 additions and 431 deletions

View file

@ -1,14 +0,0 @@
version: 2
jobs:
build:
working_directory: /go/src/github.com/wcharczuk/go-chart
docker:
- image: circleci/golang:1.15
steps:
- checkout
- run:
name: new-install
command: make new-install
- run:
name: Continuous Integration
command: make

33
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,33 @@
name: "Continuous Integration"
on:
workflow_dispatch:
push:
branches: [ main ]
paths: [ "*.go" ]
pull_request:
branches: [ main ]
paths: [ "*.go" ]
jobs:
ci:
name: "Tests"
runs-on: ubuntu-latest
env:
GOOS: "linux"
GOARCH: "amd64"
GO111MODULE: "on"
CGO_ENABLED: "0"
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.21
- name: Check out go-incr
uses: actions/checkout@v3
- name: Run all tests
run: go test ./...

1
.gitignore vendored
View file

@ -17,3 +17,4 @@
.vscode .vscode
.DS_Store .DS_Store
coverage.html coverage.html
.idea

View file

@ -1,6 +1,7 @@
MIT License MIT License
Copyright (c) 2016 William Charczuk. Copyright (c) 2016 William Charczuk.
Copyright (c) 2024 Zeni Kim.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -1,8 +1,9 @@
go-chart go-chart
======== ========
[![CircleCI](https://circleci.com/gh/wcharczuk/go-chart.svg?style=svg)](https://circleci.com/gh/wcharczuk/go-chart) [![Go Report Card](https://goreportcard.com/badge/github.com/wcharczuk/go-chart)](https://goreportcard.com/report/github.com/wcharczuk/go-chart)
Package `chart` is a very simple golang native charting library that supports timeseries and continuous line charts. This project starts from a full copy from [https://git.smarteching.com/zeni/go-chart](https://git.smarteching.com/zeni/go-chart). 28 Oct 2024.
-
Master should now be on the v3.x codebase, which overhauls the api significantly. Per usual, see `examples` for more information. Master should now be on the v3.x codebase, which overhauls the api significantly. Per usual, see `examples` for more information.
@ -11,7 +12,7 @@ Master should now be on the v3.x codebase, which overhauls the api significantly
To install `chart` run the following: To install `chart` run the following:
```bash ```bash
> go get -u github.com/wcharczuk/go-chart > go get git.smarteching.com/zeni/go-chart/v2@latest
``` ```
Most of the components are interchangeable so feel free to crib whatever you want. Most of the components are interchangeable so feel free to crib whatever you want.
@ -20,27 +21,27 @@ Most of the components are interchangeable so feel free to crib whatever you wan
Spark Lines: Spark Lines:
![](https://raw.githubusercontent.com/wcharczuk/go-chart/master/_images/tvix_ltm.png) ![](https://git.smarteching.com/zeni/go-chart/raw/branch/main/_images/tvix_ltm.png)
Single axis: Single axis:
![](https://raw.githubusercontent.com/wcharczuk/go-chart/master/_images/goog_ltm.png) ![](https://git.smarteching.com/zeni/go-chart/raw/branch/main/_images/goog_ltm.png)
Two axis: Two axis:
![](https://raw.githubusercontent.com/wcharczuk/go-chart/master/_images/two_axis.png) ![](https://git.smarteching.com/zeni/go-chart/raw/branch/main/_images/two_axis.png)
# Other Chart Types # Other Chart Types
Pie Chart: Pie Chart:
![](https://raw.githubusercontent.com/wcharczuk/go-chart/master/_images/pie_chart.png) ![](https://git.smarteching.com/zeni/go-chart/raw/branch/main/_images/pie_chart.png)
The code for this chart can be found in `examples/pie_chart/main.go`. The code for this chart can be found in `examples/pie_chart/main.go`.
Stacked Bar: Stacked Bar:
![](https://raw.githubusercontent.com/wcharczuk/go-chart/master/_images/stacked_bar.png) ![](https://git.smarteching.com/zeni/go-chart/raw/branch/main/_images/stacked_bar.png)
The code for this chart can be found in `examples/stacked_bar/main.go`. The code for this chart can be found in `examples/stacked_bar/main.go`.
@ -48,6 +49,8 @@ The code for this chart can be found in `examples/stacked_bar/main.go`.
Actual chart configurations and examples can be found in the `./examples/` directory. They are simple CLI programs that write to `output.png` (they are also updated with `go generate`. Actual chart configurations and examples can be found in the `./examples/` directory. They are simple CLI programs that write to `output.png` (they are also updated with `go generate`.
If folder ends in "web", has web servers, so start them with `go run main.go` then access `http://localhost:8080` to see the output.
# Usage # Usage
Everything starts with the `chart.Chart` object. The bare minimum to draw a chart would be the following: Everything starts with the `chart.Chart` object. The bare minimum to draw a chart would be the following:
@ -58,7 +61,7 @@ import (
... ...
"bytes" "bytes"
... ...
"github.com/wcharczuk/go-chart" //exposes "chart" "git.smarteching.com/zeni/go-chart/v2" //exposes "chart"
) )
graph := chart.Chart{ graph := chart.Chart{

147
_colors/colors_extended.txt Normal file
View file

@ -0,0 +1,147 @@
aliceblue #f0f8ff 240,248,255
antiquewhite #faebd7 250,235,215
aqua #00ffff 0,255,255
aquamarine #7fffd4 127,255,212
azure #f0ffff 240,255,255
beige #f5f5dc 245,245,220
bisque #ffe4c4 255,228,196
black #000000 0,0,0
blanchedalmond #ffebcd 255,235,205
blue #0000ff 0,0,255
blueviolet #8a2be2 138,43,226
brown #a52a2a 165,42,42
burlywood #deb887 222,184,135
cadetblue #5f9ea0 95,158,160
chartreuse #7fff00 127,255,0
chocolate #d2691e 210,105,30
coral #ff7f50 255,127,80
cornflowerblue #6495ed 100,149,237
cornsilk #fff8dc 255,248,220
crimson #dc143c 220,20,60
cyan #00ffff 0,255,255
darkblue #00008b 0,0,139
darkcyan #008b8b 0,139,139
darkgoldenrod #b8860b 184,134,11
darkgray #a9a9a9 169,169,169
darkgreen #006400 0,100,0
darkgrey #a9a9a9 169,169,169
darkkhaki #bdb76b 189,183,107
darkmagenta #8b008b 139,0,139
darkolivegreen #556b2f 85,107,47
darkorange #ff8c00 255,140,0
darkorchid #9932cc 153,50,204
darkred #8b0000 139,0,0
darksalmon #e9967a 233,150,122
darkseagreen #8fbc8f 143,188,143
darkslateblue #483d8b 72,61,139
darkslategray #2f4f4f 47,79,79
darkslategrey #2f4f4f 47,79,79
darkturquoise #00ced1 0,206,209
darkviolet #9400d3 148,0,211
deeppink #ff1493 255,20,147
deepskyblue #00bfff 0,191,255
dimgray #696969 105,105,105
dimgrey #696969 105,105,105
dodgerblue #1e90ff 30,144,255
firebrick #b22222 178,34,34
floralwhite #fffaf0 255,250,240
forestgreen #228b22 34,139,34
fuchsia #ff00ff 255,0,255
gainsboro #dcdcdc 220,220,220
ghostwhite #f8f8ff 248,248,255
gold #ffd700 255,215,0
goldenrod #daa520 218,165,32
gray #808080 128,128,128
green #008000 0,128,0
greenyellow #adff2f 173,255,47
grey #808080 128,128,128
honeydew #f0fff0 240,255,240
hotpink #ff69b4 255,105,180
indianred #cd5c5c 205,92,92
indigo #4b0082 75,0,130
ivory #fffff0 255,255,240
khaki #f0e68c 240,230,140
lavender #e6e6fa 230,230,250
lavenderblush #fff0f5 255,240,245
lawngreen #7cfc00 124,252,0
lemonchiffon #fffacd 255,250,205
lightblue #add8e6 173,216,230
lightcoral #f08080 240,128,128
lightcyan #e0ffff 224,255,255
lightgoldenrodyellow #fafad2 250,250,210
lightgray #d3d3d3 211,211,211
lightgreen #90ee90 144,238,144
lightgrey #d3d3d3 211,211,211
lightpink #ffb6c1 255,182,193
lightsalmon #ffa07a 255,160,122
lightseagreen #20b2aa 32,178,170
lightskyblue #87cefa 135,206,250
lightslategray #778899 119,136,153
lightslategrey #778899 119,136,153
lightsteelblue #b0c4de 176,196,222
lightyellow #ffffe0 255,255,224
lime #00ff00 0,255,0
limegreen #32cd32 50,205,50
linen #faf0e6 250,240,230
magenta #ff00ff 255,0,255
maroon #800000 128,0,0
mediumaquamarine #66cdaa 102,205,170
mediumblue #0000cd 0,0,205
mediumorchid #ba55d3 186,85,211
mediumpurple #9370db 147,112,219
mediumseagreen #3cb371 60,179,113
mediumslateblue #7b68ee 123,104,238
mediumspringgreen #00fa9a 0,250,154
mediumturquoise #48d1cc 72,209,204
mediumvioletred #c71585 199,21,133
midnightblue #191970 25,25,112
mintcream #f5fffa 245,255,250
mistyrose #ffe4e1 255,228,225
moccasin #ffe4b5 255,228,181
navajowhite #ffdead 255,222,173
navy #000080 0,0,128
oldlace #fdf5e6 253,245,230
olive #808000 128,128,0
olivedrab #6b8e23 107,142,35
orange #ffa500 255,165,0
orangered #ff4500 255,69,0
orchid #da70d6 218,112,214
palegoldenrod #eee8aa 238,232,170
palegreen #98fb98 152,251,152
paleturquoise #afeeee 175,238,238
palevioletred #db7093 219,112,147
papayawhip #ffefd5 255,239,213
peachpuff #ffdab9 255,218,185
peru #cd853f 205,133,63
pink #ffc0cb 255,192,203
plum #dda0dd 221,160,221
powderblue #b0e0e6 176,224,230
purple #800080 128,0,128
red #ff0000 255,0,0
rosybrown #bc8f8f 188,143,143
royalblue #4169e1 65,105,225
saddlebrown #8b4513 139,69,19
salmon #fa8072 250,128,114
sandybrown #f4a460 244,164,96
seagreen #2e8b57 46,139,87
seashell #fff5ee 255,245,238
sienna #a0522d 160,82,45
silver #c0c0c0 192,192,192
skyblue #87ceeb 135,206,235
slateblue #6a5acd 106,90,205
slategray #708090 112,128,144
slategrey #708090 112,128,144
snow #fffafa 255,250,250
springgreen #00ff7f 0,255,127
steelblue #4682b4 70,130,180
tan #d2b48c 210,180,140
teal #008080 0,128,128
thistle #d8bfd8 216,191,216
tomato #ff6347 255,99,71
turquoise #40e0d0 64,224,208
violet #ee82ee 238,130,238
wheat #f5deb3 245,222,179
white #ffffff 255,255,255
whitesmoke #f5f5f5 245,245,245
yellow #ffff00 255,255,0
yellowgreen #9acd32 154,205,50

View file

@ -4,8 +4,8 @@ import (
"image/color" "image/color"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestAnnotationSeriesMeasure(t *testing.T) { func TestAnnotationSeriesMeasure(t *testing.T) {

View file

@ -264,32 +264,7 @@ func (bc BarChart) drawXAxis(r Renderer, canvasBox Box) {
func (bc BarChart) drawYAxis(r Renderer, canvasBox Box, yr Range, ticks []Tick) { func (bc BarChart) drawYAxis(r Renderer, canvasBox Box, yr Range, ticks []Tick) {
if !bc.YAxis.Style.Hidden { if !bc.YAxis.Style.Hidden {
axisStyle := bc.YAxis.Style.InheritFrom(bc.styleDefaultsAxes()) bc.YAxis.Render(r, canvasBox, yr, bc.styleDefaultsAxes(), ticks)
axisStyle.WriteToRenderer(r)
r.MoveTo(canvasBox.Right, canvasBox.Top)
r.LineTo(canvasBox.Right, canvasBox.Bottom)
r.Stroke()
r.MoveTo(canvasBox.Right, canvasBox.Bottom)
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
r.Stroke()
var ty int
var tb Box
for _, t := range ticks {
ty = canvasBox.Bottom - yr.Translate(t.Value)
axisStyle.GetStrokeOptions().WriteToRenderer(r)
r.MoveTo(canvasBox.Right, ty)
r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, ty)
r.Stroke()
axisStyle.GetTextOptions().WriteToRenderer(r)
tb = r.MeasureText(t.Label)
Draw.Text(r, t.Label, canvasBox.Right+DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle)
}
} }
} }

View file

@ -5,7 +5,7 @@ import (
"math" "math"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestBarChartRender(t *testing.T) { func TestBarChartRender(t *testing.T) {

View file

@ -5,7 +5,7 @@ import (
"math" "math"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestBollingerBandSeries(t *testing.T) { func TestBollingerBandSeries(t *testing.T) {

16
box.go
View file

@ -254,6 +254,22 @@ func (b Box) OuterConstrain(bounds, other Box) Box {
return newBox return newBox
} }
func (b Box) Validate() error {
if b.Left < 0 {
return fmt.Errorf("invalid left; must be >= 0")
}
if b.Right < 0 {
return fmt.Errorf("invalid right; must be > 0")
}
if b.Top < 0 {
return fmt.Errorf("invalid top; must be > 0")
}
if b.Bottom < 0 {
return fmt.Errorf("invalid bottom; must be > 0")
}
return nil
}
// BoxCorners is a box with independent corners. // BoxCorners is a box with independent corners.
type BoxCorners struct { type BoxCorners struct {
TopLeft, TopRight, BottomRight, BottomLeft Point TopLeft, TopRight, BottomRight, BottomLeft Point

View file

@ -4,7 +4,7 @@ import (
"math" "math"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestBoxClone(t *testing.T) { func TestBoxClone(t *testing.T) {

View file

@ -8,8 +8,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestChartGetDPI(t *testing.T) { func TestChartGetDPI(t *testing.T) {
@ -573,3 +573,22 @@ func TestChartE2ELineWithFill(t *testing.T) {
testutil.AssertEqual(t, defaultSeriesColor, at(i, 0, 49)) testutil.AssertEqual(t, defaultSeriesColor, at(i, 0, 49))
testutil.AssertEqual(t, defaultSeriesColor, at(i, 49, 0)) testutil.AssertEqual(t, defaultSeriesColor, at(i, 49, 0))
} }
func Test_Chart_cve(t *testing.T) {
poc := StackedBarChart{
Title: "poc",
Bars: []StackedBar{
{
Name: "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
Values: []Value{
{Value: 1, Label: "infinite"},
{Value: 1, Label: "loop"},
},
},
},
}
var imgContent bytes.Buffer
err := poc.Render(PNG, &imgContent)
testutil.AssertNotNil(t, err)
}

View file

@ -7,7 +7,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
var ( var (

View file

@ -1,6 +1,6 @@
package chart package chart
import "github.com/wcharczuk/go-chart/v2/drawing" import "git.smarteching.com/zeni/go-chart/v2/drawing"
var ( var (
// ColorWhite is white. // ColorWhite is white.

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestConcatSeries(t *testing.T) { func TestConcatSeries(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestRangeTranslate(t *testing.T) { func TestRangeTranslate(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestContinuousSeries(t *testing.T) { func TestContinuousSeries(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestDonutChart(t *testing.T) { func TestDonutChart(t *testing.T) {

View file

@ -2,27 +2,46 @@ package drawing
import ( import (
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings"
) )
// Basic Colors from:
// https://www.w3.org/wiki/CSS/Properties/color/keywords
var ( var (
// ColorTransparent is a fully transparent color. // ColorTransparent is a fully transparent color.
ColorTransparent = Color{} ColorTransparent = Color{R: 255, G: 255, B: 255, A: 0}
// ColorWhite is white. // ColorWhite is white.
ColorWhite = Color{R: 255, G: 255, B: 255, A: 255} ColorWhite = Color{R: 255, G: 255, B: 255, A: 255}
// ColorBlack is black. // ColorBlack is black.
ColorBlack = Color{R: 0, G: 0, B: 0, A: 255} ColorBlack = Color{R: 0, G: 0, B: 0, A: 255}
// ColorRed is red. // ColorRed is red.
ColorRed = Color{R: 255, G: 0, B: 0, A: 255} ColorRed = Color{R: 255, G: 0, B: 0, A: 255}
// ColorGreen is green. // ColorGreen is green.
ColorGreen = Color{R: 0, G: 255, B: 0, A: 255} ColorGreen = Color{R: 0, G: 128, B: 0, A: 255}
// ColorBlue is blue. // ColorBlue is blue.
ColorBlue = Color{R: 0, G: 0, B: 255, A: 255} ColorBlue = Color{R: 0, G: 0, B: 255, A: 255}
// ColorSilver is a known color.
ColorSilver = Color{R: 192, G: 192, B: 192, A: 255}
// ColorMaroon is a known color.
ColorMaroon = Color{R: 128, G: 0, B: 0, A: 255}
// ColorPurple is a known color.
ColorPurple = Color{R: 128, G: 0, B: 128, A: 255}
// ColorFuchsia is a known color.
ColorFuchsia = Color{R: 255, G: 0, B: 255, A: 255}
// ColorLime is a known color.
ColorLime = Color{R: 0, G: 255, B: 0, A: 255}
// ColorOlive is a known color.
ColorOlive = Color{R: 128, G: 128, B: 0, A: 255}
// ColorYellow is a known color.
ColorYellow = Color{R: 255, G: 255, B: 0, A: 255}
// ColorNavy is a known color.
ColorNavy = Color{R: 0, G: 0, B: 128, A: 255}
// ColorTeal is a known color.
ColorTeal = Color{R: 0, G: 128, B: 128, A: 255}
// ColorAqua is a known color.
ColorAqua = Color{R: 0, G: 255, B: 255, A: 255}
) )
func parseHex(hex string) uint8 { func parseHex(hex string) uint8 {
@ -30,8 +49,97 @@ func parseHex(hex string) uint8 {
return uint8(v) return uint8(v)
} }
// ParseColor parses a color from a string.
func ParseColor(rawColor string) Color {
if strings.HasPrefix(rawColor, "rgba") {
return ColorFromRGBA(rawColor)
}
if strings.HasPrefix(rawColor, "rgb") {
return ColorFromRGB(rawColor)
}
if strings.HasPrefix(rawColor, "#") {
return ColorFromHex(rawColor)
}
return ColorFromKnown(rawColor)
}
var rgbaexpr = regexp.MustCompile(`rgba\((?P<R>.+),(?P<G>.+),(?P<B>.+),(?P<A>.+)\)`)
// ColorFromRGBA returns a color from an `rgba()` css function.
func ColorFromRGBA(rgba string) (output Color) {
values := rgbaexpr.FindStringSubmatch(rgba)
for i, name := range rgbaexpr.SubexpNames() {
if i == 0 {
continue
}
if i >= len(values) {
break
}
switch name {
case "R":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseInt(value, 10, 16)
output.R = uint8(parsed)
case "G":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseInt(value, 10, 16)
output.G = uint8(parsed)
case "B":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseInt(value, 10, 16)
output.B = uint8(parsed)
case "A":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseFloat(value, 32)
if parsed > 1 {
parsed = 1
} else if parsed < 0 {
parsed = 0
}
output.A = uint8(parsed * 255)
}
}
return
}
var rgbexpr = regexp.MustCompile(`rgb\((?P<R>.+),(?P<G>.+),(?P<B>.+)\)`)
// ColorFromRGB returns a color from an `rgb()` css function.
func ColorFromRGB(rgb string) (output Color) {
output.A = 255
values := rgbexpr.FindStringSubmatch(rgb)
for i, name := range rgbaexpr.SubexpNames() {
if i == 0 {
continue
}
if i >= len(values) {
break
}
switch name {
case "R":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseInt(value, 10, 16)
output.R = uint8(parsed)
case "G":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseInt(value, 10, 16)
output.G = uint8(parsed)
case "B":
value := strings.TrimSpace(values[i])
parsed, _ := strconv.ParseInt(value, 10, 16)
output.B = uint8(parsed)
}
}
return
}
// ColorFromHex returns a color from a css hex code. // ColorFromHex returns a color from a css hex code.
//
// NOTE: it will trim a leading '#' character if present.
func ColorFromHex(hex string) Color { func ColorFromHex(hex string) Color {
if strings.HasPrefix(hex, "#") {
hex = strings.TrimPrefix(hex, "#")
}
var c Color var c Color
if len(hex) == 3 { if len(hex) == 3 {
c.R = parseHex(string(hex[0])) * 0x11 c.R = parseHex(string(hex[0])) * 0x11
@ -46,6 +154,46 @@ func ColorFromHex(hex string) Color {
return c return c
} }
// ColorFromKnown returns an internal color from a known (basic) color name.
func ColorFromKnown(known string) Color {
switch strings.ToLower(known) {
case "transparent":
return ColorTransparent
case "white":
return ColorWhite
case "black":
return ColorBlack
case "red":
return ColorRed
case "blue":
return ColorBlue
case "green":
return ColorGreen
case "silver":
return ColorSilver
case "maroon":
return ColorMaroon
case "purple":
return ColorPurple
case "fuchsia":
return ColorFuchsia
case "lime":
return ColorLime
case "olive":
return ColorOlive
case "yellow":
return ColorYellow
case "navy":
return ColorNavy
case "teal":
return ColorTeal
case "aqua":
return ColorAqua
default:
return Color{}
}
}
// ColorFromAlphaMixedRGBA returns the system alpha mixed rgba values. // ColorFromAlphaMixedRGBA returns the system alpha mixed rgba values.
func ColorFromAlphaMixedRGBA(r, g, b, a uint32) Color { func ColorFromAlphaMixedRGBA(r, g, b, a uint32) Color {
fa := float64(a) / 255.0 fa := float64(a) / 255.0

View file

@ -1,16 +1,15 @@
package drawing package drawing
import ( import (
"fmt"
"testing" "testing"
"image/color" "image/color"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestColorFromHex(t *testing.T) { func TestColorFromHex(t *testing.T) {
// replaced new assertions helper
white := ColorFromHex("FFFFFF") white := ColorFromHex("FFFFFF")
testutil.AssertEqual(t, ColorWhite, white) testutil.AssertEqual(t, ColorWhite, white)
@ -29,11 +28,11 @@ func TestColorFromHex(t *testing.T) {
shortRed := ColorFromHex("F00") shortRed := ColorFromHex("F00")
testutil.AssertEqual(t, ColorRed, shortRed) testutil.AssertEqual(t, ColorRed, shortRed)
green := ColorFromHex("00FF00") green := ColorFromHex("008000")
testutil.AssertEqual(t, ColorGreen, green) testutil.AssertEqual(t, ColorGreen, green)
shortGreen := ColorFromHex("0F0") // shortGreen := ColorFromHex("0F0")
testutil.AssertEqual(t, ColorGreen, shortGreen) // testutil.AssertEqual(t, ColorGreen, shortGreen)
blue := ColorFromHex("0000FF") blue := ColorFromHex("0000FF")
testutil.AssertEqual(t, ColorBlue, blue) testutil.AssertEqual(t, ColorBlue, blue)
@ -42,12 +41,74 @@ func TestColorFromHex(t *testing.T) {
testutil.AssertEqual(t, ColorBlue, shortBlue) testutil.AssertEqual(t, ColorBlue, shortBlue)
} }
func TestColorFromAlphaMixedRGBA(t *testing.T) { func TestColorFromHex_handlesHash(t *testing.T) {
// replaced new assertions helper withHash := ColorFromHex("#FF0000")
testutil.AssertEqual(t, ColorRed, withHash)
withoutHash := ColorFromHex("#FF0000")
testutil.AssertEqual(t, ColorRed, withoutHash)
}
func TestColorFromAlphaMixedRGBA(t *testing.T) {
black := ColorFromAlphaMixedRGBA(color.Black.RGBA()) black := ColorFromAlphaMixedRGBA(color.Black.RGBA())
testutil.AssertTrue(t, black.Equals(ColorBlack), black.String()) testutil.AssertTrue(t, black.Equals(ColorBlack), black.String())
white := ColorFromAlphaMixedRGBA(color.White.RGBA()) white := ColorFromAlphaMixedRGBA(color.White.RGBA())
testutil.AssertTrue(t, white.Equals(ColorWhite), white.String()) testutil.AssertTrue(t, white.Equals(ColorWhite), white.String())
} }
func Test_ColorFromRGBA(t *testing.T) {
value := "rgba(192, 192, 192, 1.0)"
parsed := ColorFromRGBA(value)
testutil.AssertEqual(t, ColorSilver, parsed)
value = "rgba(192,192,192,1.0)"
parsed = ColorFromRGBA(value)
testutil.AssertEqual(t, ColorSilver, parsed)
value = "rgba(192,192,192,1.5)"
parsed = ColorFromRGBA(value)
testutil.AssertEqual(t, ColorSilver, parsed)
}
func TestParseColor(t *testing.T) {
testCases := [...]struct {
Input string
Expected Color
}{
{"", Color{}},
{"white", ColorWhite},
{"WHITE", ColorWhite}, // caps!
{"black", ColorBlack},
{"red", ColorRed},
{"green", ColorGreen},
{"blue", ColorBlue},
{"silver", ColorSilver},
{"maroon", ColorMaroon},
{"purple", ColorPurple},
{"fuchsia", ColorFuchsia},
{"lime", ColorLime},
{"olive", ColorOlive},
{"yellow", ColorYellow},
{"navy", ColorNavy},
{"teal", ColorTeal},
{"aqua", ColorAqua},
{"rgba(192, 192, 192, 1.0)", ColorSilver},
{"rgba(192,192,192,1.0)", ColorSilver},
{"rgb(192, 192, 192)", ColorSilver},
{"rgb(192,192,192)", ColorSilver},
{"#FF0000", ColorRed},
{"#008000", ColorGreen},
{"#0000FF", ColorBlue},
{"#F00", ColorRed},
{"#080", Color{0, 136, 0, 255}},
{"#00F", ColorBlue},
}
for index, tc := range testCases {
actual := ParseColor(tc.Input)
testutil.AssertEqual(t, tc.Expected, actual, fmt.Sprintf("test case: %d -> %s", index, tc.Input))
}
}

View file

@ -3,7 +3,7 @@ package drawing
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
type point struct { type point struct {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
var ( var (

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
chart "github.com/wcharczuk/go-chart/v2" chart "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
chart "github.com/wcharczuk/go-chart/v2" chart "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -5,8 +5,8 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {

View file

@ -0,0 +1,55 @@
package main
//go:generate go run main.go
import (
"fmt"
"log"
"net/http"
"os"
"git.smarteching.com/zeni/go-chart/v2"
)
func drawChart(res http.ResponseWriter, req *http.Request) {
graph := chart.BarChart{
Title: "Test Bar Chart",
Background: chart.Style{
Padding: chart.Box{
Top: 40,
},
},
Height: 512,
BarWidth: 60,
Bars: []chart.Value{
{Value: 5.25, Label: "Blue"},
{Value: 4.88, Label: "Green"},
{Value: 4.74, Label: "Gray"},
{Value: 3.22, Label: "Orange"},
{Value: 3, Label: "Test"},
{Value: 2.27, Label: "??"},
{Value: 1, Label: "!!"},
},
}
res.Header().Set("Content-Type", "image/png")
err := graph.Render(chart.PNG, res)
if err != nil {
fmt.Printf("Error rendering chart: %v\n", err)
}
}
func port() string {
if len(os.Getenv("PORT")) > 0 {
return os.Getenv("PORT")
}
return "8080"
}
func main() {
listenPort := fmt.Sprintf(":%s", port())
fmt.Printf("Listening on %s\n", listenPort)
http.HandleFunc("/", drawChart)
log.Fatal(http.ListenAndServe(listenPort, nil))
}

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -8,7 +8,7 @@ import (
"os" "os"
"time" "time"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func random(min, max float64) float64 { func random(min, max float64) float64 {

View file

@ -5,7 +5,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
// Note: Additional examples on how to add Stylesheets are in the custom_stylesheets example // Note: Additional examples on how to add Stylesheets are in the custom_stylesheets example

View file

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,8 +5,8 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,8 +5,8 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
const style = "svg .background { fill: white; }" + const style = "svg .background { fill: white; }" +

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -3,8 +3,8 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {
@ -23,7 +23,7 @@ func main() {
stackedBarChart := chart.StackedBarChart{ stackedBarChart := chart.StackedBarChart{
Title: "Quarterly Sales", Title: "Quarterly Sales",
TitleStyle: chart.StyleShow(), TitleStyle: chart.Shown(),
Background: chart.Style{ Background: chart.Style{
Padding: chart.Box{ Padding: chart.Box{
Top: 75, Top: 75,
@ -31,8 +31,8 @@ func main() {
}, },
Width: 800, Width: 800,
Height: 600, Height: 600,
XAxis: chart.StyleShow(), XAxis: chart.Shown(),
YAxis: chart.StyleShow(), YAxis: chart.Shown(),
BarSpacing: 40, BarSpacing: 40,
IsHorizontal: true, IsHorizontal: true,
Bars: []chart.StackedBar{ Bars: []chart.StackedBar{

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
chart "github.com/wcharczuk/go-chart/v2" chart "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
chart "github.com/wcharczuk/go-chart/v2" chart "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -0,0 +1,41 @@
package main
//go:generate go run main.go
import (
"os"
"git.smarteching.com/zeni/go-chart/v2"
)
func main() {
/*
In this example we set the primary YAxis to have logarithmic range.
*/
graph := chart.Chart{
Background: chart.Style{
Padding: chart.Box{
Top: 20,
Left: 20,
},
},
Series: []chart.Series{
chart.ContinuousSeries{
Name: "A test series",
XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
YValues: []float64{1, 10, 100, 1000, 10000},
},
},
YAxis: chart.YAxis{
Style: chart.Shown(),
NameStyle: chart.Shown(),
Range: &chart.LogarithmicRange{},
},
}
f, _ := os.Create("output.png")
defer f.Close()
graph.Render(chart.PNG, f)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -0,0 +1,55 @@
package main
import (
"fmt"
"log"
"net/http"
"git.smarteching.com/zeni/go-chart/v2"
)
func drawChart(res http.ResponseWriter, req *http.Request) {
pie := chart.PieChart{
Width: 512,
Height: 512,
Values: []chart.Value{
{Value: 5, Label: "Blue"},
{Value: 5, Label: "Green"},
{Value: 4, Label: "Gray"},
{Value: 4, Label: "Orange"},
{Value: 3, Label: "Deep Blue"},
{Value: 3, Label: "??"},
{Value: 1, Label: "!!"},
},
}
res.Header().Set("Content-Type", "image/png")
err := pie.Render(chart.PNG, res)
if err != nil {
fmt.Printf("Error rendering pie chart: %v\n", err)
}
}
func drawChartRegression(res http.ResponseWriter, req *http.Request) {
pie := chart.PieChart{
Width: 512,
Height: 512,
Values: []chart.Value{
{Value: 5, Label: "Blue"},
{Value: 2, Label: "Two"},
{Value: 1, Label: "One"},
},
}
res.Header().Set("Content-Type", chart.ContentTypeSVG)
err := pie.Render(chart.SVG, res)
if err != nil {
fmt.Printf("Error rendering pie chart: %v\n", err)
}
}
func main() {
http.HandleFunc("/", drawChart)
http.HandleFunc("/reg", drawChartRegression)
log.Fatal(http.ListenAndServe(":8080", nil))
}

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
chart "github.com/wcharczuk/go-chart/v2" chart "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -9,7 +9,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -6,7 +6,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
var lock sync.Mutex var lock sync.Mutex

View file

@ -6,8 +6,8 @@ import (
_ "net/http/pprof" _ "net/http/pprof"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func drawChart(res http.ResponseWriter, req *http.Request) { func drawChart(res http.ResponseWriter, req *http.Request) {

View file

@ -5,7 +5,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -3,7 +3,7 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -3,8 +3,8 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {
@ -23,7 +23,7 @@ func main() {
stackedBarChart := chart.StackedBarChart{ stackedBarChart := chart.StackedBarChart{
Title: "Quarterly Sales", Title: "Quarterly Sales",
TitleStyle: chart.StyleShow(), TitleStyle: chart.Shown(),
Background: chart.Style{ Background: chart.Style{
Padding: chart.Box{ Padding: chart.Box{
Top: 100, Top: 100,
@ -31,8 +31,8 @@ func main() {
}, },
Width: 810, Width: 810,
Height: 500, Height: 500,
XAxis: chart.StyleShow(), XAxis: chart.Shown(),
YAxis: chart.StyleShow(), YAxis: chart.Shown(),
BarSpacing: 50, BarSpacing: 50,
Bars: []chart.StackedBar{ Bars: []chart.StackedBar{
{ {

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -6,8 +6,8 @@ import (
"os" "os"
"time" "time"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {

View file

@ -5,8 +5,8 @@ package main
import ( import (
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
func main() { func main() {

View file

@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
chart "github.com/wcharczuk/go-chart/v2" chart "git.smarteching.com/zeni/go-chart/v2"
) )
func drawChart(res http.ResponseWriter, req *http.Request) { func drawChart(res http.ResponseWriter, req *http.Request) {

View file

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -7,7 +7,7 @@ import (
"log" "log"
"os" "os"
"github.com/wcharczuk/go-chart/v2" "git.smarteching.com/zeni/go-chart/v2"
) )
func main() { func main() {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestFirstValueAnnotation(t *testing.T) { func TestFirstValueAnnotation(t *testing.T) {

View file

@ -3,8 +3,8 @@ package chart
import ( import (
"sync" "sync"
"git.smarteching.com/zeni/go-chart/v2/roboto"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2/roboto"
) )
var ( var (

6
go.mod
View file

@ -1,8 +1,8 @@
module github.com/wcharczuk/go-chart/v2 module git.smarteching.com/zeni/go-chart/v2
go 1.15 go 1.23.1
require ( require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 golang.org/x/image v0.21.0
) )

5
go.sum
View file

@ -1,5 +1,4 @@
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestGenerateGridLines(t *testing.T) { func TestGenerateGridLines(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestHistogramSeries(t *testing.T) { func TestHistogramSeries(t *testing.T) {

2
jet.go
View file

@ -1,6 +1,6 @@
package chart package chart
import "github.com/wcharczuk/go-chart/v2/drawing" import "git.smarteching.com/zeni/go-chart/v2/drawing"
// Jet is a color map provider based on matlab's jet color map. // Jet is a color map provider based on matlab's jet color map.
func Jet(v, vmin, vmax float64) drawing.Color { func Jet(v, vmin, vmax float64) drawing.Color {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestLastValueAnnotationSeries(t *testing.T) { func TestLastValueAnnotationSeries(t *testing.T) {

View file

@ -1,7 +1,7 @@
package chart package chart
import ( import (
"github.com/wcharczuk/go-chart/v2/drawing" "git.smarteching.com/zeni/go-chart/v2/drawing"
) )
// Legend returns a legend renderable function. // Legend returns a legend renderable function.

View file

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestLegend(t *testing.T) { func TestLegend(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestLinearRegressionSeries(t *testing.T) { func TestLinearRegressionSeries(t *testing.T) {

94
logarithmic_range.go Normal file
View file

@ -0,0 +1,94 @@
package chart
import (
"fmt"
"math"
)
// LogarithmicRange represents a boundary for a set of numbers.
type LogarithmicRange struct {
Min float64
Max float64
Domain int
Descending bool
}
// IsDescending returns if the range is descending.
func (r LogarithmicRange) IsDescending() bool {
return r.Descending
}
// IsZero returns if the LogarithmicRange has been set or not.
func (r LogarithmicRange) IsZero() bool {
return (r.Min == 0 || math.IsNaN(r.Min)) &&
(r.Max == 0 || math.IsNaN(r.Max)) &&
r.Domain == 0
}
// GetMin gets the min value for the continuous range.
func (r LogarithmicRange) GetMin() float64 {
return r.Min
}
// SetMin sets the min value for the continuous range.
func (r *LogarithmicRange) SetMin(min float64) {
r.Min = min
}
// GetMax returns the max value for the continuous range.
func (r LogarithmicRange) GetMax() float64 {
return r.Max
}
// SetMax sets the max value for the continuous range.
func (r *LogarithmicRange) SetMax(max float64) {
r.Max = max
}
// GetDelta returns the difference between the min and max value.
func (r LogarithmicRange) GetDelta() float64 {
return r.Max - r.Min
}
// GetDomain returns the range domain.
func (r LogarithmicRange) GetDomain() int {
return r.Domain
}
// SetDomain sets the range domain.
func (r *LogarithmicRange) SetDomain(domain int) {
r.Domain = domain
}
// String returns a simple string for the LogarithmicRange.
func (r LogarithmicRange) String() string {
return fmt.Sprintf("LogarithmicRange [%.2f,%.2f] => %d", r.Min, r.Max, r.Domain)
}
// Translate maps a given value into the LogarithmicRange space. Modified version from ContinuousRange.
func (r LogarithmicRange) Translate(value float64) int {
if value < 1 {
return 0
}
normalized := math.Max(value-r.Min, 1)
ratio := math.Log10(normalized) / math.Log10(r.GetDelta())
if r.IsDescending() {
return r.Domain - int(math.Ceil(ratio*float64(r.Domain)))
}
return int(math.Ceil(ratio * float64(r.Domain)))
}
// GetTicks calculates the needed ticks for the axis, in log scale. Only supports Y values > 0.
func (r LogarithmicRange) GetTicks(render Renderer, defaults Style, vf ValueFormatter) []Tick {
var ticks []Tick
exponentStart := int64(math.Max(0, math.Floor(math.Log10(r.Min)))) // one below min
exponentEnd := int64(math.Max(0, math.Ceil(math.Log10(r.Max)))) // one above max
for exp:=exponentStart; exp<=exponentEnd; exp++ {
tickVal := math.Pow(10, float64(exp))
ticks = append(ticks, Tick{Value: tickVal, Label: vf(tickVal)})
}
return ticks
}

43
logarithmic_range_test.go Normal file
View file

@ -0,0 +1,43 @@
package chart
import (
"testing"
"git.smarteching.com/zeni/go-chart/v2/testutil"
)
func TestLogRangeTranslate(t *testing.T) {
values := []float64{1, 10, 100, 1000, 10000, 100000, 1000000}
r := LogarithmicRange{Domain: 1000}
r.Min, r.Max = MinMax(values...)
testutil.AssertEqual(t, 0, r.Translate(0)) // goes to bottom
testutil.AssertEqual(t, 0, r.Translate(1)) // goes to bottom
testutil.AssertEqual(t, 160, r.Translate(10)) // roughly 1/6th of max
testutil.AssertEqual(t, 500, r.Translate(1000)) // roughly 1/2 of max (1.0e6 / 1.0e3)
testutil.AssertEqual(t, 1000, r.Translate(1000000)) // max value
}
func TestGetTicks(t *testing.T) {
values := []float64{35, 512, 1525122}
r := LogarithmicRange{Domain: 1000}
r.Min, r.Max = MinMax(values...)
ticks := r.GetTicks(nil, Style{}, FloatValueFormatter)
testutil.AssertEqual(t, 7, len(ticks))
testutil.AssertEqual(t, 10, ticks[0].Value)
testutil.AssertEqual(t, 100, ticks[1].Value)
testutil.AssertEqual(t, 10000000, ticks[6].Value)
}
func TestGetTicksFromHigh(t *testing.T) {
values := []float64{1412, 352144, 1525122} // min tick should be 1000
r := LogarithmicRange{}
r.Min, r.Max = MinMax(values...)
ticks := r.GetTicks(nil, Style{}, FloatValueFormatter)
testutil.AssertEqual(t, 5, len(ticks))
testutil.AssertEqual(t, float64(1000), ticks[0].Value)
testutil.AssertEqual(t, float64(10000), ticks[1].Value)
testutil.AssertEqual(t, float64(10000000), ticks[4].Value)
}

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
var ( var (

View file

@ -3,7 +3,7 @@ package matrix
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestNew(t *testing.T) { func TestNew(t *testing.T) {

View file

@ -3,7 +3,7 @@ package matrix
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestPoly(t *testing.T) { func TestPoly(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestPercentageDifferenceSeries(t *testing.T) { func TestPercentageDifferenceSeries(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestPieChart(t *testing.T) { func TestPieChart(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"math" "math"
"github.com/wcharczuk/go-chart/v2/matrix" "git.smarteching.com/zeni/go-chart/v2/matrix"
) )
// Interface Assertions. // Interface Assertions.

View file

@ -3,8 +3,8 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/matrix" "git.smarteching.com/zeni/go-chart/v2/matrix"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestPolynomialRegression(t *testing.T) { func TestPolynomialRegression(t *testing.T) {

View file

@ -6,8 +6,8 @@ import (
"io" "io"
"math" "math"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2/drawing"
) )
// PNG returns a new png/raster renderer. // PNG returns a new png/raster renderer.

View file

@ -3,8 +3,8 @@ package chart
import ( import (
"io" "io"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2/drawing"
) )
// Renderer represents the basic methods required to draw a chart. // Renderer represents the basic methods required to draw a chart.

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestSeqEach(t *testing.T) { func TestSeqEach(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
type mockValuesProvider struct { type mockValuesProvider struct {

View file

@ -118,12 +118,18 @@ func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error {
var canvasBox Box var canvasBox Box
if sbc.IsHorizontal { if sbc.IsHorizontal {
canvasBox = sbc.getHorizontalAdjustedCanvasBox(r, sbc.getDefaultCanvasBox()) canvasBox = sbc.getHorizontalAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
if err := canvasBox.Validate(); err != nil {
return fmt.Errorf("invalid canvas box: %w", err)
}
sbc.drawCanvas(r, canvasBox) sbc.drawCanvas(r, canvasBox)
sbc.drawHorizontalBars(r, canvasBox) sbc.drawHorizontalBars(r, canvasBox)
sbc.drawHorizontalXAxis(r, canvasBox) sbc.drawHorizontalXAxis(r, canvasBox)
sbc.drawHorizontalYAxis(r, canvasBox) sbc.drawHorizontalYAxis(r, canvasBox)
} else { } else {
canvasBox = sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox()) canvasBox = sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox())
if err := canvasBox.Validate(); err != nil {
return fmt.Errorf("invalid canvas box: %w", err)
}
sbc.drawCanvas(r, canvasBox) sbc.drawCanvas(r, canvasBox)
sbc.drawBars(r, canvasBox) sbc.drawBars(r, canvasBox)
sbc.drawXAxis(r, canvasBox) sbc.drawXAxis(r, canvasBox)

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestSplitCSV(t *testing.T) { func TestSplitCSV(t *testing.T) {

View file

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2/drawing"
) )
const ( const (

View file

@ -3,9 +3,9 @@ package chart
import ( import (
"testing" "testing"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"git.smarteching.com/zeni/go-chart/v2/testutil"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/testutil"
) )
func TestStyleIsZero(t *testing.T) { func TestStyleIsZero(t *testing.T) {

View file

@ -9,6 +9,7 @@ import (
// AssertNil asserts an argument is nil. // AssertNil asserts an argument is nil.
func AssertNil(t *testing.T, actual interface{}) { func AssertNil(t *testing.T, actual interface{}) {
t.Helper()
if !isNil(actual) { if !isNil(actual) {
t.Errorf("assertion failed; expected actual to be nil") t.Errorf("assertion failed; expected actual to be nil")
t.FailNow() t.FailNow()
@ -17,6 +18,7 @@ func AssertNil(t *testing.T, actual interface{}) {
// AssertNotNil asserts an argument is not nil. // AssertNotNil asserts an argument is not nil.
func AssertNotNil(t *testing.T, actual interface{}, message ...interface{}) { func AssertNotNil(t *testing.T, actual interface{}, message ...interface{}) {
t.Helper()
if isNil(actual) { if isNil(actual) {
t.Error("assertion failed; expected actual to not be nil") t.Error("assertion failed; expected actual to not be nil")
if len(message) > 0 { if len(message) > 0 {
@ -28,6 +30,7 @@ func AssertNotNil(t *testing.T, actual interface{}, message ...interface{}) {
// AssertEqual asserts two arguments are equal. // AssertEqual asserts two arguments are equal.
func AssertEqual(t *testing.T, expected, actual interface{}, message ...interface{}) { func AssertEqual(t *testing.T, expected, actual interface{}, message ...interface{}) {
t.Helper()
if !equal(expected, actual) { if !equal(expected, actual) {
t.Errorf("assertion failed; expected %v to equal %v", actual, expected) t.Errorf("assertion failed; expected %v to equal %v", actual, expected)
if len(message) > 0 { if len(message) > 0 {
@ -39,6 +42,7 @@ func AssertEqual(t *testing.T, expected, actual interface{}, message ...interfac
// AssertNotEqual asserts two arguments are not equal. // AssertNotEqual asserts two arguments are not equal.
func AssertNotEqual(t *testing.T, expected, actual interface{}, message ...interface{}) { func AssertNotEqual(t *testing.T, expected, actual interface{}, message ...interface{}) {
t.Helper()
if equal(expected, actual) { if equal(expected, actual) {
t.Errorf("assertion failed; expected %v to not equal %v", actual, expected) t.Errorf("assertion failed; expected %v to not equal %v", actual, expected)
if len(message) > 0 { if len(message) > 0 {
@ -50,16 +54,19 @@ func AssertNotEqual(t *testing.T, expected, actual interface{}, message ...inter
// AssertZero asserts an argument is zero. // AssertZero asserts an argument is zero.
func AssertZero(t *testing.T, actual interface{}, message ...interface{}) { func AssertZero(t *testing.T, actual interface{}, message ...interface{}) {
t.Helper()
AssertEqual(t, 0, actual) AssertEqual(t, 0, actual)
} }
// AssertNotZero asserts an argument is not zero. // AssertNotZero asserts an argument is not zero.
func AssertNotZero(t *testing.T, actual interface{}, message ...interface{}) { func AssertNotZero(t *testing.T, actual interface{}, message ...interface{}) {
t.Helper()
AssertNotEqual(t, 0, actual) AssertNotEqual(t, 0, actual)
} }
// AssertTrue asserts an argument is true. // AssertTrue asserts an argument is true.
func AssertTrue(t *testing.T, arg bool, message ...interface{}) { func AssertTrue(t *testing.T, arg bool, message ...interface{}) {
t.Helper()
if !arg { if !arg {
t.Errorf("assertion failed; expected actual to be true") t.Errorf("assertion failed; expected actual to be true")
if len(message) > 0 { if len(message) > 0 {
@ -71,6 +78,7 @@ func AssertTrue(t *testing.T, arg bool, message ...interface{}) {
// AssertFalse asserts an argument is false. // AssertFalse asserts an argument is false.
func AssertFalse(t *testing.T, arg bool, message ...interface{}) { func AssertFalse(t *testing.T, arg bool, message ...interface{}) {
t.Helper()
if arg { if arg {
t.Errorf("assertion failed; expected actual to be false") t.Errorf("assertion failed; expected actual to be false")
if len(message) > 0 { if len(message) > 0 {
@ -84,6 +92,7 @@ func AssertFalse(t *testing.T, arg bool, message ...interface{}) {
// //
// This delta will be determined absolute, and the delta should always be positive. // This delta will be determined absolute, and the delta should always be positive.
func AssertInDelta(t *testing.T, from, to, delta float64, message ...interface{}) { func AssertInDelta(t *testing.T, from, to, delta float64, message ...interface{}) {
t.Helper()
if diff := math.Abs(from - to); diff > delta { if diff := math.Abs(from - to); diff > delta {
t.Errorf("assertion failed; expected absolute difference of %f and %f to be %f", from, to, delta) t.Errorf("assertion failed; expected absolute difference of %f and %f to be %f", from, to, delta)
if len(message) > 0 { if len(message) > 0 {
@ -95,6 +104,7 @@ func AssertInDelta(t *testing.T, from, to, delta float64, message ...interface{}
// AssertEmpty asserts an argument is empty. // AssertEmpty asserts an argument is empty.
func AssertEmpty(t *testing.T, arg interface{}, message ...interface{}) { func AssertEmpty(t *testing.T, arg interface{}, message ...interface{}) {
t.Helper()
if getLength(arg) != 0 { if getLength(arg) != 0 {
t.Errorf("assertion failed; expected actual to be empty") t.Errorf("assertion failed; expected actual to be empty")
if len(message) > 0 { if len(message) > 0 {
@ -106,6 +116,7 @@ func AssertEmpty(t *testing.T, arg interface{}, message ...interface{}) {
// AssertNotEmpty asserts an argument is not empty. // AssertNotEmpty asserts an argument is not empty.
func AssertNotEmpty(t *testing.T, arg interface{}, message ...interface{}) { func AssertNotEmpty(t *testing.T, arg interface{}, message ...interface{}) {
t.Helper()
if getLength(arg) == 0 { if getLength(arg) == 0 {
t.Errorf("assertion failed; expected actual to not be empty") t.Errorf("assertion failed; expected actual to not be empty")
if len(message) > 0 { if len(message) > 0 {
@ -117,6 +128,7 @@ func AssertNotEmpty(t *testing.T, arg interface{}, message ...interface{}) {
// AssertLen asserts an argument has a given length. // AssertLen asserts an argument has a given length.
func AssertLen(t *testing.T, arg interface{}, length int, message ...interface{}) { func AssertLen(t *testing.T, arg interface{}, length int, message ...interface{}) {
t.Helper()
if getLength(arg) != length { if getLength(arg) != length {
t.Errorf("assertion failed; expected actual to have length %d", length) t.Errorf("assertion failed; expected actual to have length %d", length)
if len(message) > 0 { if len(message) > 0 {
@ -128,6 +140,7 @@ func AssertLen(t *testing.T, arg interface{}, length int, message ...interface{}
// AssertContains asserts an argument contains a given substring. // AssertContains asserts an argument contains a given substring.
func AssertContains(t *testing.T, s, substr string, message ...interface{}) { func AssertContains(t *testing.T, s, substr string, message ...interface{}) {
t.Helper()
if !strings.Contains(s, substr) { if !strings.Contains(s, substr) {
t.Errorf("assertion failed; expected actual to contain %q", substr) t.Errorf("assertion failed; expected actual to contain %q", substr)
if len(message) > 0 { if len(message) > 0 {
@ -139,6 +152,7 @@ func AssertContains(t *testing.T, s, substr string, message ...interface{}) {
// AssertNotContains asserts an argument does not contain a given substring. // AssertNotContains asserts an argument does not contain a given substring.
func AssertNotContains(t *testing.T, s, substr string, message ...interface{}) { func AssertNotContains(t *testing.T, s, substr string, message ...interface{}) {
t.Helper()
if strings.Contains(s, substr) { if strings.Contains(s, substr) {
t.Errorf("assertion failed; expected actual to not contain %q", substr) t.Errorf("assertion failed; expected actual to not contain %q", substr)
if len(message) > 0 { if len(message) > 0 {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestTextWrapWord(t *testing.T) { func TestTextWrapWord(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestGenerateContinuousTicks(t *testing.T) { func TestGenerateContinuousTicks(t *testing.T) {

View file

@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestTimeSeriesGetValue(t *testing.T) { func TestTimeSeriesGetValue(t *testing.T) {

View file

@ -3,7 +3,7 @@ package chart
import ( import (
"testing" "testing"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestBuffer(t *testing.T) { func TestBuffer(t *testing.T) {

View file

@ -103,3 +103,8 @@ func KValueFormatter(k float64, vf ValueFormatter) ValueFormatter {
return fmt.Sprintf("%0.0fσ %s", k, vf(v)) return fmt.Sprintf("%0.0fσ %s", k, vf(v))
} }
} }
// FloatValueFormatter is a ValueFormatter for float64, exponential notation, e.g. 1.52e+08.
func ExponentialValueFormatter(v interface{}) string {
return FloatValueFormatterWithFormat(v, "%.2e")
}

View file

@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/wcharczuk/go-chart/v2/testutil" "git.smarteching.com/zeni/go-chart/v2/testutil"
) )
func TestTimeValueFormatterWithFormat(t *testing.T) { func TestTimeValueFormatterWithFormat(t *testing.T) {
@ -56,3 +56,9 @@ func TestFloatValueFormatterWithFormat(t *testing.T) {
testutil.AssertEqual(t, "123.456", sv) testutil.AssertEqual(t, "123.456", sv)
testutil.AssertEqual(t, "123.000", FloatValueFormatterWithFormat(123, "%.3f")) testutil.AssertEqual(t, "123.000", FloatValueFormatterWithFormat(123, "%.3f"))
} }
func TestExponentialValueFormatter(t *testing.T) {
testutil.AssertEqual(t, "1.23e+02", ExponentialValueFormatter(123.456))
testutil.AssertEqual(t, "1.24e+07", ExponentialValueFormatter(12421243.424))
testutil.AssertEqual(t, "4.50e-01", ExponentialValueFormatter(0.45))
}

Some files were not shown because too many files have changed in this diff Show more