From 6ae7e1d1b36bbbcc8993c01694f1e98740bbba09 Mon Sep 17 00:00:00 2001 From: vicanso Date: Tue, 1 Feb 2022 11:19:31 +0800 Subject: [PATCH] feat: support bar chart --- bar_chart.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ chart.go | 11 ++++++- line_chart.go | 10 ++---- range.go | 10 ++++++ yaxis.go | 5 +++ 5 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 bar_chart.go diff --git a/bar_chart.go b/bar_chart.go new file mode 100644 index 0000000..61fd8f4 --- /dev/null +++ b/bar_chart.go @@ -0,0 +1,84 @@ +// MIT License + +// Copyright (c) 2022 Tree Xie + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package charts + +import ( + "github.com/wcharczuk/go-chart/v2" +) + +func BarChartRender(opt ChartOption) (*Draw, error) { + result, err := chartBasicRender(&opt) + if err != nil { + return nil, err + } + d := result.d + + bd, err := NewDraw(DrawOption{ + Parent: d, + }, PaddingOption(chart.Box{ + Top: result.titleBox.Height(), + Left: YAxisWidth, + })) + if err != nil { + return nil, err + } + yRange := result.yRange + xRange := result.xRange + x0, x1 := xRange.GetRange(0) + width := int(x1 - x0) + // 每一块之间的margin + margin := 10 + // 每一个bar之间的margin + barMargin := 5 + + seriesCount := len(opt.SeriesList) + // 总的宽度-两个margin-(总数-1)的barMargin + barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / len(opt.SeriesList) + + barMaxHeight := yRange.Size + theme := NewTheme(opt.Theme) + + for i, series := range opt.SeriesList { + for j, item := range series.Data { + x0, _ := xRange.GetRange(j) + x := int(x0) + x += margin + if i != 0 { + x += i * (barWidth + barMargin) + } + + h := int(yRange.getHeight(item.Value)) + + bd.Bar(chart.Box{ + Top: barMaxHeight - h, + Left: x, + Right: x + barWidth, + Bottom: barMaxHeight - 1, + }, BarStyle{ + FillColor: theme.GetSeriesColor(i), + }) + } + } + + return d, nil +} diff --git a/chart.go b/chart.go index 8478757..0a067b1 100644 --- a/chart.go +++ b/chart.go @@ -51,6 +51,7 @@ type ChartOption struct { Title TitleOption Legend LegendOption XAxis XAxisOption + YAxis YAxisOption Width int Height int Parent *Draw @@ -128,8 +129,16 @@ func (o *ChartOption) getYRange(axisIndex int) Range { } } } + min = min * 0.9 + max = max * 1.1 + if o.YAxis.Min != nil { + min = *o.YAxis.Min + } + if o.YAxis.Max != nil { + max = *o.YAxis.Max + } // y轴分设置默认划分为6块 - r := NewRange(min*0.9, max*1.1, 6) + r := NewRange(min, max, 6) return r } diff --git a/line_chart.go b/line_chart.go index 331ebcc..5b8ba1b 100644 --- a/line_chart.go +++ b/line_chart.go @@ -27,12 +27,8 @@ import ( "github.com/wcharczuk/go-chart/v2/drawing" ) -type LineChartOption struct { - ChartOption -} - -func NewLineChart(opt LineChartOption) (*Draw, error) { - result, err := chartBasicRender(&opt.ChartOption) +func LineChartRender(opt ChartOption) (*Draw, error) { + result, err := chartBasicRender(&opt) if err != nil { return nil, err } @@ -54,7 +50,7 @@ func NewLineChart(opt LineChartOption) (*Draw, error) { for i, series := range opt.SeriesList { points := make([]Point, 0) for j, item := range series.Data { - y := yRange.getHeight(item.Value) + y := yRange.getRestHeight(item.Value) points = append(points, Point{ Y: y, X: xRange.getWidth(float64(j)), diff --git a/range.go b/range.go index c2faf8b..57dbd20 100644 --- a/range.go +++ b/range.go @@ -60,10 +60,20 @@ func NewRange(min, max float64, divideCount int) Range { } func (r *Range) getHeight(value float64) int { + v := (value - r.Min) / (r.Max - r.Min) + return int(v * float64(r.Size)) +} + +func (r *Range) getRestHeight(value float64) int { v := 1 - (value-r.Min)/(r.Max-r.Min) return int(v * float64(r.Size)) } +func (r *Range) GetRange(index int) (float64, float64) { + unit := float64(r.Size) / float64(r.divideCount) + return unit * float64(index), unit * float64(index+1) +} + func (r *Range) getWidth(value float64) int { v := value / (r.Max - r.Min) // 移至居中 diff --git a/yaxis.go b/yaxis.go index 6cef270..7329804 100644 --- a/yaxis.go +++ b/yaxis.go @@ -26,6 +26,11 @@ import ( "github.com/wcharczuk/go-chart/v2" ) +type YAxisOption struct { + Min *float64 + Max *float64 +} + const YAxisWidth = 40 func drawYAxis(p *Draw, opt *ChartOption, xAxisHeight int, padding chart.Box) (*Range, error) {