Compare commits
38 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 958172a1f1 | |||
| 0eacc8e394 | |||
|
|
d25a827706 | ||
|
|
5842c71b1d | ||
|
|
e7dc4189d5 | ||
|
|
32e6dd52d0 | ||
|
|
9614835723 | ||
|
|
9b7634c2c2 | ||
|
|
5a69c3e5a3 | ||
|
|
8c6c4e007c | ||
|
|
765febd03a | ||
|
|
19a4d783fd | ||
|
|
06fe1006d5 | ||
|
|
f1a231ff4b | ||
|
|
c7c0655113 | ||
|
|
310800a5f0 | ||
|
|
e09ab2c3c7 | ||
|
|
c2f709a742 | ||
|
|
98af9866a4 | ||
|
|
c302d0ffa4 | ||
|
|
8bcb584aba | ||
|
|
0ddb9e4ef1 | ||
|
|
18d8ee51fb | ||
|
|
687baad0af | ||
|
|
a158191faf | ||
|
|
c810369730 | ||
|
|
19173dfd37 | ||
|
|
e7a49c2c21 | ||
|
|
20e8d4a078 | ||
|
|
29a5ece545 | ||
|
|
d3f7a773af | ||
|
|
8ba9e2e1b2 | ||
|
|
e10175594b | ||
|
|
b3cb5a75cb | ||
|
|
a767b3e1af | ||
|
|
830d4bdd21 | ||
|
|
d5533447f5 | ||
|
|
ef04ac14ab |
49 changed files with 1129 additions and 231 deletions
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
|
|
@ -14,13 +14,12 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go:
|
go:
|
||||||
|
- '1.22'
|
||||||
|
- '1.21'
|
||||||
|
- '1.20'
|
||||||
- '1.19'
|
- '1.19'
|
||||||
- '1.18'
|
- '1.18'
|
||||||
- '1.17'
|
- '1.17'
|
||||||
- '1.16'
|
|
||||||
- '1.15'
|
|
||||||
- '1.14'
|
|
||||||
- '1.13'
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Go ${{ matrix.go }} test
|
- name: Go ${{ matrix.go }} test
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -1,5 +1,7 @@
|
||||||
# go-charts
|
# go-charts
|
||||||
|
|
||||||
|
Clone from https://github.com/vicanso/go-charts
|
||||||
|
|
||||||
[](https://github.com/vicanso/go-charts/blob/master/LICENSE)
|
[](https://github.com/vicanso/go-charts/blob/master/LICENSE)
|
||||||
[](https://github.com/vicanso/go-charts/actions)
|
[](https://github.com/vicanso/go-charts/actions)
|
||||||
|
|
||||||
|
|
@ -33,7 +35,7 @@ More examples can be found in the [./examples/](./examples/) directory.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
charts "github.com/vicanso/go-charts/v2"
|
charts "git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -99,7 +101,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -174,7 +176,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -231,7 +233,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -286,7 +288,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -344,7 +346,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -384,7 +386,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -449,7 +451,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
16
README_zh.md
16
README_zh.md
|
|
@ -32,7 +32,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
charts "github.com/vicanso/go-charts/v2"
|
charts "git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -98,7 +98,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -173,7 +173,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -230,7 +230,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -285,7 +285,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -343,7 +343,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -383,7 +383,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -447,7 +447,7 @@ func main() {
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
4
alias.go
4
alias.go
|
|
@ -23,8 +23,8 @@
|
||||||
package charts
|
package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Box = chart.Box
|
type Box = chart.Box
|
||||||
|
|
|
||||||
32
axis.go
32
axis.go
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type axisPainter struct {
|
type axisPainter struct {
|
||||||
|
|
@ -63,6 +63,8 @@ type AxisOption struct {
|
||||||
StrokeWidth float64
|
StrokeWidth float64
|
||||||
// The length of the axis tick
|
// The length of the axis tick
|
||||||
TickLength int
|
TickLength int
|
||||||
|
// The first axis
|
||||||
|
FirstAxis int
|
||||||
// The margin value of label
|
// The margin value of label
|
||||||
LabelMargin int
|
LabelMargin int
|
||||||
// The font size of label
|
// The font size of label
|
||||||
|
|
@ -75,7 +77,11 @@ type AxisOption struct {
|
||||||
SplitLineShow bool
|
SplitLineShow bool
|
||||||
// The color of split line
|
// The color of split line
|
||||||
SplitLineColor Color
|
SplitLineColor Color
|
||||||
Unit int
|
// The text rotation of label
|
||||||
|
TextRotation float64
|
||||||
|
// The offset of label
|
||||||
|
LabelOffset Box
|
||||||
|
Unit int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *axisPainter) Render() (Box, error) {
|
func (a *axisPainter) Render() (Box, error) {
|
||||||
|
|
@ -153,7 +159,15 @@ func (a *axisPainter) Render() (Box, error) {
|
||||||
}
|
}
|
||||||
top.SetDrawingStyle(style).OverrideTextStyle(style)
|
top.SetDrawingStyle(style).OverrideTextStyle(style)
|
||||||
|
|
||||||
|
isTextRotation := opt.TextRotation != 0
|
||||||
|
|
||||||
|
if isTextRotation {
|
||||||
|
top.SetTextRotation(opt.TextRotation)
|
||||||
|
}
|
||||||
textMaxWidth, textMaxHeight := top.MeasureTextMaxWidthHeight(data)
|
textMaxWidth, textMaxHeight := top.MeasureTextMaxWidthHeight(data)
|
||||||
|
if isTextRotation {
|
||||||
|
top.ClearTextRotation()
|
||||||
|
}
|
||||||
|
|
||||||
// 增加30px来计算文本展示区域
|
// 增加30px来计算文本展示区域
|
||||||
textFillWidth := float64(textMaxWidth + 20)
|
textFillWidth := float64(textMaxWidth + 20)
|
||||||
|
|
@ -243,6 +257,7 @@ func (a *axisPainter) Render() (Box, error) {
|
||||||
Length: tickLength,
|
Length: tickLength,
|
||||||
Unit: unit,
|
Unit: unit,
|
||||||
Orient: orient,
|
Orient: orient,
|
||||||
|
First: opt.FirstAxis,
|
||||||
})
|
})
|
||||||
p.LineStroke([]Point{
|
p.LineStroke([]Point{
|
||||||
{
|
{
|
||||||
|
|
@ -261,11 +276,14 @@ func (a *axisPainter) Render() (Box, error) {
|
||||||
Top: labelPaddingTop,
|
Top: labelPaddingTop,
|
||||||
Right: labelPaddingRight,
|
Right: labelPaddingRight,
|
||||||
})).MultiText(MultiTextOption{
|
})).MultiText(MultiTextOption{
|
||||||
Align: textAlign,
|
First: opt.FirstAxis,
|
||||||
TextList: data,
|
Align: textAlign,
|
||||||
Orient: orient,
|
TextList: data,
|
||||||
Unit: unit,
|
Orient: orient,
|
||||||
Position: labelPosition,
|
Unit: unit,
|
||||||
|
Position: labelPosition,
|
||||||
|
TextRotation: opt.TextRotation,
|
||||||
|
Offset: opt.LabelOffset,
|
||||||
})
|
})
|
||||||
// 显示辅助线
|
// 显示辅助线
|
||||||
if opt.SplitLineShow {
|
if opt.SplitLineShow {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAxis(t *testing.T) {
|
func TestAxis(t *testing.T) {
|
||||||
|
|
|
||||||
35
bar_chart.go
35
bar_chart.go
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type barChart struct {
|
type barChart struct {
|
||||||
|
|
@ -63,6 +63,8 @@ type BarChartOption struct {
|
||||||
// The legend option
|
// The legend option
|
||||||
Legend LegendOption
|
Legend LegendOption
|
||||||
BarWidth int
|
BarWidth int
|
||||||
|
// Margin of bar
|
||||||
|
BarMargin int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
|
func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
|
||||||
|
|
@ -88,6 +90,9 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
|
||||||
margin = 5
|
margin = 5
|
||||||
barMargin = 3
|
barMargin = 3
|
||||||
}
|
}
|
||||||
|
if opt.BarMargin > 0 {
|
||||||
|
barMargin = opt.BarMargin
|
||||||
|
}
|
||||||
seriesCount := len(seriesList)
|
seriesCount := len(seriesList)
|
||||||
// 总的宽度-两个margin-(总数-1)的barMargin
|
// 总的宽度-两个margin-(总数-1)的barMargin
|
||||||
barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / seriesCount
|
barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / seriesCount
|
||||||
|
|
@ -142,14 +147,25 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
|
||||||
}
|
}
|
||||||
top := barMaxHeight - h
|
top := barMaxHeight - h
|
||||||
|
|
||||||
seriesPainter.OverrideDrawingStyle(Style{
|
if series.RoundRadius <= 0 {
|
||||||
FillColor: fillColor,
|
seriesPainter.OverrideDrawingStyle(Style{
|
||||||
}).Rect(chart.Box{
|
FillColor: fillColor,
|
||||||
Top: top,
|
}).Rect(chart.Box{
|
||||||
Left: x,
|
Top: top,
|
||||||
Right: x + barWidth,
|
Left: x,
|
||||||
Bottom: barMaxHeight - 1,
|
Right: x + barWidth,
|
||||||
})
|
Bottom: barMaxHeight - 1,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
seriesPainter.OverrideDrawingStyle(Style{
|
||||||
|
FillColor: fillColor,
|
||||||
|
}).RoundedRect(chart.Box{
|
||||||
|
Top: top,
|
||||||
|
Left: x,
|
||||||
|
Right: x + barWidth,
|
||||||
|
Bottom: barMaxHeight - 1,
|
||||||
|
}, series.RoundRadius)
|
||||||
|
}
|
||||||
// 用于生成marker point
|
// 用于生成marker point
|
||||||
points[j] = Point{
|
points[j] = Point{
|
||||||
// 居中的位置
|
// 居中的位置
|
||||||
|
|
@ -189,6 +205,7 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
|
||||||
Radians: radians,
|
Radians: radians,
|
||||||
FontColor: fontColor,
|
FontColor: fontColor,
|
||||||
Offset: series.Label.Offset,
|
Offset: series.Label.Offset,
|
||||||
|
FontSize: series.Label.FontSize,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -67,10 +67,14 @@ type ChartOption struct {
|
||||||
LineStrokeWidth float64
|
LineStrokeWidth float64
|
||||||
// The bar with of bar chart
|
// The bar with of bar chart
|
||||||
BarWidth int
|
BarWidth int
|
||||||
|
// The margin of each bar
|
||||||
|
BarMargin int
|
||||||
// The bar height of horizontal bar chart
|
// The bar height of horizontal bar chart
|
||||||
BarHeight int
|
BarHeight int
|
||||||
// Fill the area of line chart
|
// Fill the area of line chart
|
||||||
FillArea bool
|
FillArea bool
|
||||||
|
// background fill (alpha) opacity
|
||||||
|
Opacity uint8
|
||||||
// The child charts
|
// The child charts
|
||||||
Children []ChartOption
|
Children []ChartOption
|
||||||
// The value formatter
|
// The value formatter
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChartOption(t *testing.T) {
|
func TestChartOption(t *testing.T) {
|
||||||
|
|
|
||||||
24
charts.go
24
charts.go
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const labelFontSize = 10
|
const labelFontSize = 10
|
||||||
|
|
@ -215,7 +215,16 @@ func defaultRender(p *Painter, opt defaultRenderOption) (*defaultRenderResult, e
|
||||||
yAxisOption.Data = r.Values()
|
yAxisOption.Data = r.Values()
|
||||||
} else {
|
} else {
|
||||||
yAxisOption.isCategoryAxis = true
|
yAxisOption.isCategoryAxis = true
|
||||||
opt.XAxis.Data = r.Values()
|
// 由于x轴为value部分,因此计算其label单独处理
|
||||||
|
opt.XAxis.Data = NewRange(AxisRangeOption{
|
||||||
|
Painter: p,
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
// 高度需要减去x轴的高度
|
||||||
|
Size: rangeHeight,
|
||||||
|
// 分隔数量
|
||||||
|
DivideCount: defaultAxisDivideCount,
|
||||||
|
}).Values()
|
||||||
opt.XAxis.isValueAxis = true
|
opt.XAxis.isValueAxis = true
|
||||||
}
|
}
|
||||||
reverseStringSlice(yAxisOption.Data)
|
reverseStringSlice(yAxisOption.Data)
|
||||||
|
|
@ -366,10 +375,11 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) {
|
||||||
if len(barSeriesList) != 0 {
|
if len(barSeriesList) != 0 {
|
||||||
handler.Add(func() error {
|
handler.Add(func() error {
|
||||||
_, err := NewBarChart(p, BarChartOption{
|
_, err := NewBarChart(p, BarChartOption{
|
||||||
Theme: opt.theme,
|
Theme: opt.theme,
|
||||||
Font: opt.font,
|
Font: opt.font,
|
||||||
XAxis: opt.XAxis,
|
XAxis: opt.XAxis,
|
||||||
BarWidth: opt.BarWidth,
|
BarWidth: opt.BarWidth,
|
||||||
|
BarMargin: opt.BarMargin,
|
||||||
}).render(renderResult, barSeriesList)
|
}).render(renderResult, barSeriesList)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
@ -382,6 +392,7 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) {
|
||||||
Theme: opt.theme,
|
Theme: opt.theme,
|
||||||
Font: opt.font,
|
Font: opt.font,
|
||||||
BarHeight: opt.BarHeight,
|
BarHeight: opt.BarHeight,
|
||||||
|
BarMargin: opt.BarMargin,
|
||||||
YAxisOptions: opt.YAxisOptions,
|
YAxisOptions: opt.YAxisOptions,
|
||||||
}).render(renderResult, horizontalBarSeriesList)
|
}).render(renderResult, horizontalBarSeriesList)
|
||||||
return err
|
return err
|
||||||
|
|
@ -409,6 +420,7 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) {
|
||||||
SymbolShow: opt.SymbolShow,
|
SymbolShow: opt.SymbolShow,
|
||||||
StrokeWidth: opt.LineStrokeWidth,
|
StrokeWidth: opt.LineStrokeWidth,
|
||||||
FillArea: opt.FillArea,
|
FillArea: opt.FillArea,
|
||||||
|
Opacity: opt.Opacity,
|
||||||
}).render(renderResult, lineSeriesList)
|
}).render(renderResult, lineSeriesList)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkMultiChartPNGRender(b *testing.B) {
|
func BenchmarkMultiChartPNGRender(b *testing.B) {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func convertToArray(data []byte) []byte {
|
func convertToArray(data []byte) []byte {
|
||||||
|
|
@ -344,6 +344,11 @@ func (esList EChartsSeriesList) ToSeriesList() SeriesList {
|
||||||
Data: NewSeriesDataFromValues(dataItem.Value.values),
|
Data: NewSeriesDataFromValues(dataItem.Value.values),
|
||||||
Max: item.Max,
|
Max: item.Max,
|
||||||
Min: item.Min,
|
Min: item.Min,
|
||||||
|
Label: SeriesLabel{
|
||||||
|
Color: parseColor(item.Label.Color),
|
||||||
|
Show: item.Label.Show,
|
||||||
|
Distance: item.Label.Distance,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConvertToArray(t *testing.T) {
|
func TestConvertToArray(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +15,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "area-line-chart.png")
|
file := filepath.Join(tmpPath, "area-line-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +15,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "bar-chart.png")
|
file := filepath.Join(tmpPath, "bar-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
charts "github.com/vicanso/go-charts/v2"
|
charts "git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var html = `<!DOCTYPE html>
|
var html = `<!DOCTYPE html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +16,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "chinese-line-chart.png")
|
file := filepath.Join(tmpPath, "chinese-line-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +15,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "funnel-chart.png")
|
file := filepath.Join(tmpPath, "funnel-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -30,6 +29,8 @@ func main() {
|
||||||
60,
|
60,
|
||||||
40,
|
40,
|
||||||
20,
|
20,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
}
|
}
|
||||||
p, err := charts.FunnelRender(
|
p, err := charts.FunnelRender(
|
||||||
values,
|
values,
|
||||||
|
|
@ -40,6 +41,8 @@ func main() {
|
||||||
"Visit",
|
"Visit",
|
||||||
"Inquiry",
|
"Inquiry",
|
||||||
"Order",
|
"Order",
|
||||||
|
"Pay",
|
||||||
|
"Cancel",
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +15,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "horizontal-bar-chart.png")
|
file := filepath.Join(tmpPath, "horizontal-bar-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -26,22 +25,22 @@ func writeFile(buf []byte) error {
|
||||||
func main() {
|
func main() {
|
||||||
values := [][]float64{
|
values := [][]float64{
|
||||||
{
|
{
|
||||||
8203,
|
10,
|
||||||
18203,
|
30,
|
||||||
23489,
|
50,
|
||||||
29034,
|
70,
|
||||||
104970,
|
90,
|
||||||
131744,
|
110,
|
||||||
630230,
|
130,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
9325,
|
20,
|
||||||
19325,
|
40,
|
||||||
23438,
|
60,
|
||||||
31000,
|
80,
|
||||||
121594,
|
100,
|
||||||
134141,
|
120,
|
||||||
681807,
|
140,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
p, err := charts.HorizontalBarRender(
|
p, err := charts.HorizontalBarRender(
|
||||||
|
|
@ -66,6 +65,9 @@ func main() {
|
||||||
"China",
|
"China",
|
||||||
"World",
|
"World",
|
||||||
}),
|
}),
|
||||||
|
func(opt *charts.ChartOption) {
|
||||||
|
opt.SeriesList[0].RoundRadius = 5
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -17,7 +16,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "line-chart.png")
|
file := filepath.Join(tmpPath, "line-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +96,11 @@ func main() {
|
||||||
Top: 5,
|
Top: 5,
|
||||||
Bottom: 10,
|
Bottom: 10,
|
||||||
}
|
}
|
||||||
|
opt.YAxisOptions = []charts.YAxisOption{
|
||||||
|
{
|
||||||
|
SplitLineShow: charts.FalseFlag(),
|
||||||
|
},
|
||||||
|
}
|
||||||
opt.SymbolShow = charts.FalseFlag()
|
opt.SymbolShow = charts.FalseFlag()
|
||||||
opt.LineStrokeWidth = 1
|
opt.LineStrokeWidth = 1
|
||||||
opt.ValueFormatter = func(f float64) string {
|
opt.ValueFormatter = func(f float64) string {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
charts "github.com/vicanso/go-charts/v2"
|
charts "git.smarteching.com/zeni/go-charts/v2"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -17,7 +16,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "painter.png")
|
file := filepath.Join(tmpPath, "painter.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +15,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "pie-chart.png")
|
file := filepath.Join(tmpPath, "pie-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte) error {
|
func writeFile(buf []byte) error {
|
||||||
|
|
@ -16,7 +15,7 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, "radar-chart.png")
|
file := filepath.Join(tmpPath, "radar-chart.png")
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/vicanso/go-charts/v2"
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeFile(buf []byte, filename string) error {
|
func writeFile(buf []byte, filename string) error {
|
||||||
|
|
@ -19,7 +18,7 @@ func writeFile(buf []byte, filename string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
file := filepath.Join(tmpPath, filename)
|
file := filepath.Join(tmpPath, filename)
|
||||||
err = ioutil.WriteFile(file, buf, 0600)
|
err = os.WriteFile(file, buf, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
examples/time_line_chart/main.go
Normal file
81
examples/time_line_chart/main.go
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.smarteching.com/zeni/go-charts/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeFile(buf []byte) error {
|
||||||
|
tmpPath := "./tmp"
|
||||||
|
err := os.MkdirAll(tmpPath, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file := filepath.Join(tmpPath, "time-line-chart.png")
|
||||||
|
err = os.WriteFile(file, buf, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xAxisValue := []string{}
|
||||||
|
values := []float64{}
|
||||||
|
now := time.Now()
|
||||||
|
firstAxis := 0
|
||||||
|
for i := 0; i < 300; i++ {
|
||||||
|
// 设置首个axis为xx:00的时间点
|
||||||
|
if firstAxis == 0 && now.Minute() == 0 {
|
||||||
|
firstAxis = i
|
||||||
|
}
|
||||||
|
xAxisValue = append(xAxisValue, now.Format("15:04"))
|
||||||
|
now = now.Add(time.Minute)
|
||||||
|
value, _ := rand.Int(rand.Reader, big.NewInt(100))
|
||||||
|
values = append(values, float64(value.Int64()))
|
||||||
|
}
|
||||||
|
p, err := charts.LineRender(
|
||||||
|
[][]float64{
|
||||||
|
values,
|
||||||
|
},
|
||||||
|
charts.TitleTextOptionFunc("Line"),
|
||||||
|
charts.XAxisDataOptionFunc(xAxisValue, charts.FalseFlag()),
|
||||||
|
charts.LegendLabelsOptionFunc([]string{
|
||||||
|
"Demo",
|
||||||
|
}, "50"),
|
||||||
|
func(opt *charts.ChartOption) {
|
||||||
|
opt.XAxis.FirstAxis = firstAxis
|
||||||
|
// 必须要比计算得来的最小值更大(每60分钟)
|
||||||
|
opt.XAxis.SplitNumber = 60
|
||||||
|
opt.Legend.Padding = charts.Box{
|
||||||
|
Top: 5,
|
||||||
|
Bottom: 10,
|
||||||
|
}
|
||||||
|
opt.SymbolShow = charts.FalseFlag()
|
||||||
|
opt.LineStrokeWidth = 1
|
||||||
|
opt.ValueFormatter = func(f float64) string {
|
||||||
|
return fmt.Sprintf("%.0f", f)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := p.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = writeFile(buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
2
font.go
2
font.go
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2/roboto"
|
"git.smarteching.com/zeni/go-chart/v2/roboto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fonts = sync.Map{}
|
var fonts = sync.Map{}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/roboto"
|
"git.smarteching.com/zeni/go-chart/v2/roboto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInstallFont(t *testing.T) {
|
func TestInstallFont(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,6 @@
|
||||||
package charts
|
package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -95,13 +92,23 @@ func (f *funnelChart) render(result *defaultRenderResult, seriesList SeriesList)
|
||||||
y := 0
|
y := 0
|
||||||
widthList := make([]int, len(seriesList))
|
widthList := make([]int, len(seriesList))
|
||||||
textList := make([]string, len(seriesList))
|
textList := make([]string, len(seriesList))
|
||||||
|
seriesNames := seriesList.Names()
|
||||||
|
offset := max - min
|
||||||
for index, item := range seriesList {
|
for index, item := range seriesList {
|
||||||
value := item.Data[0].Value
|
value := item.Data[0].Value
|
||||||
widthPercent := (value - min) / (max - min)
|
// 最大最小值一致则为100%
|
||||||
|
widthPercent := 100.0
|
||||||
|
if offset != 0 {
|
||||||
|
widthPercent = (value - min) / offset
|
||||||
|
}
|
||||||
w := int(widthPercent * float64(width))
|
w := int(widthPercent * float64(width))
|
||||||
widthList[index] = w
|
widthList[index] = w
|
||||||
p := humanize.CommafWithDigits(value/max*100, 2) + "%"
|
// 如果最大值为0,则占比100%
|
||||||
textList[index] = fmt.Sprintf("%s(%s)", item.Name, p)
|
percent := 1.0
|
||||||
|
if max != 0 {
|
||||||
|
percent = value / max
|
||||||
|
}
|
||||||
|
textList[index] = NewFunnelLabelFormatter(seriesNames, item.Label.Formatter)(index, value, percent)
|
||||||
}
|
}
|
||||||
|
|
||||||
for index, w := range widthList {
|
for index, w := range widthList {
|
||||||
|
|
|
||||||
12
go.mod
12
go.mod
|
|
@ -1,17 +1,17 @@
|
||||||
module github.com/vicanso/go-charts/v2
|
module git.smarteching.com/zeni/go-charts/v2
|
||||||
|
|
||||||
go 1.17
|
go 1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dustin/go-humanize v1.0.0
|
git.smarteching.com/zeni/go-chart/v2 v2.1.4
|
||||||
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/wcharczuk/go-chart/v2 v2.1.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
|
golang.org/x/image v0.21.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
25
go.sum
25
go.sum
|
|
@ -1,27 +1,18 @@
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
git.smarteching.com/zeni/go-chart/v2 v2.1.4 h1:pF06+F6eqJLIG8uMiTVPR5TygPGMjM/FHMzTxmu5V/Q=
|
||||||
|
git.smarteching.com/zeni/go-chart/v2 v2.1.4/go.mod h1:b3ueW9h3pGGXyhkormZAvilHaG4+mQti+bMNPdQBeOQ=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
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=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
|
|
||||||
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
|
|
||||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
|
|
||||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGrid(t *testing.T) {
|
func TestGrid(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type horizontalBarChart struct {
|
type horizontalBarChart struct {
|
||||||
|
|
@ -50,6 +50,8 @@ type HorizontalBarChartOption struct {
|
||||||
// The legend option
|
// The legend option
|
||||||
Legend LegendOption
|
Legend LegendOption
|
||||||
BarHeight int
|
BarHeight int
|
||||||
|
// Margin of bar
|
||||||
|
BarMargin int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHorizontalBarChart returns a horizontal bar chart renderer
|
// NewHorizontalBarChart returns a horizontal bar chart renderer
|
||||||
|
|
@ -81,6 +83,9 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
|
||||||
margin = 5
|
margin = 5
|
||||||
barMargin = 3
|
barMargin = 3
|
||||||
}
|
}
|
||||||
|
if opt.BarMargin > 0 {
|
||||||
|
barMargin = opt.BarMargin
|
||||||
|
}
|
||||||
seriesCount := len(seriesList)
|
seriesCount := len(seriesList)
|
||||||
// 总的高度-两个margin-(总数-1)的barMargin
|
// 总的高度-两个margin-(总数-1)的barMargin
|
||||||
barHeight := (height - 2*margin - barMargin*(seriesCount-1)) / seriesCount
|
barHeight := (height - 2*margin - barMargin*(seriesCount-1)) / seriesCount
|
||||||
|
|
@ -136,14 +141,26 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
|
||||||
fillColor = item.Style.FillColor
|
fillColor = item.Style.FillColor
|
||||||
}
|
}
|
||||||
right := w
|
right := w
|
||||||
seriesPainter.OverrideDrawingStyle(Style{
|
if series.RoundRadius <= 0 {
|
||||||
FillColor: fillColor,
|
seriesPainter.OverrideDrawingStyle(Style{
|
||||||
}).Rect(chart.Box{
|
FillColor: fillColor,
|
||||||
Top: y,
|
}).Rect(chart.Box{
|
||||||
Left: 0,
|
Top: y,
|
||||||
Right: right,
|
Left: 0,
|
||||||
Bottom: y + barHeight,
|
Right: right,
|
||||||
})
|
Bottom: y + barHeight,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
seriesPainter.OverrideDrawingStyle(Style{
|
||||||
|
FillColor: fillColor,
|
||||||
|
}).RoundedRect(chart.Box{
|
||||||
|
Top: y,
|
||||||
|
Left: 0,
|
||||||
|
Right: right,
|
||||||
|
Bottom: y + barHeight,
|
||||||
|
}, series.RoundRadius)
|
||||||
|
}
|
||||||
|
|
||||||
// 如果label不需要展示,则返回
|
// 如果label不需要展示,则返回
|
||||||
if labelPainter == nil {
|
if labelPainter == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -156,6 +173,7 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
|
||||||
Y: y + barHeight>>1,
|
Y: y + barHeight>>1,
|
||||||
Offset: series.Label.Offset,
|
Offset: series.Label.Offset,
|
||||||
FontColor: series.Label.Color,
|
FontColor: series.Label.Color,
|
||||||
|
FontSize: series.Label.FontSize,
|
||||||
}
|
}
|
||||||
if series.Label.Position == PositionLeft {
|
if series.Label.Position == PositionLeft {
|
||||||
labelValue.X = 0
|
labelValue.X = 0
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lineChart struct {
|
type lineChart struct {
|
||||||
|
|
@ -70,6 +70,8 @@ type LineChartOption struct {
|
||||||
FillArea bool
|
FillArea bool
|
||||||
// background is filled
|
// background is filled
|
||||||
backgroundIsFilled bool
|
backgroundIsFilled bool
|
||||||
|
// background fill (alpha) opacity
|
||||||
|
Opacity uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
|
func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
|
||||||
|
|
@ -113,6 +115,9 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
|
||||||
StrokeColor: seriesColor,
|
StrokeColor: seriesColor,
|
||||||
StrokeWidth: strokeWidth,
|
StrokeWidth: strokeWidth,
|
||||||
}
|
}
|
||||||
|
if len(series.Style.StrokeDashArray) > 0 {
|
||||||
|
drawingStyle.StrokeDashArray = series.Style.StrokeDashArray
|
||||||
|
}
|
||||||
|
|
||||||
yRange := result.axisRanges[series.AxisIndex]
|
yRange := result.axisRanges[series.AxisIndex]
|
||||||
points := make([]Point, 0)
|
points := make([]Point, 0)
|
||||||
|
|
@ -147,6 +152,8 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
|
||||||
Value: item.Value,
|
Value: item.Value,
|
||||||
X: p.X,
|
X: p.X,
|
||||||
Y: p.Y,
|
Y: p.Y,
|
||||||
|
// 字体大小
|
||||||
|
FontSize: series.Label.FontSize,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 如果需要填充区域
|
// 如果需要填充区域
|
||||||
|
|
@ -154,6 +161,10 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
|
||||||
areaPoints := make([]Point, len(points))
|
areaPoints := make([]Point, len(points))
|
||||||
copy(areaPoints, points)
|
copy(areaPoints, points)
|
||||||
bottomY := yRange.getRestHeight(yRange.min)
|
bottomY := yRange.getRestHeight(yRange.min)
|
||||||
|
var opacity uint8 = 200
|
||||||
|
if opt.Opacity != 0 {
|
||||||
|
opacity = opt.Opacity
|
||||||
|
}
|
||||||
areaPoints = append(areaPoints, Point{
|
areaPoints = append(areaPoints, Point{
|
||||||
X: areaPoints[len(areaPoints)-1].X,
|
X: areaPoints[len(areaPoints)-1].X,
|
||||||
Y: bottomY,
|
Y: bottomY,
|
||||||
|
|
@ -162,7 +173,7 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
|
||||||
Y: bottomY,
|
Y: bottomY,
|
||||||
}, areaPoints[0])
|
}, areaPoints[0])
|
||||||
seriesPainter.SetDrawingStyle(Style{
|
seriesPainter.SetDrawingStyle(Style{
|
||||||
FillColor: seriesColor.WithAlpha(200),
|
FillColor: seriesColor.WithAlpha(opacity),
|
||||||
})
|
})
|
||||||
seriesPainter.FillArea(areaPoints)
|
seriesPainter.FillArea(areaPoints)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarkLine(t *testing.T) {
|
func TestMarkLine(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarkPoint(t *testing.T) {
|
func TestMarkPoint(t *testing.T) {
|
||||||
|
|
|
||||||
77
painter.go
77
painter.go
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ValueFormatter func(float64) string
|
type ValueFormatter func(float64) string
|
||||||
|
|
@ -59,6 +59,8 @@ type PainterOptions struct {
|
||||||
type PainterOption func(*Painter)
|
type PainterOption func(*Painter)
|
||||||
|
|
||||||
type TicksOption struct {
|
type TicksOption struct {
|
||||||
|
// the first tick
|
||||||
|
First int
|
||||||
Length int
|
Length int
|
||||||
Orient string
|
Orient string
|
||||||
Count int
|
Count int
|
||||||
|
|
@ -71,6 +73,11 @@ type MultiTextOption struct {
|
||||||
Unit int
|
Unit int
|
||||||
Position string
|
Position string
|
||||||
Align string
|
Align string
|
||||||
|
// The text rotation of label
|
||||||
|
TextRotation float64
|
||||||
|
Offset Box
|
||||||
|
// The first text index
|
||||||
|
First int
|
||||||
}
|
}
|
||||||
|
|
||||||
type GridOption struct {
|
type GridOption struct {
|
||||||
|
|
@ -613,6 +620,7 @@ func (p *Painter) Ticks(opt TicksOption) *Painter {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
count := opt.Count
|
count := opt.Count
|
||||||
|
first := opt.First
|
||||||
width := p.Width()
|
width := p.Width()
|
||||||
height := p.Height()
|
height := p.Height()
|
||||||
unit := 1
|
unit := 1
|
||||||
|
|
@ -627,7 +635,10 @@ func (p *Painter) Ticks(opt TicksOption) *Painter {
|
||||||
values = autoDivide(width, count)
|
values = autoDivide(width, count)
|
||||||
}
|
}
|
||||||
for index, value := range values {
|
for index, value := range values {
|
||||||
if index%unit != 0 {
|
if index < first {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (index-first)%unit != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isVertical {
|
if isVertical {
|
||||||
|
|
@ -682,10 +693,19 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter {
|
||||||
} else {
|
} else {
|
||||||
values = autoDivide(width, count)
|
values = autoDivide(width, count)
|
||||||
}
|
}
|
||||||
|
isTextRotation := opt.TextRotation != 0
|
||||||
|
offset := opt.Offset
|
||||||
for index, text := range opt.TextList {
|
for index, text := range opt.TextList {
|
||||||
if opt.Unit != 0 && index%opt.Unit != showIndex {
|
if index < opt.First {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if opt.Unit != 0 && (index-opt.First)%opt.Unit != showIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isTextRotation {
|
||||||
|
p.ClearTextRotation()
|
||||||
|
p.SetTextRotation(opt.TextRotation)
|
||||||
|
}
|
||||||
box := p.MeasureText(text)
|
box := p.MeasureText(text)
|
||||||
start := values[index]
|
start := values[index]
|
||||||
if positionCenter {
|
if positionCenter {
|
||||||
|
|
@ -706,8 +726,13 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter {
|
||||||
} else {
|
} else {
|
||||||
x = start - box.Width()>>1
|
x = start - box.Width()>>1
|
||||||
}
|
}
|
||||||
|
x += offset.Left
|
||||||
|
y += offset.Top
|
||||||
p.Text(text, x, y)
|
p.Text(text, x, y)
|
||||||
}
|
}
|
||||||
|
if isTextRotation {
|
||||||
|
p.ClearTextRotation()
|
||||||
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -778,6 +803,48 @@ func (p *Painter) Rect(box Box) *Painter {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Painter) RoundedRect(box Box, radius int) *Painter {
|
||||||
|
r := (box.Right - box.Left) / 2
|
||||||
|
if radius > r {
|
||||||
|
radius = r
|
||||||
|
}
|
||||||
|
rx := float64(radius)
|
||||||
|
ry := float64(radius)
|
||||||
|
p.MoveTo(box.Left+radius, box.Top)
|
||||||
|
p.LineTo(box.Right-radius, box.Top)
|
||||||
|
|
||||||
|
cx := box.Right - radius
|
||||||
|
cy := box.Top + radius
|
||||||
|
// right top
|
||||||
|
p.ArcTo(cx, cy, rx, ry, -math.Pi/2, math.Pi/2)
|
||||||
|
|
||||||
|
p.LineTo(box.Right, box.Bottom-radius)
|
||||||
|
|
||||||
|
// right bottom
|
||||||
|
cx = box.Right - radius
|
||||||
|
cy = box.Bottom - radius
|
||||||
|
p.ArcTo(cx, cy, rx, ry, 0.0, math.Pi/2)
|
||||||
|
|
||||||
|
p.LineTo(box.Left+radius, box.Bottom)
|
||||||
|
|
||||||
|
// left bottom
|
||||||
|
cx = box.Left + radius
|
||||||
|
cy = box.Bottom - radius
|
||||||
|
p.ArcTo(cx, cy, rx, ry, math.Pi/2, math.Pi/2)
|
||||||
|
|
||||||
|
p.LineTo(box.Left, box.Top+radius)
|
||||||
|
|
||||||
|
// left top
|
||||||
|
cx = box.Left + radius
|
||||||
|
cy = box.Top + radius
|
||||||
|
p.ArcTo(cx, cy, rx, ry, math.Pi, math.Pi/2)
|
||||||
|
|
||||||
|
p.Close()
|
||||||
|
p.FillStroke()
|
||||||
|
p.Fill()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Painter) LegendLineDot(box Box) *Painter {
|
func (p *Painter) LegendLineDot(box Box) *Painter {
|
||||||
width := box.Width()
|
width := box.Width()
|
||||||
height := box.Height()
|
height := box.Height()
|
||||||
|
|
@ -793,3 +860,7 @@ func (p *Painter) LegendLineDot(box Box) *Painter {
|
||||||
p.FillStroke()
|
p.FillStroke()
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Painter) GetRenderer() chart.Renderer {
|
||||||
|
return p.render
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ import (
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"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 TestPainterOption(t *testing.T) {
|
func TestPainterOption(t *testing.T) {
|
||||||
|
|
@ -343,6 +343,29 @@ func TestPainter(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRoundedRect(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
p, err := NewPainter(PainterOptions{
|
||||||
|
Width: 400,
|
||||||
|
Height: 300,
|
||||||
|
Type: ChartOutputSVG,
|
||||||
|
})
|
||||||
|
assert.Nil(err)
|
||||||
|
p.OverrideDrawingStyle(Style{
|
||||||
|
FillColor: drawing.ColorWhite,
|
||||||
|
StrokeWidth: 1,
|
||||||
|
StrokeColor: drawing.ColorWhite,
|
||||||
|
}).RoundedRect(Box{
|
||||||
|
Left: 10,
|
||||||
|
Right: 30,
|
||||||
|
Bottom: 150,
|
||||||
|
Top: 10,
|
||||||
|
}, 5)
|
||||||
|
buf, err := p.Bytes()
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 15 10\nL 25 10\nL 25 10\nA 5 5 90.00 0 1 30 15\nL 30 145\nL 30 145\nA 5 5 90.00 0 1 25 150\nL 15 150\nL 15 150\nA 5 5 90.00 0 1 10 145\nL 10 15\nL 10 15\nA 5 5 90.00 0 1 15 10\nZ\" style=\"stroke-width:1;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:1;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)\"/></svg>", string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
func TestPainterTextFit(t *testing.T) {
|
func TestPainterTextFit(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
p, err := NewPainter(PainterOptions{
|
p, err := NewPainter(PainterOptions{
|
||||||
|
|
|
||||||
237
pie_chart.go
237
pie_chart.go
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pieChart struct {
|
type pieChart struct {
|
||||||
|
|
@ -63,6 +63,96 @@ func NewPieChart(p *Painter, opt PieChartOption) *pieChart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sector struct {
|
||||||
|
value float64
|
||||||
|
percent float64
|
||||||
|
cx int
|
||||||
|
cy int
|
||||||
|
rx float64
|
||||||
|
ry float64
|
||||||
|
start float64
|
||||||
|
delta float64
|
||||||
|
offset int
|
||||||
|
quadrant int
|
||||||
|
lineStartX int
|
||||||
|
lineStartY int
|
||||||
|
lineBranchX int
|
||||||
|
lineBranchY int
|
||||||
|
lineEndX int
|
||||||
|
lineEndY int
|
||||||
|
showLabel bool
|
||||||
|
label string
|
||||||
|
series Series
|
||||||
|
color Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSector(cx int, cy int, radius float64, labelRadius float64, value float64, currentValue float64, totalValue float64, labelLineLength int, label string, series Series, color Color) sector {
|
||||||
|
s := sector{}
|
||||||
|
s.value = value
|
||||||
|
s.percent = value / totalValue
|
||||||
|
s.cx = cx
|
||||||
|
s.cy = cy
|
||||||
|
s.rx = radius
|
||||||
|
s.ry = radius
|
||||||
|
p := (currentValue + value/2) / totalValue
|
||||||
|
if p < 0.25 {
|
||||||
|
s.quadrant = 1
|
||||||
|
} else if p < 0.5 {
|
||||||
|
s.quadrant = 4
|
||||||
|
} else if p < 0.75 {
|
||||||
|
s.quadrant = 3
|
||||||
|
} else {
|
||||||
|
s.quadrant = 2
|
||||||
|
}
|
||||||
|
s.start = chart.PercentToRadians(currentValue/totalValue) - math.Pi/2
|
||||||
|
s.delta = chart.PercentToRadians(value / totalValue)
|
||||||
|
angle := s.start + s.delta/2
|
||||||
|
s.lineStartX = cx + int(radius*math.Cos(angle))
|
||||||
|
s.lineStartY = cy + int(radius*math.Sin(angle))
|
||||||
|
s.lineBranchX = cx + int(labelRadius*math.Cos(angle))
|
||||||
|
s.lineBranchY = cy + int(labelRadius*math.Sin(angle))
|
||||||
|
s.offset = labelLineLength
|
||||||
|
if s.lineBranchX <= cx {
|
||||||
|
s.offset *= -1
|
||||||
|
}
|
||||||
|
s.lineEndX = s.lineBranchX + s.offset
|
||||||
|
s.lineEndY = s.lineBranchY
|
||||||
|
s.series = series
|
||||||
|
s.color = color
|
||||||
|
s.showLabel = series.Label.Show
|
||||||
|
s.label = NewPieLabelFormatter([]string{label}, series.Label.Formatter)(0, s.value, s.percent)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sector) calculateY(prevY int) int {
|
||||||
|
for i := 0; i <= s.cy; i++ {
|
||||||
|
if s.quadrant <= 2 {
|
||||||
|
if (prevY - s.lineBranchY) > labelFontSize+5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.lineBranchY -= 1
|
||||||
|
} else {
|
||||||
|
if (s.lineBranchY - prevY) > labelFontSize+5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.lineBranchY += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.lineEndY = s.lineBranchY
|
||||||
|
return s.lineBranchY
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sector) calculateTextXY(textBox Box) (x int, y int) {
|
||||||
|
textMargin := 3
|
||||||
|
x = s.lineEndX + textMargin
|
||||||
|
y = s.lineEndY + textBox.Height()>>1 - 1
|
||||||
|
if s.offset < 0 {
|
||||||
|
textWidth := textBox.Width()
|
||||||
|
x = s.lineEndX - textWidth - textMargin
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
|
func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
|
||||||
opt := p.opt
|
opt := p.opt
|
||||||
values := make([]float64, len(seriesList))
|
values := make([]float64, len(seriesList))
|
||||||
|
|
@ -101,98 +191,103 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B
|
||||||
theme := opt.Theme
|
theme := opt.Theme
|
||||||
|
|
||||||
currentValue := float64(0)
|
currentValue := float64(0)
|
||||||
prevPoints := make([]Point, 0)
|
|
||||||
|
|
||||||
isOverride := func(x, y int) bool {
|
var quadrant1, quadrant2, quadrant3, quadrant4 []sector
|
||||||
for _, p := range prevPoints {
|
for index, v := range values {
|
||||||
if math.Abs(float64(p.Y-y)) > labelFontSize {
|
series := seriesList[index]
|
||||||
continue
|
color := theme.GetSeriesColor(index)
|
||||||
}
|
if index == len(values)-1 {
|
||||||
// label可能较多内容,不好计算横向占用空间
|
if color == theme.GetSeriesColor(0) {
|
||||||
// 因此x的位置需要中间位置两侧,否则认为override
|
color = theme.GetSeriesColor(1)
|
||||||
if (p.X <= cx && x <= cx) ||
|
|
||||||
(p.X > cx && x > cx) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
s := NewSector(cx, cy, radius, labelRadius, v, currentValue, total, labelLineWidth, seriesNames[index], series, color)
|
||||||
|
switch quadrant := s.quadrant; quadrant {
|
||||||
|
case 1:
|
||||||
|
quadrant1 = append([]sector{s}, quadrant1...)
|
||||||
|
case 2:
|
||||||
|
quadrant2 = append(quadrant2, s)
|
||||||
|
case 3:
|
||||||
|
quadrant3 = append([]sector{s}, quadrant3...)
|
||||||
|
case 4:
|
||||||
|
quadrant4 = append(quadrant4, s)
|
||||||
|
}
|
||||||
|
currentValue += v
|
||||||
}
|
}
|
||||||
|
sectors := append(quadrant1, quadrant4...)
|
||||||
|
sectors = append(sectors, quadrant3...)
|
||||||
|
sectors = append(sectors, quadrant2...)
|
||||||
|
|
||||||
for index, v := range values {
|
currentQuadrant := 0
|
||||||
|
prevY := 0
|
||||||
|
maxY := 0
|
||||||
|
minY := 0
|
||||||
|
for _, s := range sectors {
|
||||||
seriesPainter.OverrideDrawingStyle(Style{
|
seriesPainter.OverrideDrawingStyle(Style{
|
||||||
StrokeWidth: 1,
|
StrokeWidth: 1,
|
||||||
StrokeColor: theme.GetSeriesColor(index),
|
StrokeColor: s.color,
|
||||||
FillColor: theme.GetSeriesColor(index),
|
FillColor: s.color,
|
||||||
})
|
})
|
||||||
seriesPainter.MoveTo(cx, cy)
|
seriesPainter.MoveTo(s.cx, s.cy)
|
||||||
start := chart.PercentToRadians(currentValue/total) - math.Pi/2
|
seriesPainter.ArcTo(s.cx, s.cy, s.rx, s.ry, s.start, s.delta).LineTo(s.cx, s.cy).Close().FillStroke()
|
||||||
currentValue += v
|
if !s.showLabel {
|
||||||
percent := (v / total)
|
|
||||||
delta := chart.PercentToRadians(percent)
|
|
||||||
seriesPainter.ArcTo(cx, cy, radius, radius, start, delta).
|
|
||||||
LineTo(cx, cy).
|
|
||||||
Close().
|
|
||||||
FillStroke()
|
|
||||||
|
|
||||||
series := seriesList[index]
|
|
||||||
// 是否显示label
|
|
||||||
showLabel := series.Label.Show
|
|
||||||
if !showLabel {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if currentQuadrant != s.quadrant {
|
||||||
// label的角度为饼块中间
|
if s.quadrant == 1 {
|
||||||
angle := start + delta/2
|
minY = cy * 2
|
||||||
startx := cx + int(radius*math.Cos(angle))
|
maxY = 0
|
||||||
starty := cy + int(radius*math.Sin(angle))
|
prevY = cy * 2
|
||||||
|
|
||||||
endx := cx + int(labelRadius*math.Cos(angle))
|
|
||||||
endy := cy + int(labelRadius*math.Sin(angle))
|
|
||||||
// 计算是否有重叠,如果有则调整y坐标位置
|
|
||||||
// 最多只尝试5次
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
if !isOverride(endx, endy) {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
endy -= (labelFontSize << 1)
|
if s.quadrant == 2 {
|
||||||
|
if currentQuadrant != 3 {
|
||||||
|
prevY = s.lineEndY
|
||||||
|
} else {
|
||||||
|
prevY = minY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.quadrant == 3 {
|
||||||
|
if currentQuadrant != 4 {
|
||||||
|
prevY = s.lineEndY
|
||||||
|
} else {
|
||||||
|
minY = cy * 2
|
||||||
|
maxY = 0
|
||||||
|
prevY = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.quadrant == 4 {
|
||||||
|
if currentQuadrant != 1 {
|
||||||
|
prevY = s.lineEndY
|
||||||
|
} else {
|
||||||
|
prevY = maxY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentQuadrant = s.quadrant
|
||||||
}
|
}
|
||||||
prevPoints = append(prevPoints, Point{
|
prevY = s.calculateY(prevY)
|
||||||
X: endx,
|
if prevY > maxY {
|
||||||
Y: endy,
|
maxY = prevY
|
||||||
})
|
|
||||||
|
|
||||||
seriesPainter.MoveTo(startx, starty)
|
|
||||||
seriesPainter.LineTo(endx, endy)
|
|
||||||
offset := labelLineWidth
|
|
||||||
if endx < cx {
|
|
||||||
offset *= -1
|
|
||||||
}
|
}
|
||||||
seriesPainter.MoveTo(endx, endy)
|
if prevY < minY {
|
||||||
endx += offset
|
minY = prevY
|
||||||
seriesPainter.LineTo(endx, endy)
|
}
|
||||||
|
seriesPainter.MoveTo(s.lineStartX, s.lineStartY)
|
||||||
|
seriesPainter.LineTo(s.lineBranchX, s.lineBranchY)
|
||||||
|
seriesPainter.MoveTo(s.lineBranchX, s.lineBranchY)
|
||||||
|
seriesPainter.LineTo(s.lineEndX, s.lineEndY)
|
||||||
seriesPainter.Stroke()
|
seriesPainter.Stroke()
|
||||||
|
|
||||||
textStyle := Style{
|
textStyle := Style{
|
||||||
FontColor: theme.GetTextColor(),
|
FontColor: theme.GetTextColor(),
|
||||||
FontSize: labelFontSize,
|
FontSize: labelFontSize,
|
||||||
Font: opt.Font,
|
Font: opt.Font,
|
||||||
}
|
}
|
||||||
if !series.Label.Color.IsZero() {
|
if !s.series.Label.Color.IsZero() {
|
||||||
textStyle.FontColor = series.Label.Color
|
textStyle.FontColor = s.series.Label.Color
|
||||||
}
|
}
|
||||||
seriesPainter.OverrideTextStyle(textStyle)
|
seriesPainter.OverrideTextStyle(textStyle)
|
||||||
text := NewPieLabelFormatter(seriesNames, series.Label.Formatter)(index, v, percent)
|
x, y := s.calculateTextXY(seriesPainter.MeasureText(s.label))
|
||||||
textBox := seriesPainter.MeasureText(text)
|
seriesPainter.Text(s.label, x, y)
|
||||||
textMargin := 3
|
|
||||||
x := endx + textMargin
|
|
||||||
y := endy + textBox.Height()>>1 - 1
|
|
||||||
if offset < 0 {
|
|
||||||
textWidth := textBox.Width()
|
|
||||||
x = endx - textWidth - textMargin
|
|
||||||
}
|
|
||||||
seriesPainter.Text(text, x, y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.p.box, nil
|
return p.p.box, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -25,9 +25,10 @@ package charts
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type radarChart struct {
|
type radarChart struct {
|
||||||
|
|
@ -230,9 +231,15 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList)
|
||||||
StrokeColor: color,
|
StrokeColor: color,
|
||||||
FillColor: dotFillColor,
|
FillColor: dotFillColor,
|
||||||
})
|
})
|
||||||
for _, point := range linePoints {
|
for index, point := range linePoints {
|
||||||
seriesPainter.Circle(dotWith, point.X, point.Y)
|
seriesPainter.Circle(dotWith, point.X, point.Y)
|
||||||
seriesPainter.FillStroke()
|
seriesPainter.FillStroke()
|
||||||
|
if series.Label.Show && index < len(series.Data) {
|
||||||
|
value := humanize.FtoaWithDigits(series.Data[index].Value, 2)
|
||||||
|
b := seriesPainter.MeasureText(value)
|
||||||
|
seriesPainter.Text(value, point.X-b.Width()/2, point.Y)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
series.go
14
series.go
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SeriesData struct {
|
type SeriesData struct {
|
||||||
|
|
@ -83,6 +83,8 @@ type SeriesLabel struct {
|
||||||
Position string
|
Position string
|
||||||
// The offset of label's position
|
// The offset of label's position
|
||||||
Offset Box
|
Offset Box
|
||||||
|
// The font size of label
|
||||||
|
FontSize float64
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -124,6 +126,8 @@ type Series struct {
|
||||||
Name string
|
Name string
|
||||||
// Radius for Pie chart, e.g.: 40%, default is "40%"
|
// Radius for Pie chart, e.g.: 40%, default is "40%"
|
||||||
Radius string
|
Radius string
|
||||||
|
// Round for bar chart
|
||||||
|
RoundRadius int
|
||||||
// Mark point for series
|
// Mark point for series
|
||||||
MarkPoint SeriesMarkPoint
|
MarkPoint SeriesMarkPoint
|
||||||
// Make line for series
|
// Make line for series
|
||||||
|
|
@ -277,6 +281,14 @@ func NewPieLabelFormatter(seriesNames []string, layout string) LabelFormatter {
|
||||||
return NewLabelFormatter(seriesNames, layout)
|
return NewLabelFormatter(seriesNames, layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFunnelLabelFormatter returns a funner label formatter
|
||||||
|
func NewFunnelLabelFormatter(seriesNames []string, layout string) LabelFormatter {
|
||||||
|
if len(layout) == 0 {
|
||||||
|
layout = "{b}({d})"
|
||||||
|
}
|
||||||
|
return NewLabelFormatter(seriesNames, layout)
|
||||||
|
}
|
||||||
|
|
||||||
// NewValueLabelFormatter returns a value formatter
|
// NewValueLabelFormatter returns a value formatter
|
||||||
func NewValueLabelFormatter(seriesNames []string, layout string) LabelFormatter {
|
func NewValueLabelFormatter(seriesNames []string, layout string) LabelFormatter {
|
||||||
if len(layout) == 0 {
|
if len(layout) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2"
|
"git.smarteching.com/zeni/go-chart/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type labelRenderValue struct {
|
type labelRenderValue struct {
|
||||||
|
|
@ -45,8 +45,10 @@ type LabelValue struct {
|
||||||
Radians float64
|
Radians float64
|
||||||
// 字体颜色
|
// 字体颜色
|
||||||
FontColor Color
|
FontColor Color
|
||||||
Orient string
|
// 字体大小
|
||||||
Offset Box
|
FontSize float64
|
||||||
|
Orient string
|
||||||
|
Offset Box
|
||||||
}
|
}
|
||||||
|
|
||||||
type SeriesLabelPainter struct {
|
type SeriesLabelPainter struct {
|
||||||
|
|
@ -89,6 +91,9 @@ func (o *SeriesLabelPainter) Add(value LabelValue) {
|
||||||
FontSize: labelFontSize,
|
FontSize: labelFontSize,
|
||||||
Font: o.font,
|
Font: o.font,
|
||||||
}
|
}
|
||||||
|
if value.FontSize != 0 {
|
||||||
|
labelStyle.FontSize = value.FontSize
|
||||||
|
}
|
||||||
if !value.FontColor.IsZero() {
|
if !value.FontColor.IsZero() {
|
||||||
label.Color = value.FontColor
|
label.Color = value.FontColor
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
table.go
4
table.go
|
|
@ -26,8 +26,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tableChart struct {
|
type tableChart struct {
|
||||||
|
|
|
||||||
2
theme.go
2
theme.go
|
|
@ -24,7 +24,7 @@ package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"git.smarteching.com/zeni/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ThemeDark = "dark"
|
const ThemeDark = "dark"
|
||||||
|
|
|
||||||
4
util.go
4
util.go
|
|
@ -29,8 +29,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"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 TrueFlag() *bool {
|
func TrueFlag() *bool {
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"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 TestGetDefaultInt(t *testing.T) {
|
func TestGetDefaultInt(t *testing.T) {
|
||||||
|
|
|
||||||
11
xaxis.go
11
xaxis.go
|
|
@ -47,7 +47,13 @@ type XAxisOption struct {
|
||||||
// The line color of axis
|
// The line color of axis
|
||||||
StrokeColor Color
|
StrokeColor Color
|
||||||
// The color of label
|
// The color of label
|
||||||
FontColor Color
|
FontColor Color
|
||||||
|
// The text rotation of label
|
||||||
|
TextRotation float64
|
||||||
|
// The first axis
|
||||||
|
FirstAxis int
|
||||||
|
// The offset of label
|
||||||
|
LabelOffset Box
|
||||||
isValueAxis bool
|
isValueAxis bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,6 +87,9 @@ func (opt *XAxisOption) ToAxisOption() AxisOption {
|
||||||
FontColor: opt.FontColor,
|
FontColor: opt.FontColor,
|
||||||
Show: opt.Show,
|
Show: opt.Show,
|
||||||
SplitLineColor: opt.Theme.GetAxisSplitLineColor(),
|
SplitLineColor: opt.Theme.GetAxisSplitLineColor(),
|
||||||
|
TextRotation: opt.TextRotation,
|
||||||
|
LabelOffset: opt.LabelOffset,
|
||||||
|
FirstAxis: opt.FirstAxis,
|
||||||
}
|
}
|
||||||
if opt.isValueAxis {
|
if opt.isValueAxis {
|
||||||
axisOpt.SplitLineShow = true
|
axisOpt.SplitLineShow = true
|
||||||
|
|
|
||||||
5
yaxis.go
5
yaxis.go
|
|
@ -50,6 +50,8 @@ type YAxisOption struct {
|
||||||
DivideCount int
|
DivideCount int
|
||||||
Unit int
|
Unit int
|
||||||
isCategoryAxis bool
|
isCategoryAxis bool
|
||||||
|
// The flag for show axis split line, set this to true will show axis split line
|
||||||
|
SplitLineShow *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewYAxisOptions returns a y axis option
|
// NewYAxisOptions returns a y axis option
|
||||||
|
|
@ -100,6 +102,9 @@ func (opt *YAxisOption) ToAxisOption(p *Painter) AxisOption {
|
||||||
axisOpt.StrokeWidth = 1
|
axisOpt.StrokeWidth = 1
|
||||||
axisOpt.SplitLineShow = false
|
axisOpt.SplitLineShow = false
|
||||||
}
|
}
|
||||||
|
if opt.SplitLineShow != nil {
|
||||||
|
axisOpt.SplitLineShow = *opt.SplitLineShow
|
||||||
|
}
|
||||||
return axisOpt
|
return axisOpt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue