From 6f6d6c344730f48b5297bb9695a16f9ecc7874f1 Mon Sep 17 00:00:00 2001 From: vicanso Date: Mon, 7 Nov 2022 20:34:28 +0800 Subject: [PATCH 01/43] fix: fix label render of pie chart, #34 --- pie_chart.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/pie_chart.go b/pie_chart.go index 0075ffc..b4714ac 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -101,8 +101,23 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B theme := opt.Theme currentValue := float64(0) - prevEndX := 0 - prevEndY := 0 + prevPoints := make([]Point, 0) + + isOverride := func(x, y int) bool { + for _, p := range prevPoints { + if math.Abs(float64(p.Y-y)) > labelFontSize { + continue + } + // label可能较多内容,不好计算横向占用空间 + // 因此x的位置需要中间位置两侧,否则认为override + if (p.X <= cx && x <= cx) || + (p.X > cx && x > cx) { + return true + } + } + return false + } + for index, v := range values { seriesPainter.OverrideDrawingStyle(Style{ StrokeWidth: 1, @@ -134,13 +149,17 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B endx := cx + int(labelRadius*math.Cos(angle)) endy := cy + int(labelRadius*math.Sin(angle)) // 计算是否有重叠,如果有则调整y坐标位置 - if index != 0 && - math.Abs(float64(endx-prevEndX)) < labelFontSize && - math.Abs(float64(endy-prevEndY)) < labelFontSize { + // 最多只尝试5次 + for i := 0; i < 5; i++ { + if !isOverride(endx, endy) { + break + } endy -= (labelFontSize << 1) } - prevEndX = endx - prevEndY = endy + prevPoints = append(prevPoints, Point{ + X: endx, + Y: endy, + }) seriesPainter.MoveTo(startx, starty) seriesPainter.LineTo(endx, endy) From 2ed86a81d018bcf9d0105bf217a9c424aa42bf5e Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 12 Nov 2022 10:48:24 +0800 Subject: [PATCH 02/43] fix: fix setting font family for table render --- chart_option.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chart_option.go b/chart_option.go index d4605a1..3c8ac4b 100644 --- a/chart_option.go +++ b/chart_option.go @@ -384,6 +384,9 @@ func TableOptionRender(opt TableChartOption) (*Painter, error) { if opt.Width <= 0 { opt.Width = defaultChartWidth } + if opt.FontFamily != "" { + opt.Font, _ = GetFont(opt.FontFamily) + } if opt.Font == nil { opt.Font, _ = chart.GetDefaultFont() } From de4250f60bfad7d22847b089bef62f2dce30091b Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 12 Nov 2022 20:01:36 +0800 Subject: [PATCH 03/43] feat: support get and set default font --- chart_option.go | 5 ++--- font.go | 19 ++++++++++++++++++- mark_line.go | 3 +-- painter.go | 2 +- painter_test.go | 2 +- theme.go | 3 +-- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/chart_option.go b/chart_option.go index 3c8ac4b..ee6851f 100644 --- a/chart_option.go +++ b/chart_option.go @@ -26,7 +26,6 @@ import ( "sort" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" ) type ChartOption struct { @@ -270,7 +269,7 @@ func (o *ChartOption) fillDefault() { o.font, _ = GetFont(o.FontFamily) if o.font == nil { - o.font, _ = chart.GetDefaultFont() + o.font, _ = GetDefaultFont() } else { // 如果指定了字体,则设置主题的字体 t.SetFont(o.font) @@ -388,7 +387,7 @@ func TableOptionRender(opt TableChartOption) (*Painter, error) { opt.Font, _ = GetFont(opt.FontFamily) } if opt.Font == nil { - opt.Font, _ = chart.GetDefaultFont() + opt.Font, _ = GetDefaultFont() } p, err := NewPainter(PainterOptions{ diff --git a/font.go b/font.go index c40b51e..dae5141 100644 --- a/font.go +++ b/font.go @@ -32,9 +32,13 @@ import ( var fonts = sync.Map{} var ErrFontNotExists = errors.New("font is not exists") +var defaultFontFamily = "defaultFontFamily" func init() { - _ = InstallFont("roboto", roboto.Roboto) + name := "roboto" + _ = InstallFont(name, roboto.Roboto) + font, _ := GetFont(name) + SetDefaultFont(font) } // InstallFont installs the font for charts @@ -47,6 +51,19 @@ func InstallFont(fontFamily string, data []byte) error { return nil } +// GetDefaultFont get default font +func GetDefaultFont() (*truetype.Font, error) { + return GetFont(defaultFontFamily) +} + +// SetDefaultFont set default font +func SetDefaultFont(font *truetype.Font) { + if font == nil { + return + } + fonts.Store(defaultFontFamily, font) +} + // GetFont get the font by font family func GetFont(fontFamily string) (*truetype.Font, error) { value, ok := fonts.Load(fontFamily) diff --git a/mark_line.go b/mark_line.go index af1062d..bc850bb 100644 --- a/mark_line.go +++ b/mark_line.go @@ -24,7 +24,6 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" ) // NewMarkLine returns a series mark line @@ -75,7 +74,7 @@ func (m *markLinePainter) Render() (Box, error) { } font := opt.Font if font == nil { - font, _ = chart.GetDefaultFont() + font, _ = GetDefaultFont() } summary := s.Summary() for _, markLine := range s.MarkLine.Data { diff --git a/painter.go b/painter.go index efd5045..97ad205 100644 --- a/painter.go +++ b/painter.go @@ -149,7 +149,7 @@ func NewPainter(opts PainterOptions, opt ...PainterOption) (*Painter, error) { } font := opts.Font if font == nil { - f, err := chart.GetDefaultFont() + f, err := GetDefaultFont() if err != nil { return nil, err } diff --git a/painter_test.go b/painter_test.go index 96e41ef..2392d5b 100644 --- a/painter_test.go +++ b/painter_test.go @@ -351,7 +351,7 @@ func TestPainterTextFit(t *testing.T) { Type: ChartOutputSVG, }) assert.Nil(err) - f, _ := chart.GetDefaultFont() + f, _ := GetDefaultFont() style := Style{ FontSize: 12, FontColor: chart.ColorBlack, diff --git a/theme.go b/theme.go index 8068687..17706ad 100644 --- a/theme.go +++ b/theme.go @@ -24,7 +24,6 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" "github.com/wcharczuk/go-chart/v2/drawing" ) @@ -311,7 +310,7 @@ func (t *themeColorPalette) GetFont() *truetype.Font { if t.font != nil { return t.font } - f, _ := chart.GetDefaultFont() + f, _ := GetDefaultFont() return f } From 7e1f003be85d09216e71a89337634bd38e4abed2 Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 12 Nov 2022 20:18:02 +0800 Subject: [PATCH 04/43] refactor: update demo --- examples/chinese/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/chinese/main.go b/examples/chinese/main.go index 9068a08..d77216a 100644 --- a/examples/chinese/main.go +++ b/examples/chinese/main.go @@ -34,6 +34,8 @@ func main() { if err != nil { panic(err) } + font, _ := charts.GetFont("noto") + charts.SetDefaultFont(font) values := [][]float64{ { @@ -85,7 +87,6 @@ func main() { p, err := charts.LineRender( values, charts.TitleTextOptionFunc("测试"), - charts.FontFamilyOptionFunc("noto"), charts.XAxisDataOptionFunc([]string{ "星期一", "星期二", From a42d0727df41f5f788ae015c9472a5675bf27774 Mon Sep 17 00:00:00 2001 From: vicanso Date: Tue, 15 Nov 2022 20:09:29 +0800 Subject: [PATCH 05/43] feat: support text rotation --- painter.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/painter.go b/painter.go index 97ad205..6743b37 100644 --- a/painter.go +++ b/painter.go @@ -558,6 +558,12 @@ func (p *Painter) Text(body string, x, y int) *Painter { return p } +func (p *Painter) TextRotation(body string, x, y int, radians float64) { + p.render.SetTextRotation(radians) + p.render.Text(body, x, y) + p.render.ClearTextRotation() +} + func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) chart.Box { style := p.style textWarp := style.TextWrap From 55eca7b0b9331b660ea1f1c03fab3d803769e815 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 16 Nov 2022 20:46:19 +0800 Subject: [PATCH 06/43] feat: support detect color dark or light --- chart_option_test.go | 2 +- echarts_test.go | 2 +- mark_point.go | 12 +++++------- theme.go | 13 +++++++++++++ util.go | 7 +++++++ util_test.go | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 59 insertions(+), 9 deletions(-) diff --git a/chart_option_test.go b/chart_option_test.go index 6f331b3..ff17750 100644 --- a/chart_option_test.go +++ b/chart_option_test.go @@ -277,7 +277,7 @@ func TestBarRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\nRainfallEvaporation24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) + assert.Equal("\\nRainfallEvaporation24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) } func TestHorizontalBarRender(t *testing.T) { diff --git a/echarts_test.go b/echarts_test.go index 5c2dbad..2ce1715 100644 --- a/echarts_test.go +++ b/echarts_test.go @@ -578,5 +578,5 @@ func TestRenderEChartsToSVG(t *testing.T) { ] }`) assert.Nil(err) - assert.Equal("\\nRainfallEvaporationRainfall vs EvaporationFake Data24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) + assert.Equal("\\nRainfallEvaporationRainfall vs EvaporationFake Data24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) } diff --git a/mark_point.go b/mark_point.go index f6c93f3..fd8a88b 100644 --- a/mark_point.go +++ b/mark_point.go @@ -24,7 +24,6 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2/drawing" ) // NewMarkPoint returns a series mark point @@ -78,16 +77,15 @@ func (m *markPointPainter) Render() (Box, error) { symbolSize = 30 } textStyle := Style{ - FontColor: drawing.Color{ - R: 238, - G: 238, - B: 238, - A: 255, - }, FontSize: labelFontSize, StrokeWidth: 1, Font: opt.Font, } + if isLightColor(opt.FillColor) { + textStyle.FontColor = defaultLightFontColor + } else { + textStyle.FontColor = defaultDarkFontColor + } painter.OverrideDrawingStyle(Style{ FillColor: opt.FillColor, }).OverrideTextStyle(textStyle) diff --git a/theme.go b/theme.go index 17706ad..a6d624f 100644 --- a/theme.go +++ b/theme.go @@ -76,6 +76,19 @@ const defaultFontSize = 12.0 var defaultTheme ColorPalette +var defaultLightFontColor = drawing.Color{ + R: 70, + G: 70, + B: 70, + A: 255, +} +var defaultDarkFontColor = drawing.Color{ + R: 238, + G: 238, + B: 238, + A: 255, +} + func init() { echartSeriesColors := []Color{ parseColor("#5470c6"), diff --git a/util.go b/util.go index f8a451e..b333e6d 100644 --- a/util.go +++ b/util.go @@ -262,3 +262,10 @@ func getPolygonPoints(center Point, radius float64, sides int) []Point { } return points } + +func isLightColor(c Color) bool { + r := float64(c.R) * float64(c.R) * 0.299 + g := float64(c.G) * float64(c.G) * 0.587 + b := float64(c.B) * float64(c.B) * 0.114 + return math.Sqrt(r+g+b) > 127.5 +} diff --git a/util_test.go b/util_test.go index 7c2ab2f..62fd08d 100644 --- a/util_test.go +++ b/util_test.go @@ -189,3 +189,35 @@ func TestParseColor(t *testing.T) { A: 250, }, c) } + +func TestIsLightColor(t *testing.T) { + assert := assert.New(t) + + assert.True(isLightColor(drawing.Color{ + R: 255, + G: 255, + B: 255, + })) + assert.True(isLightColor(drawing.Color{ + R: 145, + G: 204, + B: 117, + })) + + assert.False(isLightColor(drawing.Color{ + R: 88, + G: 112, + B: 198, + })) + + assert.False(isLightColor(drawing.Color{ + R: 0, + G: 0, + B: 0, + })) + assert.False(isLightColor(drawing.Color{ + R: 16, + G: 12, + B: 42, + })) +} From 4fc250aefc0ec4e50099ca483b2a7a3f497b33a8 Mon Sep 17 00:00:00 2001 From: vicanso Date: Tue, 22 Nov 2022 22:41:56 +0800 Subject: [PATCH 07/43] feat: support rotate series label --- bar_chart.go | 19 +++++++++++++++++- examples/charts/main.go | 4 ++++ painter.go | 9 ++++++++- series.go | 2 ++ series_label.go | 44 +++++++++++++++++++++++++++++++---------- 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index 19c1664..695b9fd 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -23,6 +23,8 @@ package charts import ( + "math" + "github.com/golang/freetype/truetype" "github.com/wcharczuk/go-chart/v2" ) @@ -164,11 +166,26 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B if labelPainter == nil { continue } + y := barMaxHeight - h + radians := float64(0) + var fontColor Color + if series.Label.Position == PositionBottom { + y = barMaxHeight + radians = -math.Pi / 2 + if isLightColor(fillColor) { + fontColor = defaultLightFontColor + } else { + fontColor = defaultDarkFontColor + } + } labelPainter.Add(LabelValue{ Index: index, Value: item.Value, X: x + barWidth>>1, - Y: barMaxHeight - h, + Y: y, + // 旋转 + Radians: radians, + FontColor: fontColor, }) } diff --git a/examples/charts/main.go b/examples/charts/main.go index c3bb486..76aa42c 100644 --- a/examples/charts/main.go +++ b/examples/charts/main.go @@ -355,6 +355,10 @@ func indexHandler(w http.ResponseWriter, req *http.Request) { Value: 180, }, }, + Label: charts.SeriesLabel{ + Show: true, + Position: charts.PositionBottom, + }, }, }, }, diff --git a/painter.go b/painter.go index 6743b37..a0f81ed 100644 --- a/painter.go +++ b/painter.go @@ -560,7 +560,14 @@ func (p *Painter) Text(body string, x, y int) *Painter { func (p *Painter) TextRotation(body string, x, y int, radians float64) { p.render.SetTextRotation(radians) - p.render.Text(body, x, y) + p.render.Text(body, x+p.box.Left, y+p.box.Top) + p.render.ClearTextRotation() +} + +func (p *Painter) SetTextRotation(radians float64) { + p.render.SetTextRotation(radians) +} +func (p *Painter) ClearTextRotation() { p.render.ClearTextRotation() } diff --git a/series.go b/series.go index 7bd6834..373c7dc 100644 --- a/series.go +++ b/series.go @@ -79,6 +79,8 @@ type SeriesLabel struct { Show bool // Distance to the host graphic element. Distance int + // The position of label + Position string } const ( diff --git a/series_label.go b/series_label.go index 57bd1bf..f2dd40f 100644 --- a/series_label.go +++ b/series_label.go @@ -32,6 +32,8 @@ type labelRenderValue struct { Style Style X int Y int + // 旋转 + Radians float64 } type LabelValue struct { @@ -39,6 +41,10 @@ type LabelValue struct { Value float64 X int Y int + // 旋转 + Radians float64 + // 字体颜色 + FontColor Color } type SeriesLabelPainter struct { @@ -81,19 +87,33 @@ func (o *SeriesLabelPainter) Add(value LabelValue) { FontSize: labelFontSize, Font: o.font, } + if !value.FontColor.IsZero() { + label.Color = value.FontColor + } if !label.Color.IsZero() { labelStyle.FontColor = label.Color } - o.p.OverrideDrawingStyle(labelStyle) - textBox := o.p.MeasureText(text) - renderValue := labelRenderValue{ - Text: text, - Style: labelStyle, - X: value.X - textBox.Width()>>1, - Y: value.Y - distance, + p := o.p + p.OverrideDrawingStyle(labelStyle) + rotated := value.Radians != 0 + if rotated { + p.SetTextRotation(value.Radians) } - if textBox.Width()%2 != 0 { - renderValue.X++ + textBox := p.MeasureText(text) + renderValue := labelRenderValue{ + Text: text, + Style: labelStyle, + X: value.X - textBox.Width()>>1, + Y: value.Y - distance, + Radians: value.Radians, + } + if rotated { + renderValue.X = value.X + textBox.Width()>>1 - 1 + p.ClearTextRotation() + } else { + if textBox.Width()%2 != 0 { + renderValue.X++ + } } o.values = append(o.values, renderValue) } @@ -101,7 +121,11 @@ func (o *SeriesLabelPainter) Add(value LabelValue) { func (o *SeriesLabelPainter) Render() (Box, error) { for _, item := range o.values { o.p.OverrideTextStyle(item.Style) - o.p.Text(item.Text, item.X, item.Y) + if item.Radians != 0 { + o.p.TextRotation(item.Text, item.X, item.Y, item.Radians) + } else { + o.p.Text(item.Text, item.X, item.Y) + } } return chart.BoxZero, nil } From 6db8e2c8dc5f5ead957474fddb4af20787b82b95 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 23 Nov 2022 23:01:52 +0800 Subject: [PATCH 08/43] feat: support series label for horizontal bar --- bar_chart.go | 1 + horizontal_bar_chart.go | 41 +++++++++++++++++++++++++++++++++++++++++ series.go | 2 ++ series_label.go | 16 ++++++++++++++-- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index 695b9fd..8219472 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -186,6 +186,7 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B // 旋转 Radians: radians, FontColor: fontColor, + Offset: series.Label.Offset, }) } diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index 58c6e19..5e433a6 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -99,11 +99,25 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri DivideCount: defaultAxisDivideCount, Size: seriesPainter.Width(), }) + seriesNames := seriesList.Names() + rendererList := []Renderer{} for index := range seriesList { series := seriesList[index] seriesColor := theme.GetSeriesColor(series.index) divideValues := yRange.AutoDivide() + + var labelPainter *SeriesLabelPainter + if series.Label.Show { + labelPainter = NewSeriesLabelPainter(SeriesLabelPainterParams{ + P: seriesPainter, + SeriesNames: seriesNames, + Label: series.Label, + Theme: opt.Theme, + Font: opt.Font, + }) + rendererList = append(rendererList, labelPainter) + } for j, item := range series.Data { if j >= yRange.divideCount { continue @@ -130,8 +144,35 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri Right: right, Bottom: y + barHeight, }) + // 如果label不需要展示,则返回 + if labelPainter == nil { + continue + } + x := right + var fontColor Color + if series.Label.Position == PositionLeft { + x = 0 + if isLightColor(fillColor) { + fontColor = defaultLightFontColor + } else { + fontColor = defaultDarkFontColor + } + } + labelPainter.Add(LabelValue{ + Orient: OrientHorizontal, + Index: index, + Value: item.Value, + X: x, + Y: y + barHeight>>1, + FontColor: fontColor, + Offset: series.Label.Offset, + }) } } + err := doRender(rendererList...) + if err != nil { + return BoxZero, err + } return p.box, nil } diff --git a/series.go b/series.go index 373c7dc..c36fa8b 100644 --- a/series.go +++ b/series.go @@ -81,6 +81,8 @@ type SeriesLabel struct { Distance int // The position of label Position string + // The offset of label's position + Offset Box } const ( diff --git a/series_label.go b/series_label.go index f2dd40f..f0fb2ec 100644 --- a/series_label.go +++ b/series_label.go @@ -45,6 +45,8 @@ type LabelValue struct { Radians float64 // 字体颜色 FontColor Color + Orient string + Offset Box } type SeriesLabelPainter struct { @@ -103,10 +105,18 @@ func (o *SeriesLabelPainter) Add(value LabelValue) { renderValue := labelRenderValue{ Text: text, Style: labelStyle, - X: value.X - textBox.Width()>>1, - Y: value.Y - distance, + X: value.X, + Y: value.Y, Radians: value.Radians, } + if value.Orient != OrientHorizontal { + renderValue.X -= textBox.Width() >> 1 + renderValue.Y -= distance + } else { + renderValue.X += distance + renderValue.Y += textBox.Height() >> 1 + renderValue.Y -= 2 + } if rotated { renderValue.X = value.X + textBox.Width()>>1 - 1 p.ClearTextRotation() @@ -115,6 +125,8 @@ func (o *SeriesLabelPainter) Add(value LabelValue) { renderValue.X++ } } + renderValue.X += value.Offset.Left + renderValue.Y += value.Offset.Top o.values = append(o.values, renderValue) } From 5f0aec60d3d3300316bd9d4d1ad58e357f7c4caf Mon Sep 17 00:00:00 2001 From: vicanso Date: Thu, 24 Nov 2022 20:12:19 +0800 Subject: [PATCH 09/43] refactor: adjust label value of horizontal bar --- horizontal_bar_chart.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index 5e433a6..1340103 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -148,25 +148,23 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri if labelPainter == nil { continue } - x := right - var fontColor Color + labelValue := LabelValue{ + Orient: OrientHorizontal, + Index: index, + Value: item.Value, + X: right, + Y: y + barHeight>>1, + Offset: series.Label.Offset, + } if series.Label.Position == PositionLeft { - x = 0 + labelValue.X = 0 if isLightColor(fillColor) { - fontColor = defaultLightFontColor + labelValue.FontColor = defaultLightFontColor } else { - fontColor = defaultDarkFontColor + labelValue.FontColor = defaultDarkFontColor } } - labelPainter.Add(LabelValue{ - Orient: OrientHorizontal, - Index: index, - Value: item.Value, - X: x, - Y: y + barHeight>>1, - FontColor: fontColor, - Offset: series.Label.Offset, - }) + labelPainter.Add(labelValue) } } err := doRender(rendererList...) From df6180e59aea0a7d3d45be72a9b49bb4a09df0c9 Mon Sep 17 00:00:00 2001 From: vicanso Date: Mon, 28 Nov 2022 19:55:14 +0800 Subject: [PATCH 10/43] fix: fix zero max value of nan, #37 --- range.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/range.go b/range.go index 51d3332..ec64c2d 100644 --- a/range.go +++ b/range.go @@ -121,6 +121,9 @@ func (r axisRange) Values() []string { } func (r *axisRange) getHeight(value float64) int { + if r.max <= r.min { + return 0 + } v := (value - r.min) / (r.max - r.min) return int(v * float64(r.size)) } From f9a534ea02fe56f1c4ec79c73839664ba8cb51a6 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 7 Dec 2022 19:57:35 +0800 Subject: [PATCH 11/43] fix: fix the color of series label, #37 --- bar_chart.go | 12 +++++++----- horizontal_bar_chart.go | 23 +++++++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index 8219472..d8a307e 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -168,14 +168,16 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B } y := barMaxHeight - h radians := float64(0) - var fontColor Color + fontColor := series.Label.Color if series.Label.Position == PositionBottom { y = barMaxHeight radians = -math.Pi / 2 - if isLightColor(fillColor) { - fontColor = defaultLightFontColor - } else { - fontColor = defaultDarkFontColor + if fontColor.IsZero() { + if isLightColor(fillColor) { + fontColor = defaultLightFontColor + } else { + fontColor = defaultDarkFontColor + } } } labelPainter.Add(LabelValue{ diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index 1340103..95d9a3d 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -149,19 +149,22 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri continue } labelValue := LabelValue{ - Orient: OrientHorizontal, - Index: index, - Value: item.Value, - X: right, - Y: y + barHeight>>1, - Offset: series.Label.Offset, + Orient: OrientHorizontal, + Index: index, + Value: item.Value, + X: right, + Y: y + barHeight>>1, + Offset: series.Label.Offset, + FontColor: series.Label.Color, } if series.Label.Position == PositionLeft { labelValue.X = 0 - if isLightColor(fillColor) { - labelValue.FontColor = defaultLightFontColor - } else { - labelValue.FontColor = defaultDarkFontColor + if labelValue.FontColor.IsZero() { + if isLightColor(fillColor) { + labelValue.FontColor = defaultLightFontColor + } else { + labelValue.FontColor = defaultDarkFontColor + } } } labelPainter.Add(labelValue) From ef04ac14abcfe6380464fdc4b3d923448286e198 Mon Sep 17 00:00:00 2001 From: vicanso Date: Fri, 9 Dec 2022 20:08:02 +0800 Subject: [PATCH 12/43] feat: support font size for series label, #38 --- bar_chart.go | 1 + horizontal_bar_chart.go | 1 + line_chart.go | 2 ++ series.go | 2 ++ series_label.go | 9 +++++++-- 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index d8a307e..efeb465 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -189,6 +189,7 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B Radians: radians, FontColor: fontColor, Offset: series.Label.Offset, + FontSize: series.Label.FontSize, }) } diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index 95d9a3d..2ab4c03 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -156,6 +156,7 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri Y: y + barHeight>>1, Offset: series.Label.Offset, FontColor: series.Label.Color, + FontSize: series.Label.FontSize, } if series.Label.Position == PositionLeft { labelValue.X = 0 diff --git a/line_chart.go b/line_chart.go index 26f94a4..9f350bd 100644 --- a/line_chart.go +++ b/line_chart.go @@ -147,6 +147,8 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) ( Value: item.Value, X: p.X, Y: p.Y, + // 字体大小 + FontSize: series.Label.FontSize, }) } // 如果需要填充区域 diff --git a/series.go b/series.go index c36fa8b..13c637e 100644 --- a/series.go +++ b/series.go @@ -83,6 +83,8 @@ type SeriesLabel struct { Position string // The offset of label's position Offset Box + // The font size of label + FontSize float64 } const ( diff --git a/series_label.go b/series_label.go index f0fb2ec..10fd148 100644 --- a/series_label.go +++ b/series_label.go @@ -45,8 +45,10 @@ type LabelValue struct { Radians float64 // 字体颜色 FontColor Color - Orient string - Offset Box + // 字体大小 + FontSize float64 + Orient string + Offset Box } type SeriesLabelPainter struct { @@ -89,6 +91,9 @@ func (o *SeriesLabelPainter) Add(value LabelValue) { FontSize: labelFontSize, Font: o.font, } + if value.FontSize != 0 { + labelStyle.FontSize = value.FontSize + } if !value.FontColor.IsZero() { label.Color = value.FontColor } From d5533447f565ed55b604a59d0578375c61d496cd Mon Sep 17 00:00:00 2001 From: vicanso Date: Sun, 11 Dec 2022 14:57:05 +0800 Subject: [PATCH 13/43] feat: support text rotation for series label, #38 --- axis.go | 26 ++++++++++++++++++++------ painter.go | 9 +++++++++ xaxis.go | 8 +++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/axis.go b/axis.go index 578813c..3f71451 100644 --- a/axis.go +++ b/axis.go @@ -75,7 +75,11 @@ type AxisOption struct { SplitLineShow bool // The color of split line 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) { @@ -153,7 +157,15 @@ func (a *axisPainter) Render() (Box, error) { } top.SetDrawingStyle(style).OverrideTextStyle(style) + isTextRotation := opt.TextRotation != 0 + + if isTextRotation { + top.SetTextRotation(opt.TextRotation) + } textMaxWidth, textMaxHeight := top.MeasureTextMaxWidthHeight(data) + if isTextRotation { + top.ClearTextRotation() + } // 增加30px来计算文本展示区域 textFillWidth := float64(textMaxWidth + 20) @@ -261,11 +273,13 @@ func (a *axisPainter) Render() (Box, error) { Top: labelPaddingTop, Right: labelPaddingRight, })).MultiText(MultiTextOption{ - Align: textAlign, - TextList: data, - Orient: orient, - Unit: unit, - Position: labelPosition, + Align: textAlign, + TextList: data, + Orient: orient, + Unit: unit, + Position: labelPosition, + TextRotation: opt.TextRotation, + Offset: opt.LabelOffset, }) // 显示辅助线 if opt.SplitLineShow { diff --git a/painter.go b/painter.go index a0f81ed..71d205f 100644 --- a/painter.go +++ b/painter.go @@ -71,6 +71,9 @@ type MultiTextOption struct { Unit int Position string Align string + // The text rotation of label + TextRotation float64 + Offset Box } type GridOption struct { @@ -682,10 +685,13 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { } else { values = autoDivide(width, count) } + offset := opt.Offset for index, text := range opt.TextList { if opt.Unit != 0 && index%opt.Unit != showIndex { continue } + p.ClearTextRotation() + p.SetTextRotation(opt.TextRotation) box := p.MeasureText(text) start := values[index] if positionCenter { @@ -706,8 +712,11 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { } else { x = start - box.Width()>>1 } + x += offset.Left + y += offset.Top p.Text(text, x, y) } + p.ClearTextRotation() return p } diff --git a/xaxis.go b/xaxis.go index 00636a5..95578ff 100644 --- a/xaxis.go +++ b/xaxis.go @@ -47,7 +47,11 @@ type XAxisOption struct { // The line color of axis StrokeColor Color // The color of label - FontColor Color + FontColor Color + // The text rotation of label + TextRotation float64 + // The offset of label + LabelOffset Box isValueAxis bool } @@ -81,6 +85,8 @@ func (opt *XAxisOption) ToAxisOption() AxisOption { FontColor: opt.FontColor, Show: opt.Show, SplitLineColor: opt.Theme.GetAxisSplitLineColor(), + TextRotation: opt.TextRotation, + LabelOffset: opt.LabelOffset, } if opt.isValueAxis { axisOpt.SplitLineShow = true From 830d4bdd21201985bba34404086e7fbdcf8134fd Mon Sep 17 00:00:00 2001 From: vicanso Date: Sun, 11 Dec 2022 14:59:37 +0800 Subject: [PATCH 14/43] fix: fix test for text roration --- painter.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/painter.go b/painter.go index 71d205f..8f43940 100644 --- a/painter.go +++ b/painter.go @@ -685,13 +685,16 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { } else { values = autoDivide(width, count) } + isTextRotation := opt.TextRotation != 0 offset := opt.Offset for index, text := range opt.TextList { if opt.Unit != 0 && index%opt.Unit != showIndex { continue } - p.ClearTextRotation() - p.SetTextRotation(opt.TextRotation) + if isTextRotation { + p.ClearTextRotation() + p.SetTextRotation(opt.TextRotation) + } box := p.MeasureText(text) start := values[index] if positionCenter { @@ -716,7 +719,9 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { y += offset.Top p.Text(text, x, y) } - p.ClearTextRotation() + if isTextRotation { + p.ClearTextRotation() + } return p } From a767b3e1af4fc0a275b97a68483292ae53445a54 Mon Sep 17 00:00:00 2001 From: Thomas Knierim Date: Mon, 26 Dec 2022 15:06:53 +0700 Subject: [PATCH 15/43] added option for line chart bg fill opacity --- chart_option.go | 2 ++ charts.go | 1 + line_chart.go | 8 +++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/chart_option.go b/chart_option.go index ee6851f..5311d50 100644 --- a/chart_option.go +++ b/chart_option.go @@ -71,6 +71,8 @@ type ChartOption struct { BarHeight int // Fill the area of line chart FillArea bool + // background fill (alpha) opacity + Opacity uint8 // The child charts Children []ChartOption // The value formatter diff --git a/charts.go b/charts.go index d6745d3..8613050 100644 --- a/charts.go +++ b/charts.go @@ -409,6 +409,7 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) { SymbolShow: opt.SymbolShow, StrokeWidth: opt.LineStrokeWidth, FillArea: opt.FillArea, + Opacity: opt.Opacity, }).render(renderResult, lineSeriesList) return err }) diff --git a/line_chart.go b/line_chart.go index 9f350bd..bdbd38e 100644 --- a/line_chart.go +++ b/line_chart.go @@ -70,6 +70,8 @@ type LineChartOption struct { FillArea bool // background is filled backgroundIsFilled bool + // background fill (alpha) opacity + Opacity uint8 } func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) { @@ -156,6 +158,10 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) ( areaPoints := make([]Point, len(points)) copy(areaPoints, points) bottomY := yRange.getRestHeight(yRange.min) + var opacity uint8 = 200 + if opt.Opacity != 0 { + opacity = opt.Opacity + } areaPoints = append(areaPoints, Point{ X: areaPoints[len(areaPoints)-1].X, Y: bottomY, @@ -164,7 +170,7 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) ( Y: bottomY, }, areaPoints[0]) seriesPainter.SetDrawingStyle(Style{ - FillColor: seriesColor.WithAlpha(200), + FillColor: seriesColor.WithAlpha(opacity), }) seriesPainter.FillArea(areaPoints) } From e10175594b517f9a12217478b440faecc8d3c455 Mon Sep 17 00:00:00 2001 From: vicanso Date: Thu, 5 Jan 2023 19:15:58 +0800 Subject: [PATCH 16/43] feat: support label format for funnel chart, #41 --- funnel_chart.go | 8 +++----- series.go | 8 ++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/funnel_chart.go b/funnel_chart.go index 719853a..300b539 100644 --- a/funnel_chart.go +++ b/funnel_chart.go @@ -23,9 +23,6 @@ package charts import ( - "fmt" - - "github.com/dustin/go-humanize" "github.com/golang/freetype/truetype" ) @@ -95,13 +92,14 @@ func (f *funnelChart) render(result *defaultRenderResult, seriesList SeriesList) y := 0 widthList := make([]int, len(seriesList)) textList := make([]string, len(seriesList)) + seriesNames := seriesList.Names() for index, item := range seriesList { value := item.Data[0].Value widthPercent := (value - min) / (max - min) w := int(widthPercent * float64(width)) widthList[index] = w - p := humanize.CommafWithDigits(value/max*100, 2) + "%" - textList[index] = fmt.Sprintf("%s(%s)", item.Name, p) + percent := value / max + textList[index] = NewFunnelLabelFormatter(seriesNames, item.Label.Formatter)(index, value, percent) } for index, w := range widthList { diff --git a/series.go b/series.go index 13c637e..f28bfa9 100644 --- a/series.go +++ b/series.go @@ -279,6 +279,14 @@ func NewPieLabelFormatter(seriesNames []string, layout string) LabelFormatter { 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 func NewValueLabelFormatter(seriesNames []string, layout string) LabelFormatter { if len(layout) == 0 { From 8ba9e2e1b207e0ead3826ba28b3bbd15304651e8 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 11 Jan 2023 20:41:16 +0800 Subject: [PATCH 17/43] fix: fix x axis label of horizontal bar chart, #42 --- charts.go | 11 ++++++++++- examples/horizontal_bar_chart/main.go | 28 +++++++++++++-------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/charts.go b/charts.go index 8613050..74db733 100644 --- a/charts.go +++ b/charts.go @@ -215,7 +215,16 @@ func defaultRender(p *Painter, opt defaultRenderOption) (*defaultRenderResult, e yAxisOption.Data = r.Values() } else { 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 } reverseStringSlice(yAxisOption.Data) diff --git a/examples/horizontal_bar_chart/main.go b/examples/horizontal_bar_chart/main.go index a0f5bda..a1c50a7 100644 --- a/examples/horizontal_bar_chart/main.go +++ b/examples/horizontal_bar_chart/main.go @@ -26,22 +26,22 @@ func writeFile(buf []byte) error { func main() { values := [][]float64{ { - 8203, - 18203, - 23489, - 29034, - 104970, - 131744, - 630230, + 10, + 30, + 50, + 70, + 90, + 110, + 130, }, { - 9325, - 19325, - 23438, - 31000, - 121594, - 134141, - 681807, + 20, + 40, + 60, + 80, + 100, + 120, + 140, }, } p, err := charts.HorizontalBarRender( From d3f7a773afc152f3be5b6237baa23bd2f40177db Mon Sep 17 00:00:00 2001 From: vicanso Date: Thu, 12 Jan 2023 20:20:36 +0800 Subject: [PATCH 18/43] fix: fix zero value of funnel chart, #43 --- examples/funnel_chart/main.go | 4 ++++ funnel_chart.go | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/examples/funnel_chart/main.go b/examples/funnel_chart/main.go index 8f21db6..24f8afe 100644 --- a/examples/funnel_chart/main.go +++ b/examples/funnel_chart/main.go @@ -30,6 +30,8 @@ func main() { 60, 40, 20, + 10, + 0, } p, err := charts.FunnelRender( values, @@ -40,6 +42,8 @@ func main() { "Visit", "Inquiry", "Order", + "Pay", + "Cancel", }), ) if err != nil { diff --git a/funnel_chart.go b/funnel_chart.go index 300b539..d4a8bdd 100644 --- a/funnel_chart.go +++ b/funnel_chart.go @@ -93,12 +93,21 @@ func (f *funnelChart) render(result *defaultRenderResult, seriesList SeriesList) widthList := make([]int, len(seriesList)) textList := make([]string, len(seriesList)) seriesNames := seriesList.Names() + offset := max - min for index, item := range seriesList { 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)) widthList[index] = w - percent := value / max + // 如果最大值为0,则占比100% + percent := 1.0 + if max != 0 { + percent = value / max + } textList[index] = NewFunnelLabelFormatter(seriesNames, item.Label.Formatter)(index, value, percent) } From 29a5ece5458638b95f3d85218f96be56abdadb0d Mon Sep 17 00:00:00 2001 From: vicanso Date: Tue, 14 Feb 2023 20:35:54 +0800 Subject: [PATCH 19/43] chore: update go modules --- .github/workflows/test.yml | 1 + go.mod | 6 +++--- go.sum | 13 ++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61449a3..f591a3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,7 @@ jobs: strategy: matrix: go: + - '1.20' - '1.19' - '1.18' - '1.17' diff --git a/go.mod b/go.mod index de0bb9c..e265627 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module github.com/vicanso/go-charts/v2 go 1.17 require ( - github.com/dustin/go-humanize v1.0.0 + github.com/dustin/go-humanize v1.0.1 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 github.com/wcharczuk/go-chart/v2 v2.1.0 ) require ( github.com/davecgh/go-spew v1.1.1 // 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.0.0-20200927104501-e162460cd6b5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e0b1547..ef2a000 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,24 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +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/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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 20e8d4a078b7b17d373aea3a75e95d4119b5c12f Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 25 Feb 2023 14:04:30 +0800 Subject: [PATCH 20/43] feat: support to set the first axis --- axis.go | 4 ++ examples/time_line_chart/main.go | 82 ++++++++++++++++++++++++++++++++ painter.go | 15 +++++- xaxis.go | 3 ++ 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 examples/time_line_chart/main.go diff --git a/axis.go b/axis.go index 3f71451..762a6a2 100644 --- a/axis.go +++ b/axis.go @@ -63,6 +63,8 @@ type AxisOption struct { StrokeWidth float64 // The length of the axis tick TickLength int + // The first axis + FirstAxis int // The margin value of label LabelMargin int // The font size of label @@ -255,6 +257,7 @@ func (a *axisPainter) Render() (Box, error) { Length: tickLength, Unit: unit, Orient: orient, + First: opt.FirstAxis, }) p.LineStroke([]Point{ { @@ -273,6 +276,7 @@ func (a *axisPainter) Render() (Box, error) { Top: labelPaddingTop, Right: labelPaddingRight, })).MultiText(MultiTextOption{ + First: opt.FirstAxis, Align: textAlign, TextList: data, Orient: orient, diff --git a/examples/time_line_chart/main.go b/examples/time_line_chart/main.go new file mode 100644 index 0000000..10932cd --- /dev/null +++ b/examples/time_line_chart/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "crypto/rand" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "time" + + "github.com/vicanso/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 = ioutil.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) + } +} diff --git a/painter.go b/painter.go index 8f43940..18496fd 100644 --- a/painter.go +++ b/painter.go @@ -59,6 +59,8 @@ type PainterOptions struct { type PainterOption func(*Painter) type TicksOption struct { + // the first tick + First int Length int Orient string Count int @@ -74,6 +76,8 @@ type MultiTextOption struct { // The text rotation of label TextRotation float64 Offset Box + // The first text index + First int } type GridOption struct { @@ -616,6 +620,7 @@ func (p *Painter) Ticks(opt TicksOption) *Painter { return p } count := opt.Count + first := opt.First width := p.Width() height := p.Height() unit := 1 @@ -630,7 +635,10 @@ func (p *Painter) Ticks(opt TicksOption) *Painter { values = autoDivide(width, count) } for index, value := range values { - if index%unit != 0 { + if index < first { + continue + } + if (index-first)%unit != 0 { continue } if isVertical { @@ -688,7 +696,10 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { isTextRotation := opt.TextRotation != 0 offset := opt.Offset for index, text := range opt.TextList { - if opt.Unit != 0 && index%opt.Unit != showIndex { + if index < opt.First { + continue + } + if opt.Unit != 0 && (index-opt.First)%opt.Unit != showIndex { continue } if isTextRotation { diff --git a/xaxis.go b/xaxis.go index 95578ff..61698d7 100644 --- a/xaxis.go +++ b/xaxis.go @@ -50,6 +50,8 @@ type XAxisOption struct { FontColor Color // The text rotation of label TextRotation float64 + // The first axis + FirstAxis int // The offset of label LabelOffset Box isValueAxis bool @@ -87,6 +89,7 @@ func (opt *XAxisOption) ToAxisOption() AxisOption { SplitLineColor: opt.Theme.GetAxisSplitLineColor(), TextRotation: opt.TextRotation, LabelOffset: opt.LabelOffset, + FirstAxis: opt.FirstAxis, } if opt.isValueAxis { axisOpt.SplitLineShow = true From e7a49c2c212d022df6cda4739b8c3e391fd3594a Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Thu, 4 May 2023 12:52:28 -0600 Subject: [PATCH 21/43] Improvements to how the X Axis is rendered This provides two improvements to how the X Axis is rendered: * The calculation for where a tick should exist has been improved. It now will ensure a tick is always at both the start of the axis and the end of the axis. This makes it clear exactly what data span is captured in the graph. * The second improvement is how the label on the last tick is written. It used to often get partially cut off, and with the change to ensure a tick is always at the end this could be seen more easily. Now the last tick has it's label written to the left so that it can be fully displayed. --- painter.go | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/painter.go b/painter.go index 18496fd..450afdf 100644 --- a/painter.go +++ b/painter.go @@ -615,6 +615,17 @@ func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) ch return output } +func isTick(totalRange int, numTicks int, index int) bool { + step := float64(totalRange-1) / float64(numTicks-1) + for i := 0; i < numTicks; i++ { + value := float64(i) * step + if int(value + 0.5) == index { + return true + } + } + return false +} + func (p *Painter) Ticks(opt TicksOption) *Painter { if opt.Count <= 0 || opt.Length <= 0 { return p @@ -638,7 +649,7 @@ func (p *Painter) Ticks(opt TicksOption) *Painter { if index < first { continue } - if (index-first)%unit != 0 { + if ! isTick(len(values), unit, index) { continue } if isVertical { @@ -674,15 +685,13 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { } count := len(opt.TextList) positionCenter := true - showIndex := opt.Unit / 2 + tickLimit := true if containsString([]string{ PositionLeft, PositionTop, }, opt.Position) { positionCenter = false count-- - // 非居中 - showIndex = 0 } width := p.Width() height := p.Height() @@ -690,6 +699,7 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { isVertical := opt.Orient == OrientVertical if isVertical { values = autoDivide(height, count) + tickLimit = false } else { values = autoDivide(width, count) } @@ -699,7 +709,7 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { if index < opt.First { continue } - if opt.Unit != 0 && (index-opt.First)%opt.Unit != showIndex { + if opt.Unit != 0 && tickLimit && ! isTick(len(opt.TextList)-opt.First, opt.Unit, index-opt.First) { continue } if isTextRotation { @@ -724,7 +734,11 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { x = 0 } } else { - x = start - box.Width()>>1 + if index == len(opt.TextList) - 1 { + x = start - box.Width() + } else { + x = start - box.Width()>>1 + } } x += offset.Left y += offset.Top @@ -749,7 +763,6 @@ func (p *Painter) Grid(opt GridOption) *Painter { x1 := 0 y1 := 0 if isVertical { - x0 = v x1 = v y1 = height From 19173dfd37f737b4a5a681556b0a7fe661d749db Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Thu, 4 May 2023 17:59:11 -0600 Subject: [PATCH 22/43] painter.go: Optimize isTick function This reduces the loop frequency to one or two iterations in all cases. I have been unable to find any single line equation that can produce this same behavior, but one likely exists. --- painter.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/painter.go b/painter.go index 450afdf..175b2f2 100644 --- a/painter.go +++ b/painter.go @@ -617,10 +617,12 @@ func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) ch func isTick(totalRange int, numTicks int, index int) bool { step := float64(totalRange-1) / float64(numTicks-1) - for i := 0; i < numTicks; i++ { - value := float64(i) * step - if int(value + 0.5) == index { + for i := int(float64(index) / step); i < numTicks; i++ { + value := int((float64(i) * step) + 0.5) + if value == index { return true + } else if value > index { + break } } return false From c810369730585ed5692c3e5e13c36a4c785568c1 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 5 May 2023 09:44:09 -0600 Subject: [PATCH 23/43] Change ticks to avoid values impacting each other The recently introduced logic has an incorrect understanding of the `unit` parameter. This would result in too many ticks being outputted, particularly as datasets got larger. This fixes it by re-calculating the tick count using the `unit` param as originally intended. --- painter.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/painter.go b/painter.go index 175b2f2..d74b80d 100644 --- a/painter.go +++ b/painter.go @@ -615,7 +615,8 @@ func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) ch return output } -func isTick(totalRange int, numTicks int, index int) bool { +func isTick(totalRange int, unit int, index int) bool { + numTicks := (totalRange / unit) + 1 step := float64(totalRange-1) / float64(numTicks-1) for i := int(float64(index) / step); i < numTicks; i++ { value := int((float64(i) * step) + 0.5) @@ -737,7 +738,7 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { } } else { if index == len(opt.TextList) - 1 { - x = start - box.Width() + x = start - box.Width() + 10 } else { x = start - box.Width()>>1 } From a158191faf90eb0baa08d2cbe8692afd5b09b58c Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 5 May 2023 09:55:55 -0600 Subject: [PATCH 24/43] Add `Unit` to XAxis as a publicly visible parameter In some cases the XAxis may have a single long title. This can result in very few increments being shown. In order to be more flexible for those cases this allows the XAxis Tick frequency to be able to be directly controlled. --- axis.go | 1 - xaxis.go | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/axis.go b/axis.go index 762a6a2..af104a8 100644 --- a/axis.go +++ b/axis.go @@ -176,7 +176,6 @@ func (a *axisPainter) Render() (Box, error) { unit := opt.Unit if unit <= 0 { - unit = ceilFloatToInt(float64(dataCount) / float64(fitTextCount)) unit = chart.MaxInt(unit, opt.SplitNumber) // 偶数 diff --git a/xaxis.go b/xaxis.go index 61698d7..5557015 100644 --- a/xaxis.go +++ b/xaxis.go @@ -40,7 +40,7 @@ type XAxisOption struct { FontSize float64 // The flag for show axis, set this to *false will hide axis Show *bool - // Number of segments that the axis is split into. Note that this number serves only as a recommendation. + // Number of segments that the axis is split into. Note that this number serves only as a recommendation to avoid writing overlap. SplitNumber int // The position of axis, it can be 'top' or 'bottom' Position string @@ -55,6 +55,8 @@ type XAxisOption struct { // The offset of label LabelOffset Box isValueAxis bool + // This value overrides SplitNumber, specifying directly the frequency at which the axis is split into, higher numbers result in less ticks + Unit int } const defaultXAxisHeight = 30 @@ -90,6 +92,7 @@ func (opt *XAxisOption) ToAxisOption() AxisOption { TextRotation: opt.TextRotation, LabelOffset: opt.LabelOffset, FirstAxis: opt.FirstAxis, + Unit: opt.Unit, } if opt.isValueAxis { axisOpt.SplitLineShow = true From 687baad0af8ff907e1e317f2671e3d76b91746c1 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 5 May 2023 10:19:01 -0600 Subject: [PATCH 25/43] Unit test fixes Unit tests updated for new tick positions and in a couple cases additional one X axis sample. --- axis_test.go | 6 +++--- bar_chart_test.go | 2 +- chart_option_test.go | 6 +++--- echarts_test.go | 2 +- horizontal_bar_chart_test.go | 2 +- line_chart_test.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/axis_test.go b/axis_test.go index d0cff41..a04024d 100644 --- a/axis_test.go +++ b/axis_test.go @@ -53,7 +53,7 @@ func TestAxis(t *testing.T) { }).Render() return p.Bytes() }, - result: "\\nMonTueWedThuFriSatSun", + result: "\\nMonTueWedThuFriSatSun", }, // 底部x轴文本居左 { @@ -72,7 +72,7 @@ func TestAxis(t *testing.T) { }).Render() return p.Bytes() }, - result: "\\nMonTueWedThuFriSatSun", + result: "\\nMonTueWedThuFriSatSun", }, // 左侧y轴 { @@ -155,7 +155,7 @@ func TestAxis(t *testing.T) { }).Render() return p.Bytes() }, - result: "\\nMon --Tue --Wed --Thu --Fri --Sat --Sun --", + result: "\\nMon --Tue --Wed --Thu --Fri --Sat --Sun --", }, } diff --git a/bar_chart_test.go b/bar_chart_test.go index e1522d6..aec6428 100644 --- a/bar_chart_test.go +++ b/bar_chart_test.go @@ -102,7 +102,7 @@ func TestBarChart(t *testing.T) { } return p.Bytes() }, - result: "\\n24020016012080400FebMayAugNov24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", + result: "\\n24020016012080400JanAprJulSepDec24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", }, } diff --git a/chart_option_test.go b/chart_option_test.go index ff17750..b7f4e93 100644 --- a/chart_option_test.go +++ b/chart_option_test.go @@ -204,7 +204,7 @@ func TestLineRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", string(data)) + assert.Equal("\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", string(data)) } func TestBarRender(t *testing.T) { @@ -277,7 +277,7 @@ func TestBarRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\nRainfallEvaporation24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) + assert.Equal("\\nRainfallEvaporation24020016012080400JanAprJulSepDec162.22182.22.341.6248.07", string(data)) } func TestHorizontalBarRender(t *testing.T) { @@ -326,7 +326,7 @@ func TestHorizontalBarRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", string(data)) + assert.Equal("\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", string(data)) } func TestPieRender(t *testing.T) { diff --git a/echarts_test.go b/echarts_test.go index 2ce1715..dd7562f 100644 --- a/echarts_test.go +++ b/echarts_test.go @@ -578,5 +578,5 @@ func TestRenderEChartsToSVG(t *testing.T) { ] }`) assert.Nil(err) - assert.Equal("\\nRainfallEvaporationRainfall vs EvaporationFake Data24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) + assert.Equal("\\nRainfallEvaporationRainfall vs EvaporationFake Data24020016012080400JanAprJulSepDec162.22182.22.341.6248.07", string(data)) } diff --git a/horizontal_bar_chart_test.go b/horizontal_bar_chart_test.go index e078c4a..78f3e69 100644 --- a/horizontal_bar_chart_test.go +++ b/horizontal_bar_chart_test.go @@ -83,7 +83,7 @@ func TestHorizontalBarChart(t *testing.T) { } return p.Bytes() }, - result: "\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", + result: "\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", }, } for _, tt := range tests { diff --git a/line_chart_test.go b/line_chart_test.go index e169f90..e8bc1d7 100644 --- a/line_chart_test.go +++ b/line_chart_test.go @@ -117,7 +117,7 @@ func TestLineChart(t *testing.T) { } return p.Bytes() }, - result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", + result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", }, { render: func(p *Painter) ([]byte, error) { @@ -201,7 +201,7 @@ func TestLineChart(t *testing.T) { } return p.Bytes() }, - result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", + result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", }, } From 0ddb9e4ef1d089a08fdf712377e6d3d7d7396cc4 Mon Sep 17 00:00:00 2001 From: vicanso Date: Fri, 12 May 2023 20:31:42 +0800 Subject: [PATCH 26/43] chore: update modules --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e265627..d8a492c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/dustin/go-humanize v1.0.1 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/wcharczuk/go-chart/v2 v2.1.0 ) diff --git a/go.sum b/go.sum index ef2a000..ac1d9f7 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= From 8bcb584abac4fe128bd83103a9b3a04a01db0346 Mon Sep 17 00:00:00 2001 From: Tree Xie Date: Wed, 27 Dec 2023 18:20:55 +0800 Subject: [PATCH 27/43] Revert "Improvements to how the X Axis is rendered" --- axis.go | 1 + axis_test.go | 6 +++--- bar_chart_test.go | 2 +- chart_option_test.go | 6 +++--- echarts_test.go | 2 +- horizontal_bar_chart_test.go | 2 +- line_chart_test.go | 4 ++-- painter.go | 30 +++++++----------------------- xaxis.go | 5 +---- 9 files changed, 20 insertions(+), 38 deletions(-) diff --git a/axis.go b/axis.go index af104a8..762a6a2 100644 --- a/axis.go +++ b/axis.go @@ -176,6 +176,7 @@ func (a *axisPainter) Render() (Box, error) { unit := opt.Unit if unit <= 0 { + unit = ceilFloatToInt(float64(dataCount) / float64(fitTextCount)) unit = chart.MaxInt(unit, opt.SplitNumber) // 偶数 diff --git a/axis_test.go b/axis_test.go index a04024d..d0cff41 100644 --- a/axis_test.go +++ b/axis_test.go @@ -53,7 +53,7 @@ func TestAxis(t *testing.T) { }).Render() return p.Bytes() }, - result: "\\nMonTueWedThuFriSatSun", + result: "\\nMonTueWedThuFriSatSun", }, // 底部x轴文本居左 { @@ -72,7 +72,7 @@ func TestAxis(t *testing.T) { }).Render() return p.Bytes() }, - result: "\\nMonTueWedThuFriSatSun", + result: "\\nMonTueWedThuFriSatSun", }, // 左侧y轴 { @@ -155,7 +155,7 @@ func TestAxis(t *testing.T) { }).Render() return p.Bytes() }, - result: "\\nMon --Tue --Wed --Thu --Fri --Sat --Sun --", + result: "\\nMon --Tue --Wed --Thu --Fri --Sat --Sun --", }, } diff --git a/bar_chart_test.go b/bar_chart_test.go index aec6428..e1522d6 100644 --- a/bar_chart_test.go +++ b/bar_chart_test.go @@ -102,7 +102,7 @@ func TestBarChart(t *testing.T) { } return p.Bytes() }, - result: "\\n24020016012080400JanAprJulSepDec24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", + result: "\\n24020016012080400FebMayAugNov24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", }, } diff --git a/chart_option_test.go b/chart_option_test.go index b7f4e93..ff17750 100644 --- a/chart_option_test.go +++ b/chart_option_test.go @@ -204,7 +204,7 @@ func TestLineRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", string(data)) + assert.Equal("\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", string(data)) } func TestBarRender(t *testing.T) { @@ -277,7 +277,7 @@ func TestBarRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\nRainfallEvaporation24020016012080400JanAprJulSepDec162.22182.22.341.6248.07", string(data)) + assert.Equal("\\nRainfallEvaporation24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) } func TestHorizontalBarRender(t *testing.T) { @@ -326,7 +326,7 @@ func TestHorizontalBarRender(t *testing.T) { assert.Nil(err) data, err := p.Bytes() assert.Nil(err) - assert.Equal("\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", string(data)) + assert.Equal("\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", string(data)) } func TestPieRender(t *testing.T) { diff --git a/echarts_test.go b/echarts_test.go index dd7562f..2ce1715 100644 --- a/echarts_test.go +++ b/echarts_test.go @@ -578,5 +578,5 @@ func TestRenderEChartsToSVG(t *testing.T) { ] }`) assert.Nil(err) - assert.Equal("\\nRainfallEvaporationRainfall vs EvaporationFake Data24020016012080400JanAprJulSepDec162.22182.22.341.6248.07", string(data)) + assert.Equal("\\nRainfallEvaporationRainfall vs EvaporationFake Data24020016012080400FebMayAugNov162.22182.22.341.6248.07", string(data)) } diff --git a/horizontal_bar_chart_test.go b/horizontal_bar_chart_test.go index 78f3e69..e078c4a 100644 --- a/horizontal_bar_chart_test.go +++ b/horizontal_bar_chart_test.go @@ -83,7 +83,7 @@ func TestHorizontalBarChart(t *testing.T) { } return p.Bytes() }, - result: "\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", + result: "\\n20112012World PopulationWorldChinaIndiaUSAIndonesiaBrazil0122.28k244.56k366.84k489.12k611.4k733.68k", }, } for _, tt := range tests { diff --git a/line_chart_test.go b/line_chart_test.go index e8bc1d7..e169f90 100644 --- a/line_chart_test.go +++ b/line_chart_test.go @@ -117,7 +117,7 @@ func TestLineChart(t *testing.T) { } return p.Bytes() }, - result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", + result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", }, { render: func(p *Painter) ([]byte, error) { @@ -201,7 +201,7 @@ func TestLineChart(t *testing.T) { } return p.Bytes() }, - result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", + result: "\\nEmailUnion AdsVideo AdsDirectSearch EngineLine1.44k1.2k9607204802400MonTueWedThuFriSatSun", }, } diff --git a/painter.go b/painter.go index d74b80d..18496fd 100644 --- a/painter.go +++ b/painter.go @@ -615,20 +615,6 @@ func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) ch return output } -func isTick(totalRange int, unit int, index int) bool { - numTicks := (totalRange / unit) + 1 - step := float64(totalRange-1) / float64(numTicks-1) - for i := int(float64(index) / step); i < numTicks; i++ { - value := int((float64(i) * step) + 0.5) - if value == index { - return true - } else if value > index { - break - } - } - return false -} - func (p *Painter) Ticks(opt TicksOption) *Painter { if opt.Count <= 0 || opt.Length <= 0 { return p @@ -652,7 +638,7 @@ func (p *Painter) Ticks(opt TicksOption) *Painter { if index < first { continue } - if ! isTick(len(values), unit, index) { + if (index-first)%unit != 0 { continue } if isVertical { @@ -688,13 +674,15 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { } count := len(opt.TextList) positionCenter := true - tickLimit := true + showIndex := opt.Unit / 2 if containsString([]string{ PositionLeft, PositionTop, }, opt.Position) { positionCenter = false count-- + // 非居中 + showIndex = 0 } width := p.Width() height := p.Height() @@ -702,7 +690,6 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { isVertical := opt.Orient == OrientVertical if isVertical { values = autoDivide(height, count) - tickLimit = false } else { values = autoDivide(width, count) } @@ -712,7 +699,7 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { if index < opt.First { continue } - if opt.Unit != 0 && tickLimit && ! isTick(len(opt.TextList)-opt.First, opt.Unit, index-opt.First) { + if opt.Unit != 0 && (index-opt.First)%opt.Unit != showIndex { continue } if isTextRotation { @@ -737,11 +724,7 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter { x = 0 } } else { - if index == len(opt.TextList) - 1 { - x = start - box.Width() + 10 - } else { - x = start - box.Width()>>1 - } + x = start - box.Width()>>1 } x += offset.Left y += offset.Top @@ -766,6 +749,7 @@ func (p *Painter) Grid(opt GridOption) *Painter { x1 := 0 y1 := 0 if isVertical { + x0 = v x1 = v y1 = height diff --git a/xaxis.go b/xaxis.go index 5557015..61698d7 100644 --- a/xaxis.go +++ b/xaxis.go @@ -40,7 +40,7 @@ type XAxisOption struct { FontSize float64 // The flag for show axis, set this to *false will hide axis Show *bool - // Number of segments that the axis is split into. Note that this number serves only as a recommendation to avoid writing overlap. + // Number of segments that the axis is split into. Note that this number serves only as a recommendation. SplitNumber int // The position of axis, it can be 'top' or 'bottom' Position string @@ -55,8 +55,6 @@ type XAxisOption struct { // The offset of label LabelOffset Box isValueAxis bool - // This value overrides SplitNumber, specifying directly the frequency at which the axis is split into, higher numbers result in less ticks - Unit int } const defaultXAxisHeight = 30 @@ -92,7 +90,6 @@ func (opt *XAxisOption) ToAxisOption() AxisOption { TextRotation: opt.TextRotation, LabelOffset: opt.LabelOffset, FirstAxis: opt.FirstAxis, - Unit: opt.Unit, } if opt.isValueAxis { axisOpt.SplitLineShow = true From 98af9866a47cd7ac3665588501889601f7900ef4 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 27 Dec 2023 20:33:12 +0800 Subject: [PATCH 28/43] refactor: support label show for radar chart, #62 --- echarts.go | 5 +++++ radar_chart.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/echarts.go b/echarts.go index fbe9a36..5a0e5a0 100644 --- a/echarts.go +++ b/echarts.go @@ -344,6 +344,11 @@ func (esList EChartsSeriesList) ToSeriesList() SeriesList { Data: NewSeriesDataFromValues(dataItem.Value.values), Max: item.Max, Min: item.Min, + Label: SeriesLabel{ + Color: parseColor(item.Label.Color), + Show: item.Label.Show, + Distance: item.Label.Distance, + }, }) } continue diff --git a/radar_chart.go b/radar_chart.go index 429850d..f3d63b9 100644 --- a/radar_chart.go +++ b/radar_chart.go @@ -25,6 +25,7 @@ package charts import ( "errors" + "github.com/dustin/go-humanize" "github.com/golang/freetype/truetype" "github.com/wcharczuk/go-chart/v2" "github.com/wcharczuk/go-chart/v2/drawing" @@ -230,9 +231,15 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList) StrokeColor: color, FillColor: dotFillColor, }) - for _, point := range linePoints { + for index, point := range linePoints { seriesPainter.Circle(dotWith, point.X, point.Y) 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) + } + } } From c2f709a74299db5a1c0290f5d6c07606f0b9f124 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 27 Dec 2023 20:34:05 +0800 Subject: [PATCH 29/43] chore: update modules --- .github/workflows/test.yml | 1 + go.mod | 6 +++--- go.sum | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f591a3a..8d6a027 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,7 @@ jobs: strategy: matrix: go: + - '1.21' - '1.20' - '1.19' - '1.18' diff --git a/go.mod b/go.mod index d8a492c..b46ff02 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.17 require ( github.com/dustin/go-humanize v1.0.1 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/stretchr/testify v1.8.2 - github.com/wcharczuk/go-chart/v2 v2.1.0 + github.com/stretchr/testify v1.8.4 + github.com/wcharczuk/go-chart/v2 v2.1.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect + golang.org/x/image v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ac1d9f7..e518b63 100644 --- a/go.sum +++ b/go.sum @@ -14,11 +14,48 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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= +github.com/wcharczuk/go-chart/v2 v2.1.1 h1:2u7na789qiD5WzccZsFz4MJWOJP72G+2kUuJoSNqWnE= +github.com/wcharczuk/go-chart/v2 v2.1.1/go.mod h1:CyCAUt2oqvfhCl6Q5ZvAZwItgpQKZOkCJGb+VGv6l14= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= +golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From e09ab2c3c7a5a338be739f503361bc17779733dd Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 27 Dec 2023 20:37:18 +0800 Subject: [PATCH 30/43] Revert "chore: update modules" This reverts commit c2f709a74299db5a1c0290f5d6c07606f0b9f124. --- .github/workflows/test.yml | 1 - go.mod | 6 +++--- go.sum | 37 ------------------------------------- 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d6a027..f591a3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,6 @@ jobs: strategy: matrix: go: - - '1.21' - '1.20' - '1.19' - '1.18' diff --git a/go.mod b/go.mod index b46ff02..d8a492c 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.17 require ( github.com/dustin/go-humanize v1.0.1 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - github.com/stretchr/testify v1.8.4 - github.com/wcharczuk/go-chart/v2 v2.1.1 + github.com/stretchr/testify v1.8.2 + github.com/wcharczuk/go-chart/v2 v2.1.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/image v0.14.0 // indirect + golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e518b63..ac1d9f7 100644 --- a/go.sum +++ b/go.sum @@ -14,48 +14,11 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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= -github.com/wcharczuk/go-chart/v2 v2.1.1 h1:2u7na789qiD5WzccZsFz4MJWOJP72G+2kUuJoSNqWnE= -github.com/wcharczuk/go-chart/v2 v2.1.1/go.mod h1:CyCAUt2oqvfhCl6Q5ZvAZwItgpQKZOkCJGb+VGv6l14= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= -golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= -golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 310800a5f01eaa29226b7595cdcc0384d2d9f55b Mon Sep 17 00:00:00 2001 From: "xuejinwei.1112" Date: Tue, 2 Jan 2024 12:32:56 +0800 Subject: [PATCH 31/43] support dash line for line chart --- line_chart.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/line_chart.go b/line_chart.go index bdbd38e..363cd36 100644 --- a/line_chart.go +++ b/line_chart.go @@ -115,6 +115,9 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) ( StrokeColor: seriesColor, StrokeWidth: strokeWidth, } + if len(series.Style.StrokeDashArray) > 0 { + drawingStyle.StrokeDashArray = series.Style.StrokeDashArray + } yRange := result.axisRanges[series.AxisIndex] points := make([]Point, 0) From f1a231ff4b0e660609709babc1a9336c89c0233a Mon Sep 17 00:00:00 2001 From: vicanso Date: Sun, 11 Feb 2024 12:36:26 +0800 Subject: [PATCH 32/43] feat: support split line show option for charts, #69 --- .github/workflows/test.yml | 2 ++ examples/area_line_chart/main.go | 3 +-- examples/bar_chart/main.go | 3 +-- examples/chinese/main.go | 2 +- examples/funnel_chart/main.go | 3 +-- examples/horizontal_bar_chart/main.go | 3 +-- examples/line_chart/main.go | 8 ++++++-- examples/painter/main.go | 3 +-- examples/pie_chart/main.go | 3 +-- examples/radar_chart/main.go | 3 +-- examples/table/main.go | 3 +-- examples/time_line_chart/main.go | 3 +-- yaxis.go | 5 +++++ 13 files changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f591a3a..5544970 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,8 @@ jobs: strategy: matrix: go: + - '1.22' + - '1.21' - '1.20' - '1.19' - '1.18' diff --git a/examples/area_line_chart/main.go b/examples/area_line_chart/main.go index 7a84df0..ea8f1c2 100644 --- a/examples/area_line_chart/main.go +++ b/examples/area_line_chart/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -16,7 +15,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "area-line-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/bar_chart/main.go b/examples/bar_chart/main.go index c559a76..feea66e 100644 --- a/examples/bar_chart/main.go +++ b/examples/bar_chart/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -16,7 +15,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "bar-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/chinese/main.go b/examples/chinese/main.go index d77216a..2d96b58 100644 --- a/examples/chinese/main.go +++ b/examples/chinese/main.go @@ -16,7 +16,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "chinese-line-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/funnel_chart/main.go b/examples/funnel_chart/main.go index 24f8afe..f29ccf9 100644 --- a/examples/funnel_chart/main.go +++ b/examples/funnel_chart/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -16,7 +15,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "funnel-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/horizontal_bar_chart/main.go b/examples/horizontal_bar_chart/main.go index a1c50a7..f2cabe8 100644 --- a/examples/horizontal_bar_chart/main.go +++ b/examples/horizontal_bar_chart/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -16,7 +15,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "horizontal-bar-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/line_chart/main.go b/examples/line_chart/main.go index c1478a6..4e6448f 100644 --- a/examples/line_chart/main.go +++ b/examples/line_chart/main.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -17,7 +16,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "line-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } @@ -97,6 +96,11 @@ func main() { Top: 5, Bottom: 10, } + opt.YAxisOptions = []charts.YAxisOption{ + { + SplitLineShow: charts.FalseFlag(), + }, + } opt.SymbolShow = charts.FalseFlag() opt.LineStrokeWidth = 1 opt.ValueFormatter = func(f float64) string { diff --git a/examples/painter/main.go b/examples/painter/main.go index 3c31ce4..b7a5832 100644 --- a/examples/painter/main.go +++ b/examples/painter/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -17,7 +16,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "painter.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/pie_chart/main.go b/examples/pie_chart/main.go index 3721ed1..38488d2 100644 --- a/examples/pie_chart/main.go +++ b/examples/pie_chart/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -16,7 +15,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "pie-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/radar_chart/main.go b/examples/radar_chart/main.go index 51f7409..e8095ae 100644 --- a/examples/radar_chart/main.go +++ b/examples/radar_chart/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" @@ -16,7 +15,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "radar-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/table/main.go b/examples/table/main.go index 2701ec1..0210ecf 100644 --- a/examples/table/main.go +++ b/examples/table/main.go @@ -1,7 +1,6 @@ package main import ( - "io/ioutil" "os" "path/filepath" "strconv" @@ -19,7 +18,7 @@ func writeFile(buf []byte, filename string) error { } file := filepath.Join(tmpPath, filename) - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/examples/time_line_chart/main.go b/examples/time_line_chart/main.go index 10932cd..6cb3f3d 100644 --- a/examples/time_line_chart/main.go +++ b/examples/time_line_chart/main.go @@ -3,7 +3,6 @@ package main import ( "crypto/rand" "fmt" - "io/ioutil" "math/big" "os" "path/filepath" @@ -20,7 +19,7 @@ func writeFile(buf []byte) error { } file := filepath.Join(tmpPath, "time-line-chart.png") - err = ioutil.WriteFile(file, buf, 0600) + err = os.WriteFile(file, buf, 0600) if err != nil { return err } diff --git a/yaxis.go b/yaxis.go index bece2cc..e58b7a6 100644 --- a/yaxis.go +++ b/yaxis.go @@ -50,6 +50,8 @@ type YAxisOption struct { DivideCount int Unit int 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 @@ -100,6 +102,9 @@ func (opt *YAxisOption) ToAxisOption(p *Painter) AxisOption { axisOpt.StrokeWidth = 1 axisOpt.SplitLineShow = false } + if opt.SplitLineShow != nil { + axisOpt.SplitLineShow = *opt.SplitLineShow + } return axisOpt } From 06fe1006d5b2f1a79f7f68f5a986de4bd1c7b9d2 Mon Sep 17 00:00:00 2001 From: vicanso Date: Sun, 11 Feb 2024 12:39:39 +0800 Subject: [PATCH 33/43] chore: update test go version --- .github/workflows/test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5544970..ce56fe7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,10 +20,6 @@ jobs: - '1.19' - '1.18' - '1.17' - - '1.16' - - '1.15' - - '1.14' - - '1.13' steps: - name: Go ${{ matrix.go }} test From 19a4d783fdfb2f6dedfbad90da9117d389443ffe Mon Sep 17 00:00:00 2001 From: Alexander Heidrich Date: Fri, 8 Mar 2024 20:24:13 +0100 Subject: [PATCH 34/43] fix: Label position of the pie chart --- pie_chart.go | 223 ++++++++++++++++++++++++----------- pie_chart_test.go | 294 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 447 insertions(+), 70 deletions(-) diff --git a/pie_chart.go b/pie_chart.go index b4714ac..bbf7814 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -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 / 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 // Bogenmaß + 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) { opt := p.opt values := make([]float64, len(seriesList)) @@ -101,98 +191,91 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B theme := opt.Theme currentValue := float64(0) - prevPoints := make([]Point, 0) - isOverride := func(x, y int) bool { - for _, p := range prevPoints { - if math.Abs(float64(p.Y-y)) > labelFontSize { - continue - } - // label可能较多内容,不好计算横向占用空间 - // 因此x的位置需要中间位置两侧,否则认为override - if (p.X <= cx && x <= cx) || - (p.X > cx && x > cx) { - return true + var quadrant1, quadrant2, quadrant3, quadrant4 []sector + for index, v := range values { + series := seriesList[index] + color := theme.GetSeriesColor(index) + if index == len(values)-1 { + if color == theme.GetSeriesColor(0) { + color = theme.GetSeriesColor(1) } } - 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{ StrokeWidth: 1, - StrokeColor: theme.GetSeriesColor(index), - FillColor: theme.GetSeriesColor(index), + StrokeColor: s.color, + FillColor: s.color, }) - seriesPainter.MoveTo(cx, cy) - start := chart.PercentToRadians(currentValue/total) - math.Pi/2 - currentValue += v - 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 { + seriesPainter.MoveTo(s.cx, s.cy) + seriesPainter.ArcTo(s.cx, s.cy, s.rx, s.ry, s.start, s.delta).LineTo(s.cx, s.cy).Close().FillStroke() + if !s.showLabel { continue } - - // label的角度为饼块中间 - angle := start + delta/2 - startx := cx + int(radius*math.Cos(angle)) - starty := cy + int(radius*math.Sin(angle)) - - 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 + if currentQuadrant != s.quadrant { + currentQuadrant = s.quadrant + if s.quadrant == 1 { + minY = cy * 2 + maxY = 0 + prevY = cy * 2 + } + if s.quadrant == 2 { + prevY = minY + } + if s.quadrant == 3 { + minY = cy * 2 + maxY = 0 + prevY = 0 + } + if s.quadrant == 4 { + prevY = maxY } - endy -= (labelFontSize << 1) } - prevPoints = append(prevPoints, Point{ - X: endx, - Y: endy, - }) - - seriesPainter.MoveTo(startx, starty) - seriesPainter.LineTo(endx, endy) - offset := labelLineWidth - if endx < cx { - offset *= -1 + prevY = s.calculateY(prevY) + if prevY > maxY { + maxY = prevY } - seriesPainter.MoveTo(endx, endy) - endx += offset - seriesPainter.LineTo(endx, endy) + if prevY < minY { + minY = prevY + } + 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() - textStyle := Style{ FontColor: theme.GetTextColor(), FontSize: labelFontSize, Font: opt.Font, } - if !series.Label.Color.IsZero() { - textStyle.FontColor = series.Label.Color + if !s.series.Label.Color.IsZero() { + textStyle.FontColor = s.series.Label.Color } seriesPainter.OverrideTextStyle(textStyle) - text := NewPieLabelFormatter(seriesNames, series.Label.Formatter)(index, v, percent) - textBox := seriesPainter.MeasureText(text) - 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) + x, y := s.calculateTextXY(seriesPainter.MeasureText(s.label)) + seriesPainter.Text(s.label, x, y) } - return p.p.box, nil } diff --git a/pie_chart_test.go b/pie_chart_test.go index c373a7e..0b8f798 100644 --- a/pie_chart_test.go +++ b/pie_chart_test.go @@ -23,6 +23,7 @@ package charts import ( + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -98,3 +99,296 @@ func TestPieChart(t *testing.T) { assert.Equal(tt.result, string(data)) } } + +func TestPieChartWithLabelsValuesSortedDescending(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + render func(*Painter) ([]byte, error) + result string + }{ + { + render: func(p *Painter) ([]byte, error) { + values := []float64{ + 84358845, + 68070697, + 58850717, + 48059777, + 36753736, + 19051562, + 17947406, + 11754004, + 10827529, + 10521556, + 10467366, + 10394055, + 9597085, + 9104772, + 6447710, + 5932654, + 5563970, + 5428792, + 5194336, + 3850894, + 2857279, + 2116792, + 1883008, + 1373101, + 920701, + 660809, + 542051, + } + _, err := NewPieChart(p, PieChartOption{ + SeriesList: NewPieSeriesList(values, PieSeriesOption{ + Label: SeriesLabel{ + Show: true, + Formatter: "{b} ({c} ≅ {d})", + }, + Radius: "200", + }), + Title: TitleOption{ + Text: "European Union member states by population", + Left: PositionRight, + }, + Padding: Box{ + Top: 20, + Right: 20, + Bottom: 20, + Left: 20, + }, + Legend: LegendOption{ + Data: []string{ + "Germany", + "France", + "Italy", + "Spain", + "Poland", + "Romania", + "Netherlands", + "Belgium", + "Czech Republic", + "Sweden", + "Portugal", + "Greece", + "Hungary", + "Austria", + "Bulgaria", + "Denmark", + "Finland", + "Slovakia", + "Ireland", + "Croatia", + "Lithuania", + "Slovenia", + "Latvia", + "Estonia", + "Cyprus", + "Luxembourg", + "Malta", + }, + Show: FalseFlag(), + }, + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\nEuropean Union member states by populationFrance (68070697 ≅ 15.17%)Germany (84358845 ≅ 18.8%)Italy (58850717 ≅ 13.12%)Spain (48059777 ≅ 10.71%)Belgium (11754004 ≅ 2.62%)Netherlands (17947406 ≅ 4%)Romania (19051562 ≅ 4.24%)Poland (36753736 ≅ 8.19%)Czech Republic (10827529 ≅ 2.41%)Sweden (10521556 ≅ 2.34%)Portugal (10467366 ≅ 2.33%)Greece (10394055 ≅ 2.31%)Hungary (9597085 ≅ 2.13%)Austria (9104772 ≅ 2.02%)Bulgaria (6447710 ≅ 1.43%)Denmark (5932654 ≅ 1.32%)Finland (5563970 ≅ 1.24%)Slovakia (5428792 ≅ 1.21%)Ireland (5194336 ≅ 1.15%)Croatia (3850894 ≅ 0.85%)Lithuania (2857279 ≅ 0.63%)Slovenia (2116792 ≅ 0.47%)Latvia (1883008 ≅ 0.41%)Estonia (1373101 ≅ 0.3%)Cyprus (920701 ≅ 0.2%)Luxembourg (660809 ≅ 0.14%)Malta (542051 ≅ 0.12%)", + }, + } + for _, tt := range tests { + p, err := NewPainter(PainterOptions{ + Type: ChartOutputSVG, + Width: 1000, + Height: 800, + }, PainterThemeOption(defaultTheme)) + assert.Nil(err) + data, err := tt.render(p.Child(PainterPaddingOption(Box{ + Left: 20, + Top: 20, + Right: 20, + Bottom: 20, + }))) + assert.Nil(err) + assert.Equal(tt.result, string(data)) + } +} + +func TestPieChartWithLabelsValuesUnsorted(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + render func(*Painter) ([]byte, error) + result string + }{ + { + render: func(p *Painter) ([]byte, error) { + values := []float64{ + 9104772, + 11754004, + 6447710, + 3850894, + 920701, + 10827529, + 5932654, + 1373101, + 5563970, + 68070697, + 84358845, + 10394055, + 9597085, + 5194336, + 58850717, + 1883008, + 2857279, + 660809, + 542051, + 17947406, + 36753736, + 10467366, + 19051562, + 5428792, + 2116792, + 48059777, + 10521556, + } + _, err := NewPieChart(p, PieChartOption{ + SeriesList: NewPieSeriesList(values, PieSeriesOption{ + Label: SeriesLabel{ + Show: true, + Formatter: "{b} ({c} ≅ {d})", + }, + Radius: "200", + }), + Title: TitleOption{ + Text: "European Union member states by population", + Left: PositionRight, + }, + Padding: Box{ + Top: 20, + Right: 20, + Bottom: 20, + Left: 20, + }, + Legend: LegendOption{ + Data: []string{ + "Austria", + "Belgium", + "Bulgaria", + "Croatia", + "Cyprus", + "Czech Republic", + "Denmark", + "Estonia", + "Finland", + "France", + "Germany", + "Greece", + "Hungary", + "Ireland", + "Italy", + "Latvia", + "Lithuania", + "Luxembourg", + "Malta", + "Netherlands", + "Poland", + "Portugal", + "Romania", + "Slovakia", + "Slovenia", + "Spain", + "Sweden", + }, + Show: FalseFlag(), + }, + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\nEuropean Union member states by populationFrance (68070697 ≅ 15.17%)Finland (5563970 ≅ 1.24%)Estonia (1373101 ≅ 0.3%)Denmark (5932654 ≅ 1.32%)Czech Republic (10827529 ≅ 2.41%)Cyprus (920701 ≅ 0.2%)Croatia (3850894 ≅ 0.85%)Bulgaria (6447710 ≅ 1.43%)Belgium (11754004 ≅ 2.62%)Austria (9104772 ≅ 2.02%)Germany (84358845 ≅ 18.8%)Greece (10394055 ≅ 2.31%)Hungary (9597085 ≅ 2.13%)Poland (36753736 ≅ 8.19%)Netherlands (17947406 ≅ 4%)Malta (542051 ≅ 0.12%)Luxembourg (660809 ≅ 0.14%)Lithuania (2857279 ≅ 0.63%)Latvia (1883008 ≅ 0.41%)Italy (58850717 ≅ 13.12%)Ireland (5194336 ≅ 1.15%)Portugal (10467366 ≅ 2.33%)Romania (19051562 ≅ 4.24%)Slovakia (5428792 ≅ 1.21%)Slovenia (2116792 ≅ 0.47%)Spain (48059777 ≅ 10.71%)Sweden (10521556 ≅ 2.34%)", + }, + } + for _, tt := range tests { + p, err := NewPainter(PainterOptions{ + Type: ChartOutputSVG, + Width: 1000, + Height: 800, + }, PainterThemeOption(defaultTheme)) + assert.Nil(err) + data, err := tt.render(p.Child(PainterPaddingOption(Box{ + Left: 20, + Top: 20, + Right: 20, + Bottom: 20, + }))) + assert.Nil(err) + assert.Equal(tt.result, string(data)) + } +} + +func TestPieChartWith100Labels(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + render func(*Painter) ([]byte, error) + result string + }{ + { + render: func(p *Painter) ([]byte, error) { + var values []float64 + var labels []string + for i := 1; i <= 100; i++ { + values = append(values, float64(1)) + labels = append(labels, "Label "+strconv.Itoa(i)) + } + _, err := NewPieChart(p, PieChartOption{ + SeriesList: NewPieSeriesList(values, PieSeriesOption{ + Label: SeriesLabel{ + Show: true, + }, + Radius: "200", + }), + Title: TitleOption{ + Text: "Test with 100 labels", + Left: PositionRight, + }, + Padding: Box{ + Top: 20, + Right: 20, + Bottom: 20, + Left: 20, + }, + Legend: LegendOption{ + Data: labels, + Show: FalseFlag(), + }, + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\nTest with 100 labelsLabel 25: 1%Label 24: 1%Label 23: 1%Label 22: 1%Label 21: 1%Label 20: 1%Label 19: 1%Label 18: 1%Label 17: 1%Label 16: 1%Label 15: 1%Label 14: 1%Label 13: 1%Label 12: 1%Label 11: 1%Label 10: 1%Label 9: 1%Label 8: 1%Label 7: 1%Label 6: 1%Label 5: 1%Label 4: 1%Label 3: 1%Label 2: 1%Label 1: 1%Label 26: 1%Label 27: 1%Label 28: 1%Label 29: 1%Label 30: 1%Label 31: 1%Label 32: 1%Label 33: 1%Label 34: 1%Label 35: 1%Label 36: 1%Label 37: 1%Label 38: 1%Label 39: 1%Label 40: 1%Label 41: 1%Label 42: 1%Label 43: 1%Label 44: 1%Label 45: 1%Label 46: 1%Label 47: 1%Label 48: 1%Label 49: 1%Label 50: 1%Label 75: 1%Label 74: 1%Label 73: 1%Label 72: 1%Label 71: 1%Label 70: 1%Label 69: 1%Label 68: 1%Label 67: 1%Label 66: 1%Label 65: 1%Label 64: 1%Label 63: 1%Label 62: 1%Label 61: 1%Label 60: 1%Label 59: 1%Label 58: 1%Label 57: 1%Label 56: 1%Label 55: 1%Label 54: 1%Label 53: 1%Label 52: 1%Label 51: 1%Label 76: 1%Label 77: 1%Label 78: 1%Label 79: 1%Label 80: 1%Label 81: 1%Label 82: 1%Label 83: 1%Label 84: 1%Label 85: 1%Label 86: 1%Label 87: 1%Label 88: 1%Label 89: 1%Label 90: 1%Label 91: 1%Label 92: 1%Label 93: 1%Label 94: 1%Label 95: 1%Label 96: 1%Label 97: 1%Label 98: 1%Label 99: 1%Label 100: 1%", + }, + } + for _, tt := range tests { + p, err := NewPainter(PainterOptions{ + Type: ChartOutputSVG, + Width: 1000, + Height: 900, + }, PainterThemeOption(defaultTheme)) + assert.Nil(err) + data, err := tt.render(p.Child(PainterPaddingOption(Box{ + Left: 20, + Top: 20, + Right: 20, + Bottom: 20, + }))) + assert.Nil(err) + assert.Equal(tt.result, string(data)) + } +} From 8c6c4e007c85b7c5fabf3340fff4952fc0810592 Mon Sep 17 00:00:00 2001 From: Alexander Heidrich Date: Fri, 22 Mar 2024 08:27:06 +0100 Subject: [PATCH 35/43] fix: Label position of the pie chart --- pie_chart.go | 4 +- pie_chart_test.go | 141 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/pie_chart.go b/pie_chart.go index bbf7814..6cc48c4 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -94,7 +94,7 @@ func NewSector(cx int, cy int, radius float64, labelRadius float64, value float6 s.cy = cy s.rx = radius s.ry = radius - p := currentValue / totalValue + p := (currentValue + value/2) / totalValue if p < 0.25 { s.quadrant = 1 } else if p < 0.5 { @@ -104,7 +104,7 @@ func NewSector(cx int, cy int, radius float64, labelRadius float64, value float6 } else { s.quadrant = 2 } - s.start = chart.PercentToRadians(currentValue/totalValue) - math.Pi/2 // Bogenmaß + 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)) diff --git a/pie_chart_test.go b/pie_chart_test.go index 0b8f798..3795d32 100644 --- a/pie_chart_test.go +++ b/pie_chart_test.go @@ -194,7 +194,7 @@ func TestPieChartWithLabelsValuesSortedDescending(t *testing.T) { } return p.Bytes() }, - result: "\\nEuropean Union member states by populationFrance (68070697 ≅ 15.17%)Germany (84358845 ≅ 18.8%)Italy (58850717 ≅ 13.12%)Spain (48059777 ≅ 10.71%)Belgium (11754004 ≅ 2.62%)Netherlands (17947406 ≅ 4%)Romania (19051562 ≅ 4.24%)Poland (36753736 ≅ 8.19%)Czech Republic (10827529 ≅ 2.41%)Sweden (10521556 ≅ 2.34%)Portugal (10467366 ≅ 2.33%)Greece (10394055 ≅ 2.31%)Hungary (9597085 ≅ 2.13%)Austria (9104772 ≅ 2.02%)Bulgaria (6447710 ≅ 1.43%)Denmark (5932654 ≅ 1.32%)Finland (5563970 ≅ 1.24%)Slovakia (5428792 ≅ 1.21%)Ireland (5194336 ≅ 1.15%)Croatia (3850894 ≅ 0.85%)Lithuania (2857279 ≅ 0.63%)Slovenia (2116792 ≅ 0.47%)Latvia (1883008 ≅ 0.41%)Estonia (1373101 ≅ 0.3%)Cyprus (920701 ≅ 0.2%)Luxembourg (660809 ≅ 0.14%)Malta (542051 ≅ 0.12%)", + result: "\\nEuropean Union member states by populationGermany (84358845 ≅ 18.8%)France (68070697 ≅ 15.17%)Italy (58850717 ≅ 13.12%)Netherlands (17947406 ≅ 4%)Romania (19051562 ≅ 4.24%)Poland (36753736 ≅ 8.19%)Spain (48059777 ≅ 10.71%)Belgium (11754004 ≅ 2.62%)Czech Republic (10827529 ≅ 2.41%)Sweden (10521556 ≅ 2.34%)Portugal (10467366 ≅ 2.33%)Greece (10394055 ≅ 2.31%)Hungary (9597085 ≅ 2.13%)Austria (9104772 ≅ 2.02%)Bulgaria (6447710 ≅ 1.43%)Denmark (5932654 ≅ 1.32%)Finland (5563970 ≅ 1.24%)Slovakia (5428792 ≅ 1.21%)Ireland (5194336 ≅ 1.15%)Croatia (3850894 ≅ 0.85%)Lithuania (2857279 ≅ 0.63%)Slovenia (2116792 ≅ 0.47%)Latvia (1883008 ≅ 0.41%)Estonia (1373101 ≅ 0.3%)Cyprus (920701 ≅ 0.2%)Luxembourg (660809 ≅ 0.14%)Malta (542051 ≅ 0.12%)", }, } for _, tt := range tests { @@ -392,3 +392,142 @@ func TestPieChartWith100Labels(t *testing.T) { assert.Equal(tt.result, string(data)) } } + +func TestPieChartFixLabelPos72586(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + render func(*Painter) ([]byte, error) + result string + }{ + { + render: func(p *Painter) ([]byte, error) { + values := []float64{ + 397594, + 185596, + 149086, + 144258, + 120194, + 117514, + 99412, + 91135, + 87282, + 76790, + 72586, + 58818, + 58270, + 56306, + 55486, + 54792, + 53746, + 51460, + 41242, + 39476, + 37414, + 36644, + 33784, + 32788, + 32566, + 29608, + 29558, + 29384, + 28166, + 26998, + 26948, + 26054, + 25804, + 25730, + 24438, + 23782, + 22896, + 21404, + 428978, + } + _, err := NewPieChart(p, PieChartOption{ + SeriesList: NewPieSeriesList(values, PieSeriesOption{ + Label: SeriesLabel{ + Show: true, + Formatter: "{b} ({c} ≅ {d})", + }, + Radius: "150", + }), + Title: TitleOption{ + Text: "Fix label K (72586)", + Left: PositionRight, + }, + Padding: Box{ + Top: 20, + Right: 20, + Bottom: 20, + Left: 20, + }, + Legend: LegendOption{ + Data: []string{ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "AA", + "AB", + "AC", + "AD", + "AE", + "AF", + "AG", + "AH", + "AI", + "AJ", + "AK", + "AL", + "AM", + }, + Show: FalseFlag(), + }, + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\nFix label K (72586)C (149086 ≅ 5.04%)B (185596 ≅ 6.28%)A (397594 ≅ 13.45%)D (144258 ≅ 4.88%)E (120194 ≅ 4.06%)F (117514 ≅ 3.97%)G (99412 ≅ 3.36%)H (91135 ≅ 3.08%)I (87282 ≅ 2.95%)J (76790 ≅ 2.59%)Z (29608 ≅ 1%)Y (32566 ≅ 1.1%)X (32788 ≅ 1.1%)W (33784 ≅ 1.14%)V (36644 ≅ 1.24%)U (37414 ≅ 1.26%)T (39476 ≅ 1.33%)S (41242 ≅ 1.39%)R (51460 ≅ 1.74%)Q (53746 ≅ 1.81%)P (54792 ≅ 1.85%)O (55486 ≅ 1.87%)N (56306 ≅ 1.9%)M (58270 ≅ 1.97%)L (58818 ≅ 1.99%)K (72586 ≅ 2.45%)AA (29558 ≅ 1%)AB (29384 ≅ 0.99%)AC (28166 ≅ 0.95%)AD (26998 ≅ 0.91%)AE (26948 ≅ 0.91%)AF (26054 ≅ 0.88%)AG (25804 ≅ 0.87%)AH (25730 ≅ 0.87%)AI (24438 ≅ 0.82%)AJ (23782 ≅ 0.8%)AK (22896 ≅ 0.77%)AL (21404 ≅ 0.72%)AM (428978 ≅ 14.52%)", + }, + } + for _, tt := range tests { + p, err := NewPainter(PainterOptions{ + Type: ChartOutputSVG, + Width: 1150, + Height: 550, + }, PainterThemeOption(defaultTheme)) + assert.Nil(err) + data, err := tt.render(p.Child(PainterPaddingOption(Box{ + Left: 20, + Top: 20, + Right: 20, + Bottom: 20, + }))) + assert.Nil(err) + assert.Equal(tt.result, string(data)) + } +} From 9b7634c2c230a0a1907c6e99be6bdea11f811a9e Mon Sep 17 00:00:00 2001 From: vicanso Date: Thu, 16 May 2024 20:02:24 +0800 Subject: [PATCH 36/43] feat: support rounded rect for bar chart --- bar_chart.go | 27 ++++++++++++------ bar_chart_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ painter.go | 42 ++++++++++++++++++++++++++++ painter_test.go | 23 ++++++++++++++++ series.go | 2 ++ 5 files changed, 156 insertions(+), 8 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index efeb465..508c63e 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -142,14 +142,25 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B } top := barMaxHeight - h - seriesPainter.OverrideDrawingStyle(Style{ - FillColor: fillColor, - }).Rect(chart.Box{ - Top: top, - Left: x, - Right: x + barWidth, - Bottom: barMaxHeight - 1, - }) + if series.RoundRadius <= 0 { + seriesPainter.OverrideDrawingStyle(Style{ + FillColor: fillColor, + }).Rect(chart.Box{ + Top: top, + Left: x, + 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 points[j] = Point{ // 居中的位置 diff --git a/bar_chart_test.go b/bar_chart_test.go index e1522d6..654c320 100644 --- a/bar_chart_test.go +++ b/bar_chart_test.go @@ -104,6 +104,76 @@ func TestBarChart(t *testing.T) { }, result: "\\n24020016012080400FebMayAugNov24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", }, + { + render: func(p *Painter) ([]byte, error) { + seriesList := NewSeriesListDataFromValues([][]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, + }, + }) + for index := range seriesList { + seriesList[index].Label.Show = true + seriesList[index].RoundRadius = 5 + } + _, err := NewBarChart(p, BarChartOption{ + Padding: Box{ + Left: 10, + Top: 10, + Right: 10, + Bottom: 10, + }, + SeriesList: seriesList, + XAxis: NewXAxisOption([]string{ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + }), + YAxisOptions: NewYAxisOptions([]string{ + "Rainfall", + "Evaporation", + }), + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\n24020016012080400FebMayAugNov24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", + }, } for _, tt := range tests { diff --git a/painter.go b/painter.go index 18496fd..bc13418 100644 --- a/painter.go +++ b/painter.go @@ -803,6 +803,48 @@ func (p *Painter) Rect(box Box) *Painter { 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 { width := box.Width() height := box.Height() diff --git a/painter_test.go b/painter_test.go index 2392d5b..b159328 100644 --- a/painter_test.go +++ b/painter_test.go @@ -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("\\n", string(buf)) +} + func TestPainterTextFit(t *testing.T) { assert := assert.New(t) p, err := NewPainter(PainterOptions{ diff --git a/series.go b/series.go index f28bfa9..0ad135f 100644 --- a/series.go +++ b/series.go @@ -126,6 +126,8 @@ type Series struct { Name string // Radius for Pie chart, e.g.: 40%, default is "40%" Radius string + // Round for bar chart + RoundRadius int // Mark point for series MarkPoint SeriesMarkPoint // Make line for series From 96148357237a4698910b58e780f6a2fe095a4cfe Mon Sep 17 00:00:00 2001 From: vicanso Date: Tue, 21 May 2024 20:26:40 +0800 Subject: [PATCH 37/43] feat: support rounded rect for horizontal bar chart --- examples/horizontal_bar_chart/main.go | 3 +++ horizontal_bar_chart.go | 28 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/horizontal_bar_chart/main.go b/examples/horizontal_bar_chart/main.go index f2cabe8..a7daa8c 100644 --- a/examples/horizontal_bar_chart/main.go +++ b/examples/horizontal_bar_chart/main.go @@ -65,6 +65,9 @@ func main() { "China", "World", }), + func(opt *charts.ChartOption) { + opt.SeriesList[0].RoundRadius = 5 + }, ) if err != nil { panic(err) diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index 2ab4c03..ca91242 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -136,14 +136,26 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri fillColor = item.Style.FillColor } right := w - seriesPainter.OverrideDrawingStyle(Style{ - FillColor: fillColor, - }).Rect(chart.Box{ - Top: y, - Left: 0, - Right: right, - Bottom: y + barHeight, - }) + if series.RoundRadius <= 0 { + seriesPainter.OverrideDrawingStyle(Style{ + FillColor: fillColor, + }).Rect(chart.Box{ + Top: y, + Left: 0, + 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不需要展示,则返回 if labelPainter == nil { continue From 32e6dd52d093ff3411bab31f2bed1357ddc29f48 Mon Sep 17 00:00:00 2001 From: vicanso Date: Wed, 5 Jun 2024 21:13:03 +0800 Subject: [PATCH 38/43] refactor: export `GetRenderer` function to get chart renderer --- painter.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/painter.go b/painter.go index bc13418..2bbbe2e 100644 --- a/painter.go +++ b/painter.go @@ -860,3 +860,7 @@ func (p *Painter) LegendLineDot(box Box) *Painter { p.FillStroke() return p } + +func (p *Painter) GetRenderer() chart.Renderer { + return p.render +} From e7dc4189d5b4bae6e8cf1b10851f6766cb3d4f39 Mon Sep 17 00:00:00 2001 From: vicanso Date: Fri, 7 Jun 2024 20:35:03 +0800 Subject: [PATCH 39/43] feat: support bar margin --- bar_chart.go | 7 +++++++ chart_option.go | 2 ++ charts.go | 10 ++++++---- horizontal_bar_chart.go | 5 +++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index 508c63e..1bebc88 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -23,6 +23,7 @@ package charts import ( + "fmt" "math" "github.com/golang/freetype/truetype" @@ -63,6 +64,8 @@ type BarChartOption struct { // The legend option Legend LegendOption BarWidth int + // Margin of bar + BarMargin int } func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) { @@ -88,6 +91,10 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B margin = 5 barMargin = 3 } + if opt.BarMargin > 0 { + barMargin = opt.BarMargin + } + fmt.Println(barMargin) seriesCount := len(seriesList) // 总的宽度-两个margin-(总数-1)的barMargin barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / seriesCount diff --git a/chart_option.go b/chart_option.go index 5311d50..d80a383 100644 --- a/chart_option.go +++ b/chart_option.go @@ -67,6 +67,8 @@ type ChartOption struct { LineStrokeWidth float64 // The bar with of bar chart BarWidth int + // The margin of each bar + BarMargin int // The bar height of horizontal bar chart BarHeight int // Fill the area of line chart diff --git a/charts.go b/charts.go index 74db733..91a0048 100644 --- a/charts.go +++ b/charts.go @@ -375,10 +375,11 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) { if len(barSeriesList) != 0 { handler.Add(func() error { _, err := NewBarChart(p, BarChartOption{ - Theme: opt.theme, - Font: opt.font, - XAxis: opt.XAxis, - BarWidth: opt.BarWidth, + Theme: opt.theme, + Font: opt.font, + XAxis: opt.XAxis, + BarWidth: opt.BarWidth, + BarMargin: opt.BarMargin, }).render(renderResult, barSeriesList) return err }) @@ -391,6 +392,7 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) { Theme: opt.theme, Font: opt.font, BarHeight: opt.BarHeight, + BarMargin: opt.BarMargin, YAxisOptions: opt.YAxisOptions, }).render(renderResult, horizontalBarSeriesList) return err diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index ca91242..2ea97bd 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -50,6 +50,8 @@ type HorizontalBarChartOption struct { // The legend option Legend LegendOption BarHeight int + // Margin of bar + BarMargin int } // NewHorizontalBarChart returns a horizontal bar chart renderer @@ -81,6 +83,9 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri margin = 5 barMargin = 3 } + if opt.BarMargin > 0 { + barMargin = opt.BarMargin + } seriesCount := len(seriesList) // 总的高度-两个margin-(总数-1)的barMargin barHeight := (height - 2*margin - barMargin*(seriesCount-1)) / seriesCount From 5842c71b1d23147fad578d8c11e0f587c464ef71 Mon Sep 17 00:00:00 2001 From: vicanso Date: Thu, 1 Aug 2024 21:44:52 +0800 Subject: [PATCH 40/43] refactor: remove unused code --- bar_chart.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/bar_chart.go b/bar_chart.go index 1bebc88..2d702f2 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -23,7 +23,6 @@ package charts import ( - "fmt" "math" "github.com/golang/freetype/truetype" @@ -94,7 +93,6 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B if opt.BarMargin > 0 { barMargin = opt.BarMargin } - fmt.Println(barMargin) seriesCount := len(seriesList) // 总的宽度-两个margin-(总数-1)的barMargin barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / seriesCount From d25a827706ac3d9c23b2676085c7d6fb845e7782 Mon Sep 17 00:00:00 2001 From: vicanso Date: Thu, 15 Aug 2024 20:37:07 +0800 Subject: [PATCH 41/43] fix: fix label position of pie, #86 --- pie_chart.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pie_chart.go b/pie_chart.go index 6cc48c4..9e036c6 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -234,23 +234,35 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B continue } if currentQuadrant != s.quadrant { - currentQuadrant = s.quadrant if s.quadrant == 1 { minY = cy * 2 maxY = 0 prevY = cy * 2 } if s.quadrant == 2 { - prevY = minY + if currentQuadrant != 3 { + prevY = s.lineEndY + } else { + prevY = minY + } } if s.quadrant == 3 { - minY = cy * 2 - maxY = 0 - prevY = 0 + if currentQuadrant != 4 { + prevY = s.lineEndY + } else { + minY = cy * 2 + maxY = 0 + prevY = 0 + } } if s.quadrant == 4 { - prevY = maxY + if currentQuadrant != 1 { + prevY = s.lineEndY + } else { + prevY = maxY + } } + currentQuadrant = s.quadrant } prevY = s.calculateY(prevY) if prevY > maxY { From 0eacc8e394512d513cd7fa53b8c2fe525b9b2f3c Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Tue, 13 May 2025 21:46:02 -0500 Subject: [PATCH 42/43] start migration to our packages --- README.md | 2 ++ alias.go | 4 ++-- axis.go | 2 +- axis_test.go | 2 +- bar_chart.go | 2 +- chart_option_test.go | 2 +- charts.go | 2 +- charts_test.go | 2 +- echarts.go | 2 +- echarts_test.go | 2 +- examples/painter/main.go | 2 +- examples/table/main.go | 2 +- font.go | 2 +- font_test.go | 2 +- go.mod | 12 +++++++----- go.sum | 18 ++++++++---------- grid_test.go | 2 +- horizontal_bar_chart.go | 2 +- line_chart.go | 2 +- mark_line_test.go | 2 +- mark_point_test.go | 2 +- painter.go | 2 +- painter_test.go | 4 ++-- pie_chart.go | 2 +- radar_chart.go | 4 ++-- series.go | 2 +- series_label.go | 2 +- table.go | 4 ++-- theme.go | 2 +- util.go | 4 ++-- util_test.go | 4 ++-- 31 files changed, 51 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 1e4ea8b..4cfb004 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # go-charts +Clone from https://github.com/vicanso/go-charts + [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vicanso/go-charts/blob/master/LICENSE) [![Build Status](https://github.com/vicanso/go-charts/workflows/Test/badge.svg)](https://github.com/vicanso/go-charts/actions) diff --git a/alias.go b/alias.go index a96f50b..edf0dec 100644 --- a/alias.go +++ b/alias.go @@ -23,8 +23,8 @@ package charts import ( - "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) type Box = chart.Box diff --git a/axis.go b/axis.go index 762a6a2..55fa219 100644 --- a/axis.go +++ b/axis.go @@ -26,7 +26,7 @@ import ( "strings" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type axisPainter struct { diff --git a/axis_test.go b/axis_test.go index d0cff41..85e18ca 100644 --- a/axis_test.go +++ b/axis_test.go @@ -26,7 +26,7 @@ import ( "testing" "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) { diff --git a/bar_chart.go b/bar_chart.go index 2d702f2..043e044 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -26,7 +26,7 @@ import ( "math" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type barChart struct { diff --git a/chart_option_test.go b/chart_option_test.go index ff17750..c354b26 100644 --- a/chart_option_test.go +++ b/chart_option_test.go @@ -26,7 +26,7 @@ import ( "testing" "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) { diff --git a/charts.go b/charts.go index 91a0048..31df11c 100644 --- a/charts.go +++ b/charts.go @@ -27,7 +27,7 @@ import ( "math" "sort" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) const labelFontSize = 10 diff --git a/charts_test.go b/charts_test.go index da75ee5..bd581e9 100644 --- a/charts_test.go +++ b/charts_test.go @@ -26,7 +26,7 @@ import ( "errors" "testing" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) func BenchmarkMultiChartPNGRender(b *testing.B) { diff --git a/echarts.go b/echarts.go index 5a0e5a0..aaef1f1 100644 --- a/echarts.go +++ b/echarts.go @@ -29,7 +29,7 @@ import ( "regexp" "strconv" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) func convertToArray(data []byte) []byte { diff --git a/echarts_test.go b/echarts_test.go index 2ce1715..2077278 100644 --- a/echarts_test.go +++ b/echarts_test.go @@ -27,7 +27,7 @@ import ( "testing" "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) { diff --git a/examples/painter/main.go b/examples/painter/main.go index b7a5832..193eb7c 100644 --- a/examples/painter/main.go +++ b/examples/painter/main.go @@ -5,7 +5,7 @@ import ( "path/filepath" charts "github.com/vicanso/go-charts/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) func writeFile(buf []byte) error { diff --git a/examples/table/main.go b/examples/table/main.go index 0210ecf..036dc90 100644 --- a/examples/table/main.go +++ b/examples/table/main.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/vicanso/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 { diff --git a/font.go b/font.go index dae5141..828654e 100644 --- a/font.go +++ b/font.go @@ -27,7 +27,7 @@ import ( "sync" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2/roboto" + "git.smarteching.com/zeni/go-chart/v2/roboto" ) var fonts = sync.Map{} diff --git a/font_test.go b/font_test.go index 9dc731c..e0c56b2 100644 --- a/font_test.go +++ b/font_test.go @@ -26,7 +26,7 @@ import ( "testing" "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) { diff --git a/go.mod b/go.mod index d8a492c..b984e59 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,19 @@ -module github.com/vicanso/go-charts/v2 +module git.smarteching.com/zeni/go-charts/v2 -go 1.17 +go 1.24.1 require ( + 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/stretchr/testify v1.8.2 - github.com/wcharczuk/go-chart/v2 v2.1.0 + github.com/stretchr/testify v1.10.0 + github.com/vicanso/go-charts/v2 v2.6.10 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect + github.com/wcharczuk/go-chart/v2 v2.1.0 // indirect + golang.org/x/image v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ac1d9f7..e7a75a8 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ -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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -7,20 +8,17 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vicanso/go-charts/v2 v2.6.10 h1:Nb2YBekEbUBPbvohnUO1oYMy31v75brUPk6n/fq+JXw= +github.com/vicanso/go-charts/v2 v2.6.10/go.mod h1:Ii2KDI3udTG1wPtiTnntzjlUBJVJTqNscMzh3oYHzUk= 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 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= +golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/grid_test.go b/grid_test.go index 3110a2b..fa9c3a6 100644 --- a/grid_test.go +++ b/grid_test.go @@ -26,7 +26,7 @@ import ( "testing" "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) { diff --git a/horizontal_bar_chart.go b/horizontal_bar_chart.go index 2ea97bd..ed091c9 100644 --- a/horizontal_bar_chart.go +++ b/horizontal_bar_chart.go @@ -24,7 +24,7 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type horizontalBarChart struct { diff --git a/line_chart.go b/line_chart.go index 363cd36..fb1d16a 100644 --- a/line_chart.go +++ b/line_chart.go @@ -26,7 +26,7 @@ import ( "math" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) type lineChart struct { diff --git a/mark_line_test.go b/mark_line_test.go index 00d19ef..0448cda 100644 --- a/mark_line_test.go +++ b/mark_line_test.go @@ -26,7 +26,7 @@ import ( "testing" "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) { diff --git a/mark_point_test.go b/mark_point_test.go index ffa01a7..298345b 100644 --- a/mark_point_test.go +++ b/mark_point_test.go @@ -26,7 +26,7 @@ import ( "testing" "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) { diff --git a/painter.go b/painter.go index 2bbbe2e..bee646f 100644 --- a/painter.go +++ b/painter.go @@ -28,7 +28,7 @@ import ( "math" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type ValueFormatter func(float64) string diff --git a/painter_test.go b/painter_test.go index b159328..07c4113 100644 --- a/painter_test.go +++ b/painter_test.go @@ -28,8 +28,8 @@ import ( "github.com/golang/freetype/truetype" "github.com/stretchr/testify/assert" - "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) func TestPainterOption(t *testing.T) { diff --git a/pie_chart.go b/pie_chart.go index 9e036c6..5c04ed8 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -27,7 +27,7 @@ import ( "math" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type pieChart struct { diff --git a/radar_chart.go b/radar_chart.go index f3d63b9..cf18135 100644 --- a/radar_chart.go +++ b/radar_chart.go @@ -27,8 +27,8 @@ import ( "github.com/dustin/go-humanize" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) type radarChart struct { diff --git a/series.go b/series.go index 0ad135f..da50e64 100644 --- a/series.go +++ b/series.go @@ -26,7 +26,7 @@ import ( "strings" "github.com/dustin/go-humanize" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type SeriesData struct { diff --git a/series_label.go b/series_label.go index 10fd148..af873fc 100644 --- a/series_label.go +++ b/series_label.go @@ -24,7 +24,7 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2" ) type labelRenderValue struct { diff --git a/table.go b/table.go index 86ef569..3e6f273 100644 --- a/table.go +++ b/table.go @@ -26,8 +26,8 @@ import ( "errors" "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) type tableChart struct { diff --git a/theme.go b/theme.go index a6d624f..85016a5 100644 --- a/theme.go +++ b/theme.go @@ -24,7 +24,7 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) const ThemeDark = "dark" diff --git a/util.go b/util.go index b333e6d..87ff31c 100644 --- a/util.go +++ b/util.go @@ -29,8 +29,8 @@ import ( "strings" "github.com/dustin/go-humanize" - "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) func TrueFlag() *bool { diff --git a/util_test.go b/util_test.go index 62fd08d..5770776 100644 --- a/util_test.go +++ b/util_test.go @@ -26,8 +26,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" + "git.smarteching.com/zeni/go-chart/v2" + "git.smarteching.com/zeni/go-chart/v2/drawing" ) func TestGetDefaultInt(t *testing.T) { From 958172a1f1206cc6b676c0c02da928ce7f21cf7f Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Tue, 13 May 2025 21:53:31 -0500 Subject: [PATCH 43/43] update URL in examples --- README.md | 16 ++++++++-------- README_zh.md | 16 ++++++++-------- examples/area_line_chart/main.go | 2 +- examples/bar_chart/main.go | 2 +- examples/charts/main.go | 2 +- examples/chinese/main.go | 2 +- examples/funnel_chart/main.go | 2 +- examples/horizontal_bar_chart/main.go | 2 +- examples/line_chart/main.go | 2 +- examples/painter/main.go | 2 +- examples/pie_chart/main.go | 2 +- examples/radar_chart/main.go | 2 +- examples/table/main.go | 2 +- examples/time_line_chart/main.go | 2 +- go.mod | 2 -- go.sum | 6 ------ 16 files changed, 28 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 4cfb004..0650395 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ More examples can be found in the [./examples/](./examples/) directory. package main import ( - charts "github.com/vicanso/go-charts/v2" + charts "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -101,7 +101,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -176,7 +176,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -233,7 +233,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -288,7 +288,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -346,7 +346,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -386,7 +386,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -451,7 +451,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { diff --git a/README_zh.md b/README_zh.md index c31cf77..3f35b97 100644 --- a/README_zh.md +++ b/README_zh.md @@ -32,7 +32,7 @@ package main import ( - charts "github.com/vicanso/go-charts/v2" + charts "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -98,7 +98,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -173,7 +173,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -230,7 +230,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -285,7 +285,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -343,7 +343,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -383,7 +383,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { @@ -447,7 +447,7 @@ func main() { package main import ( - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func main() { diff --git a/examples/area_line_chart/main.go b/examples/area_line_chart/main.go index ea8f1c2..57ca1e9 100644 --- a/examples/area_line_chart/main.go +++ b/examples/area_line_chart/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/bar_chart/main.go b/examples/bar_chart/main.go index feea66e..91c9f81 100644 --- a/examples/bar_chart/main.go +++ b/examples/bar_chart/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/charts/main.go b/examples/charts/main.go index 76aa42c..81bc4f2 100644 --- a/examples/charts/main.go +++ b/examples/charts/main.go @@ -6,7 +6,7 @@ import ( "net/http" "strconv" - charts "github.com/vicanso/go-charts/v2" + charts "git.smarteching.com/zeni/go-charts/v2" ) var html = ` diff --git a/examples/chinese/main.go b/examples/chinese/main.go index 2d96b58..601f54e 100644 --- a/examples/chinese/main.go +++ b/examples/chinese/main.go @@ -5,7 +5,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/funnel_chart/main.go b/examples/funnel_chart/main.go index f29ccf9..653f834 100644 --- a/examples/funnel_chart/main.go +++ b/examples/funnel_chart/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/horizontal_bar_chart/main.go b/examples/horizontal_bar_chart/main.go index a7daa8c..f5d8497 100644 --- a/examples/horizontal_bar_chart/main.go +++ b/examples/horizontal_bar_chart/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/line_chart/main.go b/examples/line_chart/main.go index 4e6448f..baee8a3 100644 --- a/examples/line_chart/main.go +++ b/examples/line_chart/main.go @@ -5,7 +5,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/painter/main.go b/examples/painter/main.go index 193eb7c..1b842b3 100644 --- a/examples/painter/main.go +++ b/examples/painter/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - charts "github.com/vicanso/go-charts/v2" + charts "git.smarteching.com/zeni/go-charts/v2" "git.smarteching.com/zeni/go-chart/v2/drawing" ) diff --git a/examples/pie_chart/main.go b/examples/pie_chart/main.go index 38488d2..5d70438 100644 --- a/examples/pie_chart/main.go +++ b/examples/pie_chart/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/radar_chart/main.go b/examples/radar_chart/main.go index e8095ae..e7053af 100644 --- a/examples/radar_chart/main.go +++ b/examples/radar_chart/main.go @@ -4,7 +4,7 @@ import ( "os" "path/filepath" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/examples/table/main.go b/examples/table/main.go index 036dc90..de994eb 100644 --- a/examples/table/main.go +++ b/examples/table/main.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" "git.smarteching.com/zeni/go-chart/v2/drawing" ) diff --git a/examples/time_line_chart/main.go b/examples/time_line_chart/main.go index 6cb3f3d..c6c93bf 100644 --- a/examples/time_line_chart/main.go +++ b/examples/time_line_chart/main.go @@ -8,7 +8,7 @@ import ( "path/filepath" "time" - "github.com/vicanso/go-charts/v2" + "git.smarteching.com/zeni/go-charts/v2" ) func writeFile(buf []byte) error { diff --git a/go.mod b/go.mod index b984e59..76a47b6 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,11 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/stretchr/testify v1.10.0 - github.com/vicanso/go-charts/v2 v2.6.10 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/wcharczuk/go-chart/v2 v2.1.0 // indirect golang.org/x/image v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e7a75a8..3e1a48a 100644 --- a/go.sum +++ b/go.sum @@ -10,14 +10,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/vicanso/go-charts/v2 v2.6.10 h1:Nb2YBekEbUBPbvohnUO1oYMy31v75brUPk6n/fq+JXw= -github.com/vicanso/go-charts/v2 v2.6.10/go.mod h1:Ii2KDI3udTG1wPtiTnntzjlUBJVJTqNscMzh3oYHzUk= -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.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=