test: add test for axis
This commit is contained in:
parent
706896737b
commit
8c5647f65f
10 changed files with 374 additions and 9 deletions
3
grid.go
3
grid.go
|
|
@ -32,6 +32,8 @@ type GridPainterOption struct {
|
||||||
StrokeWidth float64
|
StrokeWidth float64
|
||||||
// The stroke color
|
// The stroke color
|
||||||
StrokeColor Color
|
StrokeColor Color
|
||||||
|
// The spans of column
|
||||||
|
ColumnSpans []int
|
||||||
// The column of grid
|
// The column of grid
|
||||||
Column int
|
Column int
|
||||||
// The row of grid
|
// The row of grid
|
||||||
|
|
@ -81,6 +83,7 @@ func (g *gridPainter) Render() (Box, error) {
|
||||||
})
|
})
|
||||||
g.p.Grid(GridOption{
|
g.p.Grid(GridOption{
|
||||||
Column: opt.Column,
|
Column: opt.Column,
|
||||||
|
ColumnSpans: opt.ColumnSpans,
|
||||||
Row: opt.Row,
|
Row: opt.Row,
|
||||||
IgnoreColumnLines: ignoreColumnLines,
|
IgnoreColumnLines: ignoreColumnLines,
|
||||||
IgnoreRowLines: ignoreRowLines,
|
IgnoreRowLines: ignoreRowLines,
|
||||||
|
|
|
||||||
18
grid_test.go
18
grid_test.go
|
|
@ -54,6 +54,24 @@ func TestGrid(t *testing.T) {
|
||||||
},
|
},
|
||||||
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<path d=\"M 100 0\nL 100 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 200 0\nL 200 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 300 0\nL 300 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 400 0\nL 400 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 500 0\nL 500 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 66\nL 600 66\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 133\nL 600 133\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 200\nL 600 200\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 266\nL 600 266\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 333\nL 600 333\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/></svg>",
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<path d=\"M 100 0\nL 100 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 200 0\nL 200 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 300 0\nL 300 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 400 0\nL 400 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 500 0\nL 500 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 66\nL 600 66\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 133\nL 600 133\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 200\nL 600 200\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 266\nL 600 266\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 333\nL 600 333\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/></svg>",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
render: func(p *Painter) ([]byte, error) {
|
||||||
|
_, err := NewGridPainter(p, GridPainterOption{
|
||||||
|
StrokeColor: drawing.ColorBlack,
|
||||||
|
ColumnSpans: []int{
|
||||||
|
2,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
},
|
||||||
|
Row: 6,
|
||||||
|
}).Render()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Bytes()
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<path d=\"M 0 0\nL 0 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 120 0\nL 120 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 420 0\nL 420 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 600 0\nL 600 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 0\nL 600 0\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 66\nL 600 66\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 133\nL 600 133\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 200\nL 600 200\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 266\nL 600 266\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 333\nL 600 333\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/><path d=\"M 0 400\nL 600 400\" style=\"stroke-width:1;stroke:rgba(0,0,0,1.0);fill:none\"/></svg>",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
p, err := NewPainter(PainterOptions{
|
p, err := NewPainter(PainterOptions{
|
||||||
|
|
|
||||||
16
painter.go
16
painter.go
|
|
@ -69,8 +69,9 @@ type MultiTextOption struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GridOption struct {
|
type GridOption struct {
|
||||||
Column int
|
Column int
|
||||||
Row int
|
Row int
|
||||||
|
ColumnSpans []int
|
||||||
// 忽略不展示的column
|
// 忽略不展示的column
|
||||||
IgnoreColumnLines []int
|
IgnoreColumnLines []int
|
||||||
// 忽略不展示的row
|
// 忽略不展示的row
|
||||||
|
|
@ -542,6 +543,9 @@ func (p *Painter) TextFit(body string, x, y, width int) chart.Box {
|
||||||
var output chart.Box
|
var output chart.Box
|
||||||
|
|
||||||
for index, line := range lines {
|
for index, line := range lines {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
x0 := x
|
x0 := x
|
||||||
y0 := y + output.Height()
|
y0 := y + output.Height()
|
||||||
p.Text(line, x0, y0)
|
p.Text(line, x0, y0)
|
||||||
|
|
@ -690,8 +694,12 @@ func (p *Painter) Grid(opt GridOption) *Painter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opt.Column > 0 {
|
columnCount := sumInt(opt.ColumnSpans)
|
||||||
values := autoDivide(width, opt.Column)
|
if columnCount == 0 {
|
||||||
|
columnCount = opt.Column
|
||||||
|
}
|
||||||
|
if columnCount > 0 {
|
||||||
|
values := autoDivideSpans(width, columnCount, opt.ColumnSpans)
|
||||||
drawLines(values, opt.IgnoreColumnLines, true)
|
drawLines(values, opt.IgnoreColumnLines, true)
|
||||||
}
|
}
|
||||||
if opt.Row > 0 {
|
if opt.Row > 0 {
|
||||||
|
|
|
||||||
89
series_test.go
Normal file
89
series_test.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
// Copyright (c) 2022 Tree Xie
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
package charts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewSeriesListDataFromValues(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
assert.Equal(SeriesList{
|
||||||
|
{
|
||||||
|
Type: ChartTypeBar,
|
||||||
|
Data: []SeriesData{
|
||||||
|
{
|
||||||
|
Value: 1.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, NewSeriesListDataFromValues([][]float64{
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
}, ChartTypeBar))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeriesLists(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
seriesList := NewSeriesListDataFromValues([][]float64{
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
10,
|
||||||
|
},
|
||||||
|
}, ChartTypeBar)
|
||||||
|
|
||||||
|
assert.Equal(2, len(seriesList.Filter(ChartTypeBar)))
|
||||||
|
assert.Equal(0, len(seriesList.Filter(ChartTypeLine)))
|
||||||
|
|
||||||
|
max, min := seriesList.GetMaxMin(0)
|
||||||
|
assert.Equal(float64(10), max)
|
||||||
|
assert.Equal(float64(1), min)
|
||||||
|
|
||||||
|
assert.Equal(seriesSummary{
|
||||||
|
MaxIndex: 1,
|
||||||
|
MaxValue: 2,
|
||||||
|
MinIndex: 0,
|
||||||
|
MinValue: 1,
|
||||||
|
AverageValue: 1.5,
|
||||||
|
}, seriesList[0].Summary())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatter(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
assert.Equal("a: 12%", NewPieLabelFormatter([]string{
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
}, "")(0, 10, 0.12))
|
||||||
|
|
||||||
|
assert.Equal("10", NewValueLabelFormatter([]string{
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
}, "")(0, 10, 0.12))
|
||||||
|
}
|
||||||
53
table.go
Normal file
53
table.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
// Copyright (c) 2022 Tree Xie
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
package charts
|
||||||
|
|
||||||
|
type tableChart struct {
|
||||||
|
p *Painter
|
||||||
|
opt *TableChartOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTableChart(p *Painter, opt TableChartOption) *tableChart {
|
||||||
|
if opt.Theme == nil {
|
||||||
|
opt.Theme = defaultTheme
|
||||||
|
}
|
||||||
|
return &tableChart{
|
||||||
|
p: p,
|
||||||
|
opt: &opt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableChartOption struct {
|
||||||
|
// The theme
|
||||||
|
Theme ColorPalette
|
||||||
|
// The padding of table cell
|
||||||
|
Padding Box
|
||||||
|
// The header data of table
|
||||||
|
HeaderData []string
|
||||||
|
// The data of table
|
||||||
|
Data [][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tableChart) Render() (Box, error) {
|
||||||
|
return BoxZero, nil
|
||||||
|
}
|
||||||
3
title.go
3
title.go
|
|
@ -97,6 +97,9 @@ func (t *titlePainter) Render() (Box, error) {
|
||||||
p := t.p
|
p := t.p
|
||||||
theme := opt.Theme
|
theme := opt.Theme
|
||||||
|
|
||||||
|
if theme == nil {
|
||||||
|
theme = p.theme
|
||||||
|
}
|
||||||
if opt.Text == "" && opt.Subtext == "" {
|
if opt.Text == "" && opt.Subtext == "" {
|
||||||
return BoxZero, nil
|
return BoxZero, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
93
title_test.go
Normal file
93
title_test.go
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
// Copyright (c) 2022 Tree Xie
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
package charts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTitleRenderer(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
tests := []struct {
|
||||||
|
render func(*Painter) ([]byte, error)
|
||||||
|
result string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
render: func(p *Painter) ([]byte, error) {
|
||||||
|
_, err := NewTitlePainter(p, TitleOption{
|
||||||
|
Text: "title",
|
||||||
|
Subtext: "subTitle",
|
||||||
|
Left: "20",
|
||||||
|
Top: "20",
|
||||||
|
}).Render()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Bytes()
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<text x=\"34\" y=\"35\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">title</text><text x=\"20\" y=\"50\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">subTitle</text></svg>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: func(p *Painter) ([]byte, error) {
|
||||||
|
_, err := NewTitlePainter(p, TitleOption{
|
||||||
|
Text: "title",
|
||||||
|
Subtext: "subTitle",
|
||||||
|
Left: "20%",
|
||||||
|
Top: "20",
|
||||||
|
}).Render()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Bytes()
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<text x=\"134\" y=\"35\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">title</text><text x=\"120\" y=\"50\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">subTitle</text></svg>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: func(p *Painter) ([]byte, error) {
|
||||||
|
_, err := NewTitlePainter(p, TitleOption{
|
||||||
|
Text: "title",
|
||||||
|
Subtext: "subTitle",
|
||||||
|
Left: PositionRight,
|
||||||
|
}).Render()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Bytes()
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<text x=\"558\" y=\"15\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">title</text><text x=\"544\" y=\"30\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">subTitle</text></svg>",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
p, err := NewPainter(PainterOptions{
|
||||||
|
Type: ChartOutputSVG,
|
||||||
|
Width: 600,
|
||||||
|
Height: 400,
|
||||||
|
}, PainterThemeOption(defaultTheme))
|
||||||
|
assert.Nil(err)
|
||||||
|
data, err := tt.render(p)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(tt.result, string(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
24
util.go
24
util.go
|
|
@ -90,6 +90,30 @@ func autoDivide(max, size int) []int {
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func autoDivideSpans(max, size int, spans []int) []int {
|
||||||
|
values := autoDivide(max, size)
|
||||||
|
// 重新合并
|
||||||
|
if len(spans) != 0 {
|
||||||
|
newValues := make([]int, len(spans)+1)
|
||||||
|
newValues[0] = 0
|
||||||
|
end := 0
|
||||||
|
for index, v := range spans {
|
||||||
|
end += v
|
||||||
|
newValues[index+1] = values[end]
|
||||||
|
}
|
||||||
|
values = newValues
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func sumInt(values []int) int {
|
||||||
|
sum := 0
|
||||||
|
for _, v := range values {
|
||||||
|
sum += v
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
// measureTextMaxWidthHeight returns maxWidth and maxHeight of text list
|
// measureTextMaxWidthHeight returns maxWidth and maxHeight of text list
|
||||||
func measureTextMaxWidthHeight(textList []string, p *Painter) (int, int) {
|
func measureTextMaxWidthHeight(textList []string, p *Painter) (int, int) {
|
||||||
maxWidth := 0
|
maxWidth := 0
|
||||||
|
|
|
||||||
14
yaxis.go
14
yaxis.go
|
|
@ -65,14 +65,18 @@ func NewYAxisOptions(data []string, others ...[]string) []YAxisOption {
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt *YAxisOption) ToAxisOption() AxisOption {
|
func (opt *YAxisOption) ToAxisOption(p *Painter) AxisOption {
|
||||||
position := PositionLeft
|
position := PositionLeft
|
||||||
if opt.Position == PositionRight {
|
if opt.Position == PositionRight {
|
||||||
position = PositionRight
|
position = PositionRight
|
||||||
}
|
}
|
||||||
|
theme := opt.Theme
|
||||||
|
if theme == nil {
|
||||||
|
theme = p.theme
|
||||||
|
}
|
||||||
axisOpt := AxisOption{
|
axisOpt := AxisOption{
|
||||||
Formatter: opt.Formatter,
|
Formatter: opt.Formatter,
|
||||||
Theme: opt.Theme,
|
Theme: theme,
|
||||||
Data: opt.Data,
|
Data: opt.Data,
|
||||||
Position: position,
|
Position: position,
|
||||||
FontSize: opt.FontSize,
|
FontSize: opt.FontSize,
|
||||||
|
|
@ -81,7 +85,7 @@ func (opt *YAxisOption) ToAxisOption() AxisOption {
|
||||||
FontColor: opt.FontColor,
|
FontColor: opt.FontColor,
|
||||||
BoundaryGap: FalseFlag(),
|
BoundaryGap: FalseFlag(),
|
||||||
SplitLineShow: true,
|
SplitLineShow: true,
|
||||||
SplitLineColor: opt.Theme.GetAxisSplitLineColor(),
|
SplitLineColor: theme.GetAxisSplitLineColor(),
|
||||||
Show: opt.Show,
|
Show: opt.Show,
|
||||||
}
|
}
|
||||||
if !opt.Color.IsZero() {
|
if !opt.Color.IsZero() {
|
||||||
|
|
@ -101,7 +105,7 @@ func NewLeftYAxis(p *Painter, opt YAxisOption) *axisPainter {
|
||||||
p = p.Child(PainterPaddingOption(Box{
|
p = p.Child(PainterPaddingOption(Box{
|
||||||
Bottom: defaultXAxisHeight,
|
Bottom: defaultXAxisHeight,
|
||||||
}))
|
}))
|
||||||
return NewAxisPainter(p, opt.ToAxisOption())
|
return NewAxisPainter(p, opt.ToAxisOption(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRightYAxis returns a right y axis renderer
|
// NewRightYAxis returns a right y axis renderer
|
||||||
|
|
@ -109,7 +113,7 @@ func NewRightYAxis(p *Painter, opt YAxisOption) *axisPainter {
|
||||||
p = p.Child(PainterPaddingOption(Box{
|
p = p.Child(PainterPaddingOption(Box{
|
||||||
Bottom: defaultXAxisHeight,
|
Bottom: defaultXAxisHeight,
|
||||||
}))
|
}))
|
||||||
axisOpt := opt.ToAxisOption()
|
axisOpt := opt.ToAxisOption(p)
|
||||||
axisOpt.Position = PositionRight
|
axisOpt.Position = PositionRight
|
||||||
axisOpt.SplitLineShow = false
|
axisOpt.SplitLineShow = false
|
||||||
return NewAxisPainter(p, axisOpt)
|
return NewAxisPainter(p, axisOpt)
|
||||||
|
|
|
||||||
70
yaxis_test.go
Normal file
70
yaxis_test.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
// Copyright (c) 2022 Tree Xie
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
package charts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRightYAxis(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
tests := []struct {
|
||||||
|
render func(*Painter) ([]byte, error)
|
||||||
|
result string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
render: func(p *Painter) ([]byte, error) {
|
||||||
|
opt := NewYAxisOptions([]string{
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c",
|
||||||
|
"d",
|
||||||
|
})[0]
|
||||||
|
_, err := NewRightYAxis(p, opt).Render()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Bytes()
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<text x=\"581\" y=\"17\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">a</text><text x=\"581\" y=\"133\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">b</text><text x=\"581\" y=\"250\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">c</text><text x=\"581\" y=\"367\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">d</text></svg>",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
p, err := NewPainter(PainterOptions{
|
||||||
|
Type: ChartOutputSVG,
|
||||||
|
Width: 600,
|
||||||
|
Height: 400,
|
||||||
|
}, PainterThemeOption(defaultTheme), PainterPaddingOption(Box{
|
||||||
|
Top: 10,
|
||||||
|
Right: 10,
|
||||||
|
Bottom: 10,
|
||||||
|
Left: 10,
|
||||||
|
}))
|
||||||
|
assert.Nil(err)
|
||||||
|
data, err := tt.render(p)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(tt.result, string(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue