Compare commits

..

No commits in common. "main" and "v2.4.2" have entirely different histories.
main ... v2.4.2

52 changed files with 245 additions and 1352 deletions

View file

@ -14,12 +14,13 @@ jobs:
strategy:
matrix:
go:
- '1.22'
- '1.21'
- '1.20'
- '1.19'
- '1.18'
- '1.17'
- '1.16'
- '1.15'
- '1.14'
- '1.13'
steps:
- name: Go ${{ matrix.go }} test

View file

@ -1,7 +1,5 @@
# 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)
@ -35,7 +33,7 @@ More examples can be found in the [./examples/](./examples/) directory.
package main
import (
charts "git.smarteching.com/zeni/go-charts/v2"
charts "github.com/vicanso/go-charts/v2"
)
func main() {
@ -101,7 +99,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -176,7 +174,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -233,7 +231,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -288,7 +286,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -346,7 +344,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -386,7 +384,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -451,7 +449,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {

View file

@ -32,7 +32,7 @@
package main
import (
charts "git.smarteching.com/zeni/go-charts/v2"
charts "github.com/vicanso/go-charts/v2"
)
func main() {
@ -98,7 +98,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -173,7 +173,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -230,7 +230,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -285,7 +285,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -343,7 +343,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -383,7 +383,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {
@ -447,7 +447,7 @@ func main() {
package main
import (
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func main() {

View file

@ -23,8 +23,8 @@
package charts
import (
"git.smarteching.com/zeni/go-chart/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
type Box = chart.Box

20
axis.go
View file

@ -26,7 +26,7 @@ import (
"strings"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type axisPainter struct {
@ -63,8 +63,6 @@ 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
@ -77,10 +75,6 @@ type AxisOption struct {
SplitLineShow bool
// The color of split line
SplitLineColor Color
// The text rotation of label
TextRotation float64
// The offset of label
LabelOffset Box
Unit int
}
@ -159,15 +153,7 @@ 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)
@ -257,7 +243,6 @@ func (a *axisPainter) Render() (Box, error) {
Length: tickLength,
Unit: unit,
Orient: orient,
First: opt.FirstAxis,
})
p.LineStroke([]Point{
{
@ -276,14 +261,11 @@ func (a *axisPainter) Render() (Box, error) {
Top: labelPaddingTop,
Right: labelPaddingRight,
})).MultiText(MultiTextOption{
First: opt.FirstAxis,
Align: textAlign,
TextList: data,
Orient: orient,
Unit: unit,
Position: labelPosition,
TextRotation: opt.TextRotation,
Offset: opt.LabelOffset,
})
// 显示辅助线
if opt.SplitLineShow {

View file

@ -26,7 +26,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestAxis(t *testing.T) {

View file

@ -23,10 +23,8 @@
package charts
import (
"math"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type barChart struct {
@ -63,8 +61,6 @@ 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) {
@ -90,9 +86,6 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
margin = 5
barMargin = 3
}
if opt.BarMargin > 0 {
barMargin = opt.BarMargin
}
seriesCount := len(seriesList)
// 总的宽度-两个margin-(总数-1)的barMargin
barWidth := (width - 2*margin - barMargin*(seriesCount-1)) / seriesCount
@ -147,7 +140,6 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
}
top := barMaxHeight - h
if series.RoundRadius <= 0 {
seriesPainter.OverrideDrawingStyle(Style{
FillColor: fillColor,
}).Rect(chart.Box{
@ -156,16 +148,6 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
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{
// 居中的位置
@ -182,30 +164,11 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B
if labelPainter == nil {
continue
}
y := barMaxHeight - h
radians := float64(0)
fontColor := series.Label.Color
if series.Label.Position == PositionBottom {
y = barMaxHeight
radians = -math.Pi / 2
if fontColor.IsZero() {
if isLightColor(fillColor) {
fontColor = defaultLightFontColor
} else {
fontColor = defaultDarkFontColor
}
}
}
labelPainter.Add(LabelValue{
Index: index,
Value: item.Value,
X: x + barWidth>>1,
Y: y,
// 旋转
Radians: radians,
FontColor: fontColor,
Offset: series.Label.Offset,
FontSize: series.Label.FontSize,
Y: barMaxHeight - h,
})
}

File diff suppressed because one or more lines are too long

View file

@ -26,6 +26,7 @@ import (
"sort"
"github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2"
)
type ChartOption struct {
@ -67,14 +68,10 @@ 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
FillArea bool
// background fill (alpha) opacity
Opacity uint8
// The child charts
Children []ChartOption
// The value formatter
@ -273,7 +270,7 @@ func (o *ChartOption) fillDefault() {
o.font, _ = GetFont(o.FontFamily)
if o.font == nil {
o.font, _ = GetDefaultFont()
o.font, _ = chart.GetDefaultFont()
} else {
// 如果指定了字体,则设置主题的字体
t.SetFont(o.font)
@ -387,11 +384,8 @@ 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, _ = GetDefaultFont()
opt.Font, _ = chart.GetDefaultFont()
}
p, err := NewPainter(PainterOptions{

File diff suppressed because one or more lines are too long

View file

@ -27,7 +27,7 @@ import (
"math"
"sort"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
const labelFontSize = 10
@ -215,16 +215,7 @@ func defaultRender(p *Painter, opt defaultRenderOption) (*defaultRenderResult, e
yAxisOption.Data = r.Values()
} else {
yAxisOption.isCategoryAxis = true
// 由于x轴为value部分因此计算其label单独处理
opt.XAxis.Data = NewRange(AxisRangeOption{
Painter: p,
Min: min,
Max: max,
// 高度需要减去x轴的高度
Size: rangeHeight,
// 分隔数量
DivideCount: defaultAxisDivideCount,
}).Values()
opt.XAxis.Data = r.Values()
opt.XAxis.isValueAxis = true
}
reverseStringSlice(yAxisOption.Data)
@ -379,7 +370,6 @@ func Render(opt ChartOption, opts ...OptionFunc) (*Painter, error) {
Font: opt.font,
XAxis: opt.XAxis,
BarWidth: opt.BarWidth,
BarMargin: opt.BarMargin,
}).render(renderResult, barSeriesList)
return err
})
@ -392,7 +382,6 @@ 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
@ -420,7 +409,6 @@ 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
})

View file

@ -26,7 +26,7 @@ import (
"errors"
"testing"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
func BenchmarkMultiChartPNGRender(b *testing.B) {

View file

@ -29,7 +29,7 @@ import (
"regexp"
"strconv"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
func convertToArray(data []byte) []byte {
@ -344,11 +344,6 @@ 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

File diff suppressed because one or more lines are too long

View file

@ -1,10 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -15,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "area-line-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}

View file

@ -1,10 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -15,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "bar-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}

View file

@ -6,7 +6,7 @@ import (
"net/http"
"strconv"
charts "git.smarteching.com/zeni/go-charts/v2"
charts "github.com/vicanso/go-charts/v2"
)
var html = `<!DOCTYPE html>
@ -355,10 +355,6 @@ func indexHandler(w http.ResponseWriter, req *http.Request) {
Value: 180,
},
},
Label: charts.SeriesLabel{
Show: true,
Position: charts.PositionBottom,
},
},
},
},

View file

@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -16,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "chinese-line-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}
@ -34,8 +34,6 @@ func main() {
if err != nil {
panic(err)
}
font, _ := charts.GetFont("noto")
charts.SetDefaultFont(font)
values := [][]float64{
{
@ -87,6 +85,7 @@ func main() {
p, err := charts.LineRender(
values,
charts.TitleTextOptionFunc("测试"),
charts.FontFamilyOptionFunc("noto"),
charts.XAxisDataOptionFunc([]string{
"星期一",
"星期二",

View file

@ -1,10 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -15,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "funnel-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}
@ -29,8 +30,6 @@ func main() {
60,
40,
20,
10,
0,
}
p, err := charts.FunnelRender(
values,
@ -41,8 +40,6 @@ func main() {
"Visit",
"Inquiry",
"Order",
"Pay",
"Cancel",
}),
)
if err != nil {

View file

@ -1,10 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -15,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "horizontal-bar-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}
@ -25,22 +26,22 @@ func writeFile(buf []byte) error {
func main() {
values := [][]float64{
{
10,
30,
50,
70,
90,
110,
130,
8203,
18203,
23489,
29034,
104970,
131744,
630230,
},
{
20,
40,
60,
80,
100,
120,
140,
9325,
19325,
23438,
31000,
121594,
134141,
681807,
},
}
p, err := charts.HorizontalBarRender(
@ -65,9 +66,6 @@ func main() {
"China",
"World",
}),
func(opt *charts.ChartOption) {
opt.SeriesList[0].RoundRadius = 5
},
)
if err != nil {
panic(err)

View file

@ -2,10 +2,11 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -16,7 +17,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "line-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}
@ -96,11 +97,6 @@ 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 {

View file

@ -1,11 +1,12 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
charts "git.smarteching.com/zeni/go-charts/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
charts "github.com/vicanso/go-charts/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func writeFile(buf []byte) error {
@ -16,7 +17,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "painter.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}

View file

@ -1,10 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -15,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "pie-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}

View file

@ -1,10 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"git.smarteching.com/zeni/go-charts/v2"
"github.com/vicanso/go-charts/v2"
)
func writeFile(buf []byte) error {
@ -15,7 +16,7 @@ func writeFile(buf []byte) error {
}
file := filepath.Join(tmpPath, "radar-chart.png")
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}

View file

@ -1,13 +1,14 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"git.smarteching.com/zeni/go-charts/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/vicanso/go-charts/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func writeFile(buf []byte, filename string) error {
@ -18,7 +19,7 @@ func writeFile(buf []byte, filename string) error {
}
file := filepath.Join(tmpPath, filename)
err = os.WriteFile(file, buf, 0600)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}

View file

@ -1,81 +0,0 @@
package main
import (
"crypto/rand"
"fmt"
"math/big"
"os"
"path/filepath"
"time"
"git.smarteching.com/zeni/go-charts/v2"
)
func writeFile(buf []byte) error {
tmpPath := "./tmp"
err := os.MkdirAll(tmpPath, 0700)
if err != nil {
return err
}
file := filepath.Join(tmpPath, "time-line-chart.png")
err = os.WriteFile(file, buf, 0600)
if err != nil {
return err
}
return nil
}
func main() {
xAxisValue := []string{}
values := []float64{}
now := time.Now()
firstAxis := 0
for i := 0; i < 300; i++ {
// 设置首个axis为xx:00的时间点
if firstAxis == 0 && now.Minute() == 0 {
firstAxis = i
}
xAxisValue = append(xAxisValue, now.Format("15:04"))
now = now.Add(time.Minute)
value, _ := rand.Int(rand.Reader, big.NewInt(100))
values = append(values, float64(value.Int64()))
}
p, err := charts.LineRender(
[][]float64{
values,
},
charts.TitleTextOptionFunc("Line"),
charts.XAxisDataOptionFunc(xAxisValue, charts.FalseFlag()),
charts.LegendLabelsOptionFunc([]string{
"Demo",
}, "50"),
func(opt *charts.ChartOption) {
opt.XAxis.FirstAxis = firstAxis
// 必须要比计算得来的最小值更大(每60分钟)
opt.XAxis.SplitNumber = 60
opt.Legend.Padding = charts.Box{
Top: 5,
Bottom: 10,
}
opt.SymbolShow = charts.FalseFlag()
opt.LineStrokeWidth = 1
opt.ValueFormatter = func(f float64) string {
return fmt.Sprintf("%.0f", f)
}
},
)
if err != nil {
panic(err)
}
buf, err := p.Bytes()
if err != nil {
panic(err)
}
err = writeFile(buf)
if err != nil {
panic(err)
}
}

21
font.go
View file

@ -27,18 +27,14 @@ import (
"sync"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2/roboto"
"github.com/wcharczuk/go-chart/v2/roboto"
)
var fonts = sync.Map{}
var ErrFontNotExists = errors.New("font is not exists")
var defaultFontFamily = "defaultFontFamily"
func init() {
name := "roboto"
_ = InstallFont(name, roboto.Roboto)
font, _ := GetFont(name)
SetDefaultFont(font)
_ = InstallFont("roboto", roboto.Roboto)
}
// InstallFont installs the font for charts
@ -51,19 +47,6 @@ 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)

View file

@ -26,7 +26,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2/roboto"
"github.com/wcharczuk/go-chart/v2/roboto"
)
func TestInstallFont(t *testing.T) {

View file

@ -23,6 +23,9 @@
package charts
import (
"fmt"
"github.com/dustin/go-humanize"
"github.com/golang/freetype/truetype"
)
@ -92,23 +95,13 @@ func (f *funnelChart) render(result *defaultRenderResult, seriesList SeriesList)
y := 0
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
// 最大最小值一致则为100%
widthPercent := 100.0
if offset != 0 {
widthPercent = (value - min) / offset
}
widthPercent := (value - min) / (max - min)
w := int(widthPercent * float64(width))
widthList[index] = w
// 如果最大值为0则占比100%
percent := 1.0
if max != 0 {
percent = value / max
}
textList[index] = NewFunnelLabelFormatter(seriesNames, item.Label.Formatter)(index, value, percent)
p := humanize.CommafWithDigits(value/max*100, 2) + "%"
textList[index] = fmt.Sprintf("%s(%s)", item.Name, p)
}
for index, w := range widthList {

12
go.mod
View file

@ -1,17 +1,17 @@
module git.smarteching.com/zeni/go-charts/v2
module github.com/vicanso/go-charts/v2
go 1.24.1
go 1.17
require (
git.smarteching.com/zeni/go-chart/v2 v2.1.4
github.com/dustin/go-humanize v1.0.1
github.com/dustin/go-humanize v1.0.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.8.0
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.21.0 // indirect
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

25
go.sum
View file

@ -1,18 +1,27 @@
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.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.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
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/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/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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=
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/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/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=

View file

@ -26,7 +26,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestGrid(t *testing.T) {

View file

@ -24,7 +24,7 @@ package charts
import (
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type horizontalBarChart struct {
@ -50,8 +50,6 @@ type HorizontalBarChartOption struct {
// The legend option
Legend LegendOption
BarHeight int
// Margin of bar
BarMargin int
}
// NewHorizontalBarChart returns a horizontal bar chart renderer
@ -83,9 +81,6 @@ 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
@ -104,25 +99,11 @@ 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
@ -141,7 +122,6 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
fillColor = item.Style.FillColor
}
right := w
if series.RoundRadius <= 0 {
seriesPainter.OverrideDrawingStyle(Style{
FillColor: fillColor,
}).Rect(chart.Box{
@ -150,47 +130,7 @@ func (h *horizontalBarChart) render(result *defaultRenderResult, seriesList Seri
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
}
labelValue := LabelValue{
Orient: OrientHorizontal,
Index: index,
Value: item.Value,
X: right,
Y: y + barHeight>>1,
Offset: series.Label.Offset,
FontColor: series.Label.Color,
FontSize: series.Label.FontSize,
}
if series.Label.Position == PositionLeft {
labelValue.X = 0
if labelValue.FontColor.IsZero() {
if isLightColor(fillColor) {
labelValue.FontColor = defaultLightFontColor
} else {
labelValue.FontColor = defaultDarkFontColor
}
}
}
labelPainter.Add(labelValue)
}
}
err := doRender(rendererList...)
if err != nil {
return BoxZero, err
}
return p.box, nil
}

View file

@ -26,7 +26,7 @@ import (
"math"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/drawing"
)
type lineChart struct {
@ -70,8 +70,6 @@ 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) {
@ -115,9 +113,6 @@ 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)
@ -152,8 +147,6 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
Value: item.Value,
X: p.X,
Y: p.Y,
// 字体大小
FontSize: series.Label.FontSize,
})
}
// 如果需要填充区域
@ -161,10 +154,6 @@ 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,
@ -173,7 +162,7 @@ func (l *lineChart) render(result *defaultRenderResult, seriesList SeriesList) (
Y: bottomY,
}, areaPoints[0])
seriesPainter.SetDrawingStyle(Style{
FillColor: seriesColor.WithAlpha(opacity),
FillColor: seriesColor.WithAlpha(200),
})
seriesPainter.FillArea(areaPoints)
}

View file

@ -24,6 +24,7 @@ package charts
import (
"github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2"
)
// NewMarkLine returns a series mark line
@ -74,7 +75,7 @@ func (m *markLinePainter) Render() (Box, error) {
}
font := opt.Font
if font == nil {
font, _ = GetDefaultFont()
font, _ = chart.GetDefaultFont()
}
summary := s.Summary()
for _, markLine := range s.MarkLine.Data {

View file

@ -26,7 +26,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestMarkLine(t *testing.T) {

View file

@ -24,6 +24,7 @@ package charts
import (
"github.com/golang/freetype/truetype"
"github.com/wcharczuk/go-chart/v2/drawing"
)
// NewMarkPoint returns a series mark point
@ -77,15 +78,16 @@ 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)

View file

@ -26,7 +26,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestMarkPoint(t *testing.T) {

View file

@ -28,7 +28,7 @@ import (
"math"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type ValueFormatter func(float64) string
@ -59,8 +59,6 @@ type PainterOptions struct {
type PainterOption func(*Painter)
type TicksOption struct {
// the first tick
First int
Length int
Orient string
Count int
@ -73,11 +71,6 @@ type MultiTextOption struct {
Unit int
Position string
Align string
// The text rotation of label
TextRotation float64
Offset Box
// The first text index
First int
}
type GridOption struct {
@ -156,7 +149,7 @@ func NewPainter(opts PainterOptions, opt ...PainterOption) (*Painter, error) {
}
font := opts.Font
if font == nil {
f, err := GetDefaultFont()
f, err := chart.GetDefaultFont()
if err != nil {
return nil, err
}
@ -565,19 +558,6 @@ 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+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()
}
func (p *Painter) TextFit(body string, x, y, width int, textAligns ...string) chart.Box {
style := p.style
textWarp := style.TextWrap
@ -620,7 +600,6 @@ func (p *Painter) Ticks(opt TicksOption) *Painter {
return p
}
count := opt.Count
first := opt.First
width := p.Width()
height := p.Height()
unit := 1
@ -635,10 +614,7 @@ func (p *Painter) Ticks(opt TicksOption) *Painter {
values = autoDivide(width, count)
}
for index, value := range values {
if index < first {
continue
}
if (index-first)%unit != 0 {
if index%unit != 0 {
continue
}
if isVertical {
@ -693,19 +669,10 @@ 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 index < opt.First {
if opt.Unit != 0 && index%opt.Unit != showIndex {
continue
}
if opt.Unit != 0 && (index-opt.First)%opt.Unit != showIndex {
continue
}
if isTextRotation {
p.ClearTextRotation()
p.SetTextRotation(opt.TextRotation)
}
box := p.MeasureText(text)
start := values[index]
if positionCenter {
@ -726,13 +693,8 @@ func (p *Painter) MultiText(opt MultiTextOption) *Painter {
} else {
x = start - box.Width()>>1
}
x += offset.Left
y += offset.Top
p.Text(text, x, y)
}
if isTextRotation {
p.ClearTextRotation()
}
return p
}
@ -803,48 +765,6 @@ 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()
@ -860,7 +780,3 @@ func (p *Painter) LegendLineDot(box Box) *Painter {
p.FillStroke()
return p
}
func (p *Painter) GetRenderer() chart.Renderer {
return p.render
}

View file

@ -28,8 +28,8 @@ import (
"github.com/golang/freetype/truetype"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestPainterOption(t *testing.T) {
@ -343,29 +343,6 @@ func TestPainter(t *testing.T) {
}
}
func TestRoundedRect(t *testing.T) {
assert := assert.New(t)
p, err := NewPainter(PainterOptions{
Width: 400,
Height: 300,
Type: ChartOutputSVG,
})
assert.Nil(err)
p.OverrideDrawingStyle(Style{
FillColor: drawing.ColorWhite,
StrokeWidth: 1,
StrokeColor: drawing.ColorWhite,
}).RoundedRect(Box{
Left: 10,
Right: 30,
Bottom: 150,
Top: 10,
}, 5)
buf, err := p.Bytes()
assert.Nil(err)
assert.Equal("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 15 10\nL 25 10\nL 25 10\nA 5 5 90.00 0 1 30 15\nL 30 145\nL 30 145\nA 5 5 90.00 0 1 25 150\nL 15 150\nL 15 150\nA 5 5 90.00 0 1 10 145\nL 10 15\nL 10 15\nA 5 5 90.00 0 1 15 10\nZ\" style=\"stroke-width:1;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)\"/><path d=\"\" style=\"stroke-width:1;stroke:rgba(255,255,255,1.0);fill:rgba(255,255,255,1.0)\"/></svg>", string(buf))
}
func TestPainterTextFit(t *testing.T) {
assert := assert.New(t)
p, err := NewPainter(PainterOptions{
@ -374,7 +351,7 @@ func TestPainterTextFit(t *testing.T) {
Type: ChartOutputSVG,
})
assert.Nil(err)
f, _ := GetDefaultFont()
f, _ := chart.GetDefaultFont()
style := Style{
FontSize: 12,
FontColor: chart.ColorBlack,

View file

@ -27,7 +27,7 @@ import (
"math"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type pieChart struct {
@ -63,96 +63,6 @@ func NewPieChart(p *Painter, opt PieChartOption) *pieChart {
}
}
type sector struct {
value float64
percent float64
cx int
cy int
rx float64
ry float64
start float64
delta float64
offset int
quadrant int
lineStartX int
lineStartY int
lineBranchX int
lineBranchY int
lineEndX int
lineEndY int
showLabel bool
label string
series Series
color Color
}
func NewSector(cx int, cy int, radius float64, labelRadius float64, value float64, currentValue float64, totalValue float64, labelLineLength int, label string, series Series, color Color) sector {
s := sector{}
s.value = value
s.percent = value / totalValue
s.cx = cx
s.cy = cy
s.rx = radius
s.ry = radius
p := (currentValue + value/2) / totalValue
if p < 0.25 {
s.quadrant = 1
} else if p < 0.5 {
s.quadrant = 4
} else if p < 0.75 {
s.quadrant = 3
} else {
s.quadrant = 2
}
s.start = chart.PercentToRadians(currentValue/totalValue) - math.Pi/2
s.delta = chart.PercentToRadians(value / totalValue)
angle := s.start + s.delta/2
s.lineStartX = cx + int(radius*math.Cos(angle))
s.lineStartY = cy + int(radius*math.Sin(angle))
s.lineBranchX = cx + int(labelRadius*math.Cos(angle))
s.lineBranchY = cy + int(labelRadius*math.Sin(angle))
s.offset = labelLineLength
if s.lineBranchX <= cx {
s.offset *= -1
}
s.lineEndX = s.lineBranchX + s.offset
s.lineEndY = s.lineBranchY
s.series = series
s.color = color
s.showLabel = series.Label.Show
s.label = NewPieLabelFormatter([]string{label}, series.Label.Formatter)(0, s.value, s.percent)
return s
}
func (s *sector) calculateY(prevY int) int {
for i := 0; i <= s.cy; i++ {
if s.quadrant <= 2 {
if (prevY - s.lineBranchY) > labelFontSize+5 {
break
}
s.lineBranchY -= 1
} else {
if (s.lineBranchY - prevY) > labelFontSize+5 {
break
}
s.lineBranchY += 1
}
}
s.lineEndY = s.lineBranchY
return s.lineBranchY
}
func (s *sector) calculateTextXY(textBox Box) (x int, y int) {
textMargin := 3
x = s.lineEndX + textMargin
y = s.lineEndY + textBox.Height()>>1 - 1
if s.offset < 0 {
textWidth := textBox.Width()
x = s.lineEndX - textWidth - textMargin
}
return
}
func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) {
opt := p.opt
values := make([]float64, len(seriesList))
@ -191,103 +101,79 @@ func (p *pieChart) render(result *defaultRenderResult, seriesList SeriesList) (B
theme := opt.Theme
currentValue := float64(0)
var quadrant1, quadrant2, quadrant3, quadrant4 []sector
prevEndX := 0
prevEndY := 0
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)
}
}
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...)
currentQuadrant := 0
prevY := 0
maxY := 0
minY := 0
for _, s := range sectors {
seriesPainter.OverrideDrawingStyle(Style{
StrokeWidth: 1,
StrokeColor: s.color,
FillColor: s.color,
StrokeColor: theme.GetSeriesColor(index),
FillColor: theme.GetSeriesColor(index),
})
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 {
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 {
continue
}
if currentQuadrant != s.quadrant {
if s.quadrant == 1 {
minY = cy * 2
maxY = 0
prevY = cy * 2
// 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坐标位置
if index != 0 &&
math.Abs(float64(endx-prevEndX)) < labelFontSize &&
math.Abs(float64(endy-prevEndY)) < labelFontSize {
endy -= (labelFontSize << 1)
}
if s.quadrant == 2 {
if currentQuadrant != 3 {
prevY = s.lineEndY
} else {
prevY = minY
prevEndX = endx
prevEndY = endy
seriesPainter.MoveTo(startx, starty)
seriesPainter.LineTo(endx, endy)
offset := labelLineWidth
if endx < cx {
offset *= -1
}
}
if s.quadrant == 3 {
if currentQuadrant != 4 {
prevY = s.lineEndY
} else {
minY = cy * 2
maxY = 0
prevY = 0
}
}
if s.quadrant == 4 {
if currentQuadrant != 1 {
prevY = s.lineEndY
} else {
prevY = maxY
}
}
currentQuadrant = s.quadrant
}
prevY = s.calculateY(prevY)
if prevY > maxY {
maxY = prevY
}
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.MoveTo(endx, endy)
endx += offset
seriesPainter.LineTo(endx, endy)
seriesPainter.Stroke()
textStyle := Style{
FontColor: theme.GetTextColor(),
FontSize: labelFontSize,
Font: opt.Font,
}
if !s.series.Label.Color.IsZero() {
textStyle.FontColor = s.series.Label.Color
if !series.Label.Color.IsZero() {
textStyle.FontColor = series.Label.Color
}
seriesPainter.OverrideTextStyle(textStyle)
x, y := s.calculateTextXY(seriesPainter.MeasureText(s.label))
seriesPainter.Text(s.label, x, y)
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)
}
return p.p.box, nil
}

File diff suppressed because one or more lines are too long

View file

@ -25,10 +25,9 @@ package charts
import (
"errors"
"github.com/dustin/go-humanize"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
type radarChart struct {
@ -231,15 +230,9 @@ func (r *radarChart) render(result *defaultRenderResult, seriesList SeriesList)
StrokeColor: color,
FillColor: dotFillColor,
})
for index, point := range linePoints {
for _, 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)
}
}
}

View file

@ -121,9 +121,6 @@ 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))
}

View file

@ -26,7 +26,7 @@ import (
"strings"
"github.com/dustin/go-humanize"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type SeriesData struct {
@ -79,12 +79,6 @@ type SeriesLabel struct {
Show bool
// Distance to the host graphic element.
Distance int
// The position of label
Position string
// The offset of label's position
Offset Box
// The font size of label
FontSize float64
}
const (
@ -126,8 +120,6 @@ 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
@ -281,14 +273,6 @@ 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 {

View file

@ -24,7 +24,7 @@ package charts
import (
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"github.com/wcharczuk/go-chart/v2"
)
type labelRenderValue struct {
@ -32,8 +32,6 @@ type labelRenderValue struct {
Style Style
X int
Y int
// 旋转
Radians float64
}
type LabelValue struct {
@ -41,14 +39,6 @@ type LabelValue struct {
Value float64
X int
Y int
// 旋转
Radians float64
// 字体颜色
FontColor Color
// 字体大小
FontSize float64
Orient string
Offset Box
}
type SeriesLabelPainter struct {
@ -91,58 +81,27 @@ 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
}
if !label.Color.IsZero() {
labelStyle.FontColor = label.Color
}
p := o.p
p.OverrideDrawingStyle(labelStyle)
rotated := value.Radians != 0
if rotated {
p.SetTextRotation(value.Radians)
}
textBox := p.MeasureText(text)
o.p.OverrideDrawingStyle(labelStyle)
textBox := o.p.MeasureText(text)
renderValue := labelRenderValue{
Text: text,
Style: labelStyle,
X: value.X,
Y: value.Y,
Radians: value.Radians,
X: value.X - textBox.Width()>>1,
Y: value.Y - distance,
}
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()
} else {
if textBox.Width()%2 != 0 {
renderValue.X++
}
}
renderValue.X += value.Offset.Left
renderValue.Y += value.Offset.Top
o.values = append(o.values, renderValue)
}
func (o *SeriesLabelPainter) Render() (Box, error) {
for _, item := range o.values {
o.p.OverrideTextStyle(item.Style)
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
}

View file

@ -26,8 +26,8 @@ import (
"errors"
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
type tableChart struct {

View file

@ -24,7 +24,8 @@ package charts
import (
"github.com/golang/freetype/truetype"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
const ThemeDark = "dark"
@ -76,19 +77,6 @@ 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"),
@ -323,7 +311,7 @@ func (t *themeColorPalette) GetFont() *truetype.Font {
if t.font != nil {
return t.font
}
f, _ := GetDefaultFont()
f, _ := chart.GetDefaultFont()
return f
}

11
util.go
View file

@ -29,8 +29,8 @@ import (
"strings"
"github.com/dustin/go-humanize"
"git.smarteching.com/zeni/go-chart/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TrueFlag() *bool {
@ -262,10 +262,3 @@ 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
}

View file

@ -26,8 +26,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"git.smarteching.com/zeni/go-chart/v2"
"git.smarteching.com/zeni/go-chart/v2/drawing"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"
)
func TestGetDefaultInt(t *testing.T) {
@ -189,35 +189,3 @@ 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,
}))
}

View file

@ -48,12 +48,6 @@ type XAxisOption struct {
StrokeColor Color
// The color of label
FontColor Color
// The text rotation of label
TextRotation float64
// The first axis
FirstAxis int
// The offset of label
LabelOffset Box
isValueAxis bool
}
@ -87,9 +81,6 @@ func (opt *XAxisOption) ToAxisOption() AxisOption {
FontColor: opt.FontColor,
Show: opt.Show,
SplitLineColor: opt.Theme.GetAxisSplitLineColor(),
TextRotation: opt.TextRotation,
LabelOffset: opt.LabelOffset,
FirstAxis: opt.FirstAxis,
}
if opt.isValueAxis {
axisOpt.SplitLineShow = true

View file

@ -50,8 +50,6 @@ 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
@ -102,9 +100,6 @@ func (opt *YAxisOption) ToAxisOption(p *Painter) AxisOption {
axisOpt.StrokeWidth = 1
axisOpt.SplitLineShow = false
}
if opt.SplitLineShow != nil {
axisOpt.SplitLineShow = *opt.SplitLineShow
}
return axisOpt
}