From 38c4978e44d8f92906d192041637d7ecad7c89d3 Mon Sep 17 00:00:00 2001 From: vicanso Date: Fri, 17 Jun 2022 23:37:21 +0800 Subject: [PATCH] refactor: enhance chart render function --- README.md | 32 ++-- bar_chart.go | 2 +- chart_option.go | 238 +++++++++++++++++++++++++- charts.go | 11 +- echarts.go | 18 ++ examples/bar_chart/main.go | 116 +++++-------- examples/charts/main.go | 59 +++++++ examples/funnel_chart/main.go | 62 ++----- examples/horizontal_bar_chart/main.go | 65 +++---- examples/line_chart/main.go | 135 +++++++-------- examples/pie_chart/main.go | 41 ++--- examples/radar_chart/main.go | 106 ++++-------- funnel_chart.go | 2 +- horizontal_bar_chart.go | 2 +- line_chart.go | 2 +- painter.go | 6 +- pie_chart.go | 2 +- radar_chart.go | 2 +- series.go | 8 + theme.go | 218 ++++++++++++----------- 20 files changed, 665 insertions(+), 462 deletions(-) diff --git a/README.md b/README.md index 22d3205..7affa30 100644 --- a/README.md +++ b/README.md @@ -49,25 +49,21 @@ func writeFile(file string, buf []byte) error { } func chartsRender() ([]byte, error) { - d, err := charts.LineRender([][]float64{ + values := [][]float64{ { - 150, + 120, + 132, + 101, + 134, + 90, 230, - 224, - 218, - 135, - 147, - 260, + 210, }, - }, - // output type - charts.PNGTypeOption(), - // title - charts.TitleOptionFunc(charts.TitleOption{ - Text: "Line", - }), - // x axis - charts.XAxisOptionFunc(charts.NewXAxisOption([]string{ + } + p, err := charts.LineRender( + values, + charts.TitleTextOptionFunc("Line"), + charts.XAxisDataOptionFunc([]string{ "Mon", "Tue", "Wed", @@ -75,12 +71,12 @@ func chartsRender() ([]byte, error) { "Fri", "Sat", "Sun", - })), + }), ) if err != nil { return nil, err } - return d.Bytes() + return p.Bytes() } func echartsRender() ([]byte, error) { diff --git a/bar_chart.go b/bar_chart.go index 8330542..2982829 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -34,7 +34,7 @@ type barChart struct { func NewBarChart(p *Painter, opt BarChartOption) *barChart { if opt.Theme == nil { - opt.Theme = NewTheme("") + opt.Theme = defaultTheme } return &barChart{ p: p, diff --git a/chart_option.go b/chart_option.go index 0cea754..0bc0a34 100644 --- a/chart_option.go +++ b/chart_option.go @@ -66,6 +66,182 @@ type ChartOption struct { Children []ChartOption } +// OptionFunc option function +type OptionFunc func(opt *ChartOption) + +// PNGTypeOption set png type of chart's output +func PNGTypeOption() OptionFunc { + return TypeOptionFunc(ChartOutputPNG) +} + +// TypeOptionFunc set type of chart's output +func TypeOptionFunc(t string) OptionFunc { + return func(opt *ChartOption) { + opt.Type = t + } +} + +// FontFamilyOptionFunc set font family of chart +func FontFamilyOptionFunc(fontFamily string) OptionFunc { + return func(opt *ChartOption) { + opt.FontFamily = fontFamily + } +} + +// ThemeOptionFunc set them of chart +func ThemeOptionFunc(theme string) OptionFunc { + return func(opt *ChartOption) { + opt.Theme = theme + } +} + +// TitleOptionFunc set title of chart +func TitleOptionFunc(title TitleOption) OptionFunc { + return func(opt *ChartOption) { + opt.Title = title + } +} + +// TitleTextOptionFunc set title text of chart +func TitleTextOptionFunc(text string) OptionFunc { + return func(opt *ChartOption) { + opt.Title.Text = text + } +} + +// LegendOptionFunc set legend of chart +func LegendOptionFunc(legend LegendOption) OptionFunc { + return func(opt *ChartOption) { + opt.Legend = legend + } +} + +// LegendLabelsOptionFunc set legend labels of chart +func LegendLabelsOptionFunc(labels []string, left ...string) OptionFunc { + return func(opt *ChartOption) { + opt.Legend = NewLegendOption(labels, left...) + } +} + +// XAxisOptionFunc set x axis of chart +func XAxisOptionFunc(xAxisOption XAxisOption) OptionFunc { + return func(opt *ChartOption) { + opt.XAxis = xAxisOption + } +} + +// XAxisDataOptionFunc set x axis data of chart +func XAxisDataOptionFunc(data []string, boundaryGap ...*bool) OptionFunc { + return func(opt *ChartOption) { + opt.XAxis = NewXAxisOption(data, boundaryGap...) + } +} + +// YAxisOptionFunc set y axis of chart, support two y axis +func YAxisOptionFunc(yAxisOption ...YAxisOption) OptionFunc { + return func(opt *ChartOption) { + opt.YAxisOptions = yAxisOption + } +} + +// YAxisDataOptionFunc set y axis data of chart +func YAxisDataOptionFunc(data []string) OptionFunc { + return func(opt *ChartOption) { + opt.YAxisOptions = NewYAxisOptions(data) + } +} + +// WidthOptionFunc set width of chart +func WidthOptionFunc(width int) OptionFunc { + return func(opt *ChartOption) { + opt.Width = width + } +} + +// HeightOptionFunc set height of chart +func HeightOptionFunc(height int) OptionFunc { + return func(opt *ChartOption) { + opt.Height = height + } +} + +// PaddingOptionFunc set padding of chart +func PaddingOptionFunc(padding Box) OptionFunc { + return func(opt *ChartOption) { + opt.Padding = padding + } +} + +// BoxOptionFunc set box of chart +func BoxOptionFunc(box Box) OptionFunc { + return func(opt *ChartOption) { + opt.Box = box + } +} + +// PieSeriesShowLabel set pie series show label +func PieSeriesShowLabel() OptionFunc { + return func(opt *ChartOption) { + for index := range opt.SeriesList { + opt.SeriesList[index].Label.Show = true + } + } +} + +// ChildOptionFunc add child chart +func ChildOptionFunc(child ...ChartOption) OptionFunc { + return func(opt *ChartOption) { + if opt.Children == nil { + opt.Children = make([]ChartOption, 0) + } + opt.Children = append(opt.Children, child...) + } +} + +// RadarIndicatorOptionFunc set radar indicator of chart +func RadarIndicatorOptionFunc(names []string, values []float64) OptionFunc { + return func(opt *ChartOption) { + if len(names) != len(values) { + return + } + indicators := make([]RadarIndicator, len(names)) + for index, name := range names { + indicators[index] = RadarIndicator{ + Name: name, + Max: values[index], + } + } + opt.RadarIndicators = indicators + } +} + +// BackgroundColorOptionFunc set background color of chart +func BackgroundColorOptionFunc(color Color) OptionFunc { + return func(opt *ChartOption) { + opt.BackgroundColor = color + } +} + +// MarkLineOptionFunc set mark line for series of chart +func MarkLineOptionFunc(seriesIndex int, markLineTypes ...string) OptionFunc { + return func(opt *ChartOption) { + if len(opt.SeriesList) <= seriesIndex { + return + } + opt.SeriesList[seriesIndex].MarkLine = NewMarkLine(markLineTypes...) + } +} + +// MarkPointOptionFunc set mark point for series of chart +func MarkPointOptionFunc(seriesIndex int, markPointTypes ...string) OptionFunc { + return func(opt *ChartOption) { + if len(opt.SeriesList) <= seriesIndex { + return + } + opt.SeriesList[seriesIndex].MarkPoint = NewMarkPoint(markPointTypes...) + } +} + func (o *ChartOption) fillDefault() { t := NewTheme(o.Theme) o.theme = t @@ -90,11 +266,11 @@ func (o *ChartOption) fillDefault() { o.BackgroundColor = t.GetBackgroundColor() } if o.Padding.IsZero() { - o.Padding = chart.Box{ - Top: 10, - Right: 10, - Bottom: 10, - Left: 10, + o.Padding = Box{ + Top: 20, + Right: 20, + Bottom: 20, + Left: 20, } } // legend与series name的关联 @@ -118,3 +294,55 @@ func (o *ChartOption) fillDefault() { }) } } + +// LineRender line chart render +func LineRender(values [][]float64, opts ...OptionFunc) (*Painter, error) { + seriesList := NewSeriesListDataFromValues(values, ChartTypeLine) + return Render(ChartOption{ + SeriesList: seriesList, + }, opts...) +} + +// BarRender bar chart render +func BarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) { + seriesList := NewSeriesListDataFromValues(values, ChartTypeBar) + return Render(ChartOption{ + SeriesList: seriesList, + }, opts...) +} + +// HorizontalBarRender horizontal bar chart render +func HorizontalBarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) { + seriesList := NewSeriesListDataFromValues(values, ChartTypeHorizontalBar) + return Render(ChartOption{ + SeriesList: seriesList, + }, opts...) +} + +// PieRender pie chart render +func PieRender(values []float64, opts ...OptionFunc) (*Painter, error) { + return Render(ChartOption{ + SeriesList: NewPieSeriesList(values), + }, opts...) +} + +// RadarRender radar chart render +func RadarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) { + seriesList := NewSeriesListDataFromValues(values, ChartTypeRadar) + return Render(ChartOption{ + SeriesList: seriesList, + }, opts...) +} + +// FunnelRender funnel chart render +func FunnelRender(values []float64, opts ...OptionFunc) (*Painter, error) { + seriesList := make(SeriesList, len(values)) + for index, value := range values { + seriesList[index] = NewSeriesFromValues([]float64{ + value, + }, ChartTypeFunnel) + } + return Render(ChartOption{ + SeriesList: seriesList, + }, opts...) +} diff --git a/charts.go b/charts.go index 51e247a..5759367 100644 --- a/charts.go +++ b/charts.go @@ -239,7 +239,10 @@ func doRender(renderers ...Renderer) error { return nil } -func Render(opt ChartOption) (*Painter, error) { +func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) { + for _, fn := range opts { + fn(&opt) + } opt.fillDefault() isChild := true @@ -398,6 +401,12 @@ func Render(opt ChartOption) (*Painter, error) { } for _, item := range opt.Children { item.Parent = p + if item.Theme == "" { + item.Theme = opt.Theme + } + if item.FontFamily == "" { + item.FontFamily = opt.FontFamily + } _, err = Render(item) if err != nil { return nil, err diff --git a/echarts.go b/echarts.go index ac28436..d2602b3 100644 --- a/echarts.go +++ b/echarts.go @@ -130,6 +130,7 @@ type EChartsXAxisData struct { BoundaryGap *bool `json:"boundaryGap"` SplitNumber int `json:"splitNumber"` Data []string `json:"data"` + Type string `json:"type"` } type EChartsXAxis struct { Data []EChartsXAxisData @@ -155,6 +156,7 @@ type EChartsYAxisData struct { Color string `json:"color"` } `json:"lineStyle"` } `json:"axisLine"` + Data []string `json:"data"` } type EChartsYAxis struct { Data []EChartsYAxisData `json:"data"` @@ -453,6 +455,21 @@ func (eo *EChartsOption) ToOption() ChartOption { Box: eo.Box, SeriesList: eo.Series.ToSeriesList(), } + isHorizontalChart := false + for _, item := range eo.XAxis.Data { + if item.Type == "value" { + isHorizontalChart = true + } + } + if isHorizontalChart { + for index := range o.SeriesList { + series := o.SeriesList[index] + if series.Type == ChartTypeBar { + o.SeriesList[index].Type = ChartTypeHorizontalBar + } + } + } + if len(eo.XAxis.Data) != 0 { xAxisData := eo.XAxis.Data[0] o.XAxis = XAxisOption{ @@ -468,6 +485,7 @@ func (eo *EChartsOption) ToOption() ChartOption { Max: item.Max, Formatter: item.AxisLabel.Formatter, Color: parseColor(item.AxisLine.LineStyle.Color), + Data: item.Data, } } o.YAxisOptions = yAxisOptions diff --git a/examples/bar_chart/main.go b/examples/bar_chart/main.go index 5d5da2a..c9f1d58 100644 --- a/examples/bar_chart/main.go +++ b/examples/bar_chart/main.go @@ -24,26 +24,39 @@ func writeFile(buf []byte) error { } func main() { - p, err := charts.NewPainter(charts.PainterOptions{ - Width: 800, - Height: 600, - Type: charts.ChartOutputPNG, - }) - if err != nil { - panic(err) + values := [][]float64{ + { + 2.0, + 4.9, + 7.0, + 23.2, + 25.6, + 76.7, + 135.6, + 162.2, + 32.6, + 20.0, + 6.4, + 3.3, + }, + { + 2.6, + 5.9, + 9.0, + 26.4, + 28.7, + 70.7, + 175.6, + 182.2, + 48.7, + 18.8, + 6.0, + 2.3, + }, } - _, err = charts.NewBarChart(p, charts.BarChartOption{ - Title: charts.TitleOption{ - Text: "Rainfall vs Evaporation", - Subtext: "Fake Data", - }, - Padding: charts.Box{ - Top: 20, - Right: 20, - Bottom: 20, - Left: 20, - }, - XAxis: charts.NewXAxisOption([]string{ + p, err := charts.BarRender( + values, + charts.XAxisDataOptionFunc([]string{ "Jan", "Feb", "Mar", @@ -57,61 +70,24 @@ func main() { "Nov", "Dec", }), - Legend: charts.NewLegendOption([]string{ + charts.LegendLabelsOptionFunc([]string{ "Rainfall", "Evaporation", }, charts.PositionRight), - SeriesList: []charts.Series{ - { - Type: charts.ChartTypeBar, - Data: charts.NewSeriesDataFromValues([]float64{ - 2.0, - 4.9, - 7.0, - 23.2, - 25.6, - 76.7, - 135.6, - 162.2, - 32.6, - 20.0, - 6.4, - 3.3, - }), - MarkPoint: charts.NewMarkPoint( - charts.SeriesMarkDataTypeMax, - charts.SeriesMarkDataTypeMin, - ), - MarkLine: charts.NewMarkLine( - charts.SeriesMarkDataTypeAverage, - ), - }, - { - Type: charts.ChartTypeBar, - Data: charts.NewSeriesDataFromValues([]float64{ - 2.6, - 5.9, - 9.0, - 26.4, - 28.7, - 70.7, - 175.6, - 182.2, - 48.7, - 18.8, - 6.0, - 2.3, - }), - MarkPoint: charts.NewMarkPoint( - charts.SeriesMarkDataTypeMax, - charts.SeriesMarkDataTypeMin, - ), - MarkLine: charts.NewMarkLine( - charts.SeriesMarkDataTypeAverage, - ), - }, + charts.MarkLineOptionFunc(0, charts.SeriesMarkDataTypeAverage), + charts.MarkPointOptionFunc(0, charts.SeriesMarkDataTypeMax, + charts.SeriesMarkDataTypeMin), + // custom option func + func(opt *charts.ChartOption) { + opt.SeriesList[1].MarkPoint = charts.NewMarkPoint( + charts.SeriesMarkDataTypeMax, + charts.SeriesMarkDataTypeMin, + ) + opt.SeriesList[1].MarkLine = charts.NewMarkLine( + charts.SeriesMarkDataTypeAverage, + ) }, - }).Render() + ) if err != nil { panic(err) } diff --git a/examples/charts/main.go b/examples/charts/main.go index 3a625f7..b370b69 100644 --- a/examples/charts/main.go +++ b/examples/charts/main.go @@ -73,6 +73,7 @@ func handler(w http.ResponseWriter, req *http.Request, chartOptions []charts.Cha bytesList := make([][]byte, 0) for _, opt := range chartOptions { opt.Theme = theme + opt.Type = charts.ChartOutputSVG d, err := charts.Render(opt) if err != nil { panic(err) @@ -1055,6 +1056,64 @@ func echartsHandler(w http.ResponseWriter, req *http.Request) { } ] }`, + `{ + "title": { + "text": "World Population" + }, + "tooltip": { + "trigger": "axis", + "axisPointer": { + "type": "shadow" + } + }, + "legend": {}, + "grid": { + "left": "3%", + "right": "4%", + "bottom": "3%", + "containLabel": true + }, + "xAxis": { + "type": "value" + }, + "yAxis": { + "type": "category", + "data": [ + "Brazil", + "Indonesia", + "USA", + "India", + "China", + "World" + ] + }, + "series": [ + { + "name": "2011", + "type": "bar", + "data": [ + 18203, + 23489, + 29034, + 104970, + 131744, + 630230 + ] + }, + { + "name": "2012", + "type": "bar", + "data": [ + 19325, + 23438, + 31000, + 121594, + 134141, + 681807 + ] + } + ] + }`, `{ "title": { "text": "Rainfall vs Evaporation", diff --git a/examples/funnel_chart/main.go b/examples/funnel_chart/main.go index eb753fd..6b17614 100644 --- a/examples/funnel_chart/main.go +++ b/examples/funnel_chart/main.go @@ -24,64 +24,24 @@ func writeFile(buf []byte) error { } func main() { - p, err := charts.NewPainter(charts.PainterOptions{ - Width: 800, - Height: 600, - Type: charts.ChartOutputPNG, - }) - if err != nil { - panic(err) + values := []float64{ + 100, + 80, + 60, + 40, + 20, } - _, err = charts.NewFunnelChart(p, charts.FunnelChartOption{ - Title: charts.TitleOption{ - Text: "Funnel", - }, - Legend: charts.NewLegendOption([]string{ + p, err := charts.FunnelRender( + values, + charts.TitleTextOptionFunc("Funnel"), + charts.LegendLabelsOptionFunc([]string{ "Show", "Click", "Visit", "Inquiry", "Order", }), - SeriesList: []charts.Series{ - - { - Type: charts.ChartTypeFunnel, - Name: "Show", - Data: charts.NewSeriesDataFromValues([]float64{ - 100, - }), - }, - { - Type: charts.ChartTypeFunnel, - Name: "Click", - Data: charts.NewSeriesDataFromValues([]float64{ - 80, - }), - }, - { - Type: charts.ChartTypeFunnel, - Name: "Visit", - Data: charts.NewSeriesDataFromValues([]float64{ - 60, - }), - }, - { - Type: charts.ChartTypeFunnel, - Name: "Inquiry", - Data: charts.NewSeriesDataFromValues([]float64{ - 40, - }), - }, - { - Type: charts.ChartTypeFunnel, - Name: "Order", - Data: charts.NewSeriesDataFromValues([]float64{ - 20, - }), - }, - }, - }).Render() + ) if err != nil { panic(err) } diff --git a/examples/horizontal_bar_chart/main.go b/examples/horizontal_bar_chart/main.go index eecd9ec..6b206b0 100644 --- a/examples/horizontal_bar_chart/main.go +++ b/examples/horizontal_bar_chart/main.go @@ -24,29 +24,38 @@ func writeFile(buf []byte) error { } func main() { - p, err := charts.NewPainter(charts.PainterOptions{ - Width: 800, - Height: 600, - Type: charts.ChartOutputPNG, - }) - if err != nil { - panic(err) - } - _, err = charts.NewHorizontalBarChart(p, charts.HorizontalBarChartOption{ - Title: charts.TitleOption{ - Text: "World Population", + values := [][]float64{ + { + 18203, + 23489, + 29034, + 104970, + 131744, + 630230, }, - Padding: charts.Box{ + { + 19325, + 23438, + 31000, + 121594, + 134141, + 681807, + }, + } + p, err := charts.HorizontalBarRender( + values, + charts.TitleTextOptionFunc("World Population"), + charts.PaddingOptionFunc(charts.Box{ Top: 20, Right: 40, Bottom: 20, Left: 20, - }, - Legend: charts.NewLegendOption([]string{ + }), + charts.LegendLabelsOptionFunc([]string{ "2011", "2012", }), - YAxisOptions: charts.NewYAxisOptions([]string{ + charts.YAxisDataOptionFunc([]string{ "Brazil", "Indonesia", "USA", @@ -54,31 +63,7 @@ func main() { "China", "World", }), - SeriesList: []charts.Series{ - { - Type: charts.ChartTypeHorizontalBar, - Data: charts.NewSeriesDataFromValues([]float64{ - 18203, - 23489, - 29034, - 104970, - 131744, - 630230, - }), - }, - { - Type: charts.ChartTypeHorizontalBar, - Data: charts.NewSeriesDataFromValues([]float64{ - 19325, - 23438, - 31000, - 121594, - 134141, - 681807, - }), - }, - }, - }).Render() + ) if err != nil { panic(err) } diff --git a/examples/line_chart/main.go b/examples/line_chart/main.go index 414f676..435da78 100644 --- a/examples/line_chart/main.go +++ b/examples/line_chart/main.go @@ -24,35 +24,57 @@ func writeFile(buf []byte) error { } func main() { - p, err := charts.NewPainter(charts.PainterOptions{ - Width: 800, - Height: 600, - Type: charts.ChartOutputPNG, - }) - if err != nil { - panic(err) + values := [][]float64{ + { + 120, + 132, + 101, + 134, + 90, + 230, + 210, + }, + { + 220, + 182, + 191, + 234, + 290, + 330, + 310, + }, + { + 150, + 232, + 201, + 154, + 190, + 330, + 410, + }, + { + 320, + 332, + 301, + 334, + 390, + 330, + 320, + }, + { + 820, + 932, + 901, + 934, + 1290, + 1330, + 1320, + }, } - _, err = charts.NewLineChart(p, charts.LineChartOption{ - Padding: charts.Box{ - Left: 10, - Top: 10, - Right: 10, - Bottom: 10, - }, - Title: charts.TitleOption{ - Text: "Line", - }, - Legend: charts.LegendOption{ - Data: []string{ - "Email", - "Union Ads", - "Video Ads", - "Direct", - "Search Engine", - }, - Left: charts.PositionCenter, - }, - XAxis: charts.NewXAxisOption([]string{ + p, err := charts.LineRender( + values, + charts.TitleTextOptionFunc("Line"), + charts.XAxisDataOptionFunc([]string{ "Mon", "Tue", "Wed", @@ -61,54 +83,15 @@ func main() { "Sat", "Sun", }), - SeriesList: charts.SeriesList{ - charts.NewSeriesFromValues([]float64{ - 120, - 132, - 101, - 134, - 90, - 230, - 210, - }), - charts.NewSeriesFromValues([]float64{ - 220, - 182, - 191, - 234, - 290, - 330, - 310, - }), - charts.NewSeriesFromValues([]float64{ - 150, - 232, - 201, - 154, - 190, - 330, - 410, - }), - charts.NewSeriesFromValues([]float64{ - 320, - 332, - 301, - 334, - 390, - 330, - 320, - }), - charts.NewSeriesFromValues([]float64{ - 820, - 932, - 901, - 934, - 1290, - 1330, - 1320, - }), - }, - }).Render() + charts.LegendLabelsOptionFunc([]string{ + "Email", + "Union Ads", + "Video Ads", + "Direct", + "Search Engine", + }, charts.PositionCenter), + ) + if err != nil { panic(err) } diff --git a/examples/pie_chart/main.go b/examples/pie_chart/main.go index e69bf60..8a98e57 100644 --- a/examples/pie_chart/main.go +++ b/examples/pie_chart/main.go @@ -24,27 +24,27 @@ func writeFile(buf []byte) error { } func main() { - p, err := charts.NewPainter(charts.PainterOptions{ - Width: 800, - Height: 600, - Type: charts.ChartOutputPNG, - }) - if err != nil { - panic(err) + values := []float64{ + 1048, + 735, + 580, + 484, + 300, } - _, err = charts.NewPieChart(p, charts.PieChartOption{ - Title: charts.TitleOption{ + p, err := charts.PieRender( + values, + charts.TitleOptionFunc(charts.TitleOption{ Text: "Rainfall vs Evaporation", Subtext: "Fake Data", Left: charts.PositionCenter, - }, - Padding: charts.Box{ + }), + charts.PaddingOptionFunc(charts.Box{ Top: 20, Right: 20, Bottom: 20, Left: 20, - }, - Legend: charts.LegendOption{ + }), + charts.LegendOptionFunc(charts.LegendOption{ Orient: charts.OrientVertical, Data: []string{ "Search Engine", @@ -54,20 +54,9 @@ func main() { "Video Ads", }, Left: charts.PositionLeft, - }, - SeriesList: charts.NewPieSeriesList([]float64{ - 1048, - 735, - 580, - 484, - 300, - }, charts.PieSeriesOption{ - Label: charts.SeriesLabel{ - Show: true, - }, - Radius: "35%", }), - }).Render() + charts.PieSeriesShowLabel(), + ) if err != nil { panic(err) } diff --git a/examples/radar_chart/main.go b/examples/radar_chart/main.go index 077fa48..9550951 100644 --- a/examples/radar_chart/main.go +++ b/examples/radar_chart/main.go @@ -24,79 +24,47 @@ func writeFile(buf []byte) error { } func main() { - p, err := charts.NewPainter(charts.PainterOptions{ - Width: 800, - Height: 600, - Type: charts.ChartOutputPNG, - }) - if err != nil { - panic(err) + values := [][]float64{ + { + 4200, + 3000, + 20000, + 35000, + 50000, + 18000, + }, + { + 5000, + 14000, + 28000, + 26000, + 42000, + 21000, + }, } - _, err = charts.NewRadarChart(p, charts.RadarChartOption{ - Padding: charts.Box{ - Left: 10, - Top: 10, - Right: 10, - Bottom: 10, - }, - Title: charts.TitleOption{ - Text: "Basic Radar Chart", - }, - Legend: charts.NewLegendOption([]string{ + p, err := charts.RadarRender( + values, + charts.TitleTextOptionFunc("Basic Radar Chart"), + charts.LegendLabelsOptionFunc([]string{ "Allocated Budget", "Actual Spending", }), - RadarIndicators: []charts.RadarIndicator{ - { - Name: "Sales", - Max: 6500, - }, - { - Name: "Administration", - Max: 16000, - }, - { - Name: "Information Technology", - Max: 30000, - }, - { - Name: "Customer Support", - Max: 38000, - }, - { - Name: "Development", - Max: 52000, - }, - { - Name: "Marketing", - Max: 25000, - }, - }, - SeriesList: charts.SeriesList{ - { - Type: charts.ChartTypeRadar, - Data: charts.NewSeriesDataFromValues([]float64{ - 4200, - 3000, - 20000, - 35000, - 50000, - 18000, - }), - }, - { - Type: charts.ChartTypeRadar, - Data: charts.NewSeriesDataFromValues([]float64{ - 5000, - 14000, - 28000, - 26000, - 42000, - 21000, - }), - }, - }, - }).Render() + charts.RadarIndicatorOptionFunc([]string{ + "Sales", + "Administration", + "Information Technology", + "Customer Support", + "Development", + "Marketing", + }, []float64{ + 6500, + 16000, + 30000, + 38000, + 52000, + 25000, + }), + ) if err != nil { panic(err) } diff --git a/funnel_chart.go b/funnel_chart.go index c8457dd..63b3504 100644 --- a/funnel_chart.go +++ b/funnel_chart.go @@ -36,7 +36,7 @@ type funnelChart struct { func NewFunnelChart(p *Painter, opt FunnelChartOption) *funnelChart { if opt.Theme == nil { - opt.Theme = NewTheme("") + opt.Theme = defaultTheme } return &funnelChart{ p: p, diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index c98d688..fb23734 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -52,7 +52,7 @@ type HorizontalBarChartOption struct { func NewHorizontalBarChart(p *Painter, opt HorizontalBarChartOption) *horizontalBarChart { if opt.Theme == nil { - opt.Theme = NewTheme("") + opt.Theme = defaultTheme } return &horizontalBarChart{ p: p, diff --git a/line_chart.go b/line_chart.go index c505a91..0dc0fd8 100644 --- a/line_chart.go +++ b/line_chart.go @@ -34,7 +34,7 @@ type lineChart struct { func NewLineChart(p *Painter, opt LineChartOption) *lineChart { if opt.Theme == nil { - opt.Theme = NewTheme("") + opt.Theme = defaultTheme } return &lineChart{ p: p, diff --git a/painter.go b/painter.go index 5a8dd89..0bacd3c 100644 --- a/painter.go +++ b/painter.go @@ -149,9 +149,9 @@ func NewPainter(opts PainterOptions, opt ...PainterOption) (*Painter, error) { } font = f } - fn := chart.SVG - if opts.Type == ChartOutputPNG { - fn = chart.PNG + fn := chart.PNG + if opts.Type == ChartOutputSVG { + fn = chart.SVG } width := opts.Width height := opts.Height diff --git a/pie_chart.go b/pie_chart.go index c5a2ff2..972b4c1 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -53,7 +53,7 @@ type PieChartOption struct { func NewPieChart(p *Painter, opt PieChartOption) *pieChart { if opt.Theme == nil { - opt.Theme = NewTheme("") + opt.Theme = defaultTheme } return &pieChart{ p: p, diff --git a/radar_chart.go b/radar_chart.go index dc93ca8..610d5f7 100644 --- a/radar_chart.go +++ b/radar_chart.go @@ -64,7 +64,7 @@ type RadarChartOption struct { func NewRadarChart(p *Painter, opt RadarChartOption) *radarChart { if opt.Theme == nil { - opt.Theme = NewTheme("") + opt.Theme = defaultTheme } return &radarChart{ p: p, diff --git a/series.go b/series.go index 4808bcb..905c140 100644 --- a/series.go +++ b/series.go @@ -36,6 +36,14 @@ type SeriesData struct { Style Style } +func NewSeriesListDataFromValues(values [][]float64, chartType ...string) SeriesList { + seriesList := make(SeriesList, len(values)) + for index, value := range values { + seriesList[index] = NewSeriesFromValues(value, chartType...) + } + return seriesList +} + func NewSeriesFromValues(values []float64, chartType ...string) Series { s := Series{ Data: NewSeriesDataFromValues(values), diff --git a/theme.go b/theme.go index 544588a..26786b9 100644 --- a/theme.go +++ b/theme.go @@ -55,10 +55,21 @@ type themeColorPalette struct { font *truetype.Font } +type ThemeOption struct { + IsDarkMode bool + AxisStrokeColor Color + AxisSplitLineColor Color + BackgroundColor Color + TextColor Color + SeriesColors []Color +} + var palettes = map[string]ColorPalette{} const defaultFontSize = 12.0 +var defaultTheme ColorPalette + func init() { echartSeriesColors := []Color{ parseColor("#5470c6"), @@ -93,121 +104,134 @@ func init() { } AddTheme( ThemeDark, - true, - Color{ - R: 185, - G: 184, - B: 206, - A: 255, + ThemeOption{ + IsDarkMode: true, + AxisStrokeColor: Color{ + R: 185, + G: 184, + B: 206, + A: 255, + }, + AxisSplitLineColor: Color{ + R: 72, + G: 71, + B: 83, + A: 255, + }, + BackgroundColor: Color{ + R: 16, + G: 12, + B: 42, + A: 255, + }, + TextColor: Color{ + R: 238, + G: 238, + B: 238, + A: 255, + }, + SeriesColors: echartSeriesColors, }, - Color{ - R: 72, - G: 71, - B: 83, - A: 255, - }, - Color{ - R: 16, - G: 12, - B: 42, - A: 255, - }, - Color{ - R: 238, - G: 238, - B: 238, - A: 255, - }, - echartSeriesColors, ) AddTheme( ThemeLight, - false, - Color{ - R: 110, - G: 112, - B: 121, - A: 255, + ThemeOption{ + IsDarkMode: false, + AxisStrokeColor: Color{ + R: 110, + G: 112, + B: 121, + A: 255, + }, + AxisSplitLineColor: Color{ + R: 224, + G: 230, + B: 242, + A: 255, + }, + BackgroundColor: drawing.ColorWhite, + TextColor: Color{ + R: 70, + G: 70, + B: 70, + A: 255, + }, + SeriesColors: echartSeriesColors, }, - Color{ - R: 224, - G: 230, - B: 242, - A: 255, - }, - drawing.ColorWhite, - drawing.Color{ - R: 70, - G: 70, - B: 70, - A: 255, - }, - echartSeriesColors, ) AddTheme( ThemeAnt, - false, - Color{ - R: 110, - G: 112, - B: 121, - A: 255, + ThemeOption{ + IsDarkMode: false, + AxisStrokeColor: Color{ + R: 110, + G: 112, + B: 121, + A: 255, + }, + AxisSplitLineColor: Color{ + R: 224, + G: 230, + B: 242, + A: 255, + }, + BackgroundColor: drawing.ColorWhite, + TextColor: drawing.Color{ + R: 70, + G: 70, + B: 70, + A: 255, + }, + SeriesColors: antSeriesColors, }, - Color{ - R: 224, - G: 230, - B: 242, - A: 255, - }, - drawing.ColorWhite, - drawing.Color{ - R: 70, - G: 70, - B: 70, - A: 255, - }, - antSeriesColors, ) AddTheme( ThemeGrafana, - true, - drawing.Color{ - R: 185, - G: 184, - B: 206, - A: 255, + ThemeOption{ + IsDarkMode: true, + AxisStrokeColor: Color{ + R: 185, + G: 184, + B: 206, + A: 255, + }, + AxisSplitLineColor: Color{ + R: 68, + G: 67, + B: 67, + A: 255, + }, + BackgroundColor: drawing.Color{ + R: 31, + G: 29, + B: 29, + A: 255, + }, + TextColor: Color{ + R: 216, + G: 217, + B: 218, + A: 255, + }, + SeriesColors: grafanaSeriesColors, }, - drawing.Color{ - R: 68, - G: 67, - B: 67, - A: 255, - }, - drawing.Color{ - R: 31, - G: 29, - B: 29, - A: 255, - }, - drawing.Color{ - R: 216, - G: 217, - B: 218, - A: 255, - }, - grafanaSeriesColors, ) + SetDefaultTheme(ThemeLight) } -func AddTheme(name string, isDarkMode bool, axisStrokeColor, axisSplitLineColor, backgroundColor, textColor drawing.Color, seriesColors []drawing.Color) { +func SetDefaultTheme(name string) { + defaultTheme = NewTheme(name) +} + +func AddTheme(name string, opt ThemeOption) { palettes[name] = &themeColorPalette{ - isDarkMode: isDarkMode, - axisStrokeColor: axisStrokeColor, - axisSplitLineColor: axisSplitLineColor, - backgroundColor: backgroundColor, - textColor: textColor, - seriesColors: seriesColors, + isDarkMode: opt.IsDarkMode, + axisStrokeColor: opt.AxisStrokeColor, + axisSplitLineColor: opt.AxisSplitLineColor, + backgroundColor: opt.BackgroundColor, + textColor: opt.TextColor, + seriesColors: opt.SeriesColors, } }