diff --git a/axis.go b/axis.go index 46292e4..5881f5e 100644 --- a/axis.go +++ b/axis.go @@ -27,7 +27,6 @@ import ( "github.com/golang/freetype/truetype" "github.com/wcharczuk/go-chart/v2" - "github.com/wcharczuk/go-chart/v2/drawing" ) type AxisOption struct { @@ -42,7 +41,7 @@ type AxisOption struct { SplitNumber int ClassName string // The line color of axis - StrokeColor drawing.Color + StrokeColor Color // The line width StrokeWidth float64 // The length of the axis tick @@ -56,17 +55,17 @@ type AxisOption struct { // The font of label Font *truetype.Font // The color of label - FontColor drawing.Color + FontColor Color // The flag for show axis split line, set this to true will show axis split line SplitLineShow bool // The color of split line - SplitLineColor drawing.Color + SplitLineColor Color } type axis struct { - d *Draw - data *AxisDataList - option *AxisOption + painter *Painter + data *AxisDataList + option *AxisOption } type axisMeasurement struct { Width int @@ -74,11 +73,11 @@ type axisMeasurement struct { } // NewAxis creates a new axis with data and style options -func NewAxis(d *Draw, data AxisDataList, option AxisOption) *axis { +func NewAxis(p *Painter, data AxisDataList, option AxisOption) *axis { return &axis{ - d: d, - data: &data, - option: &option, + painter: p, + data: &data, + option: &option, } } @@ -149,17 +148,19 @@ func NewAxisDataListFromStringList(textList []string) AxisDataList { func (a *axis) axisLabel(renderOpt *axisRenderOption) { option := a.option data := *a.data - d := a.d + // d := a.d if option.FontColor.IsZero() || len(data) == 0 { return } - r := d.Render + // r := d.Render - s := option.Style(d.Font) - s.GetTextOptions().WriteTextOptionsToRenderer(r) + // s.GetTextOptions().WriteTextOptionsToRenderer(r) + p := a.painter + s := option.Style(p.font) + p.SetTextStyle(s) - width := d.Box.Width() - height := d.Box.Height() + width := p.Width() + height := p.Height() textList := data.TextList() count := len(textList) @@ -188,7 +189,7 @@ func (a *axis) axisLabel(renderOpt *axisRenderOption) { reverseIntSlice(values) for index, text := range textList { y := values[index] - 2 - b := r.MeasureText(text) + b := p.MeasureText(text) if boundaryGap { height := y - values[index+1] y -= (height - b.Height()) >> 1 @@ -200,7 +201,7 @@ func (a *axis) axisLabel(renderOpt *axisRenderOption) { if position == PositionLeft { x = labelWidth - b.Width() - 1 } - d.text(text, x, y) + p.Text(text, x, y) } default: // 定位bottom,重新计算y0的定位 @@ -215,7 +216,7 @@ func (a *axis) axisLabel(renderOpt *axisRenderOption) { } x := values[index] leftOffset := 0 - b := r.MeasureText(text) + b := p.MeasureText(text) if boundaryGap { width := values[index+1] - x leftOffset = (width - b.Width()) >> 1 @@ -223,24 +224,25 @@ func (a *axis) axisLabel(renderOpt *axisRenderOption) { // 左移文本长度 leftOffset = -b.Width() >> 1 } - d.text(text, x+leftOffset, y0) + p.Text(text, x+leftOffset, y0) } } } func (a *axis) axisLine(renderOpt *axisRenderOption) { - d := a.d - r := d.Render + // d := a.d + // r := d.Render + p := a.painter option := a.option - s := option.Style(d.Font) - s.GetStrokeOptions().WriteDrawingOptionsToRenderer(r) + s := option.Style(p.font) + p.SetDrawingStyle(s.GetStrokeOptions()) x0 := 0 y0 := 0 x1 := 0 y1 := 0 - width := d.Box.Width() - height := d.Box.Height() + width := p.Width() + height := p.Height() labelMargin := option.GetLabelMargin() // 轴线 @@ -271,21 +273,22 @@ func (a *axis) axisLine(renderOpt *axisRenderOption) { y1 = y0 } - d.moveTo(x0, y0) - d.lineTo(x1, y1) - r.FillStroke() + p.MoveTo(x0, y0) + p.LineTo(x1, y1) + p.FillStroke() } func (a *axis) axisTick(renderOpt *axisRenderOption) { - d := a.d - r := d.Render + // d := a.d + // r := d.Render + p := a.painter option := a.option - s := option.Style(d.Font) - s.GetStrokeOptions().WriteDrawingOptionsToRenderer(r) + s := option.Style(p.font) + p.SetDrawingStyle(s.GetStrokeOptions()) - width := d.Box.Width() - height := d.Box.Height() + width := p.Width() + height := p.Height() data := *a.data tickCount := len(data) if tickCount == 0 { @@ -319,14 +322,14 @@ func (a *axis) axisTick(renderOpt *axisRenderOption) { for _, v := range values { x := x0 y := v - d.moveTo(x, y) - d.lineTo(x+tickLengthValue, y) - r.Stroke() + p.MoveTo(x, y) + p.LineTo(x+tickLengthValue, y) + p.Stroke() } } // 辅助线 if option.SplitLineShow && !option.SplitLineColor.IsZero() { - r.SetStrokeColor(option.SplitLineColor) + p.SetStrokeColor(option.SplitLineColor) splitLineWidth := width - labelWidth - tickLengthValue x0 = labelWidth + tickLengthValue if position == PositionRight { @@ -336,9 +339,9 @@ func (a *axis) axisTick(renderOpt *axisRenderOption) { for _, v := range values[0 : len(values)-1] { x := x0 y := v - d.moveTo(x, y) - d.lineTo(x+splitLineWidth, y) - r.Stroke() + p.MoveTo(x, y) + p.LineTo(x+splitLineWidth, y) + p.Stroke() } } default: @@ -355,14 +358,14 @@ func (a *axis) axisTick(renderOpt *axisRenderOption) { } x := v y := y0 - d.moveTo(x, y-tickLengthValue) - d.lineTo(x, y) - r.Stroke() + p.MoveTo(x, y-tickLengthValue) + p.LineTo(x, y) + p.Stroke() } } // 辅助线 if option.SplitLineShow && !option.SplitLineColor.IsZero() { - r.SetStrokeColor(option.SplitLineColor) + p.SetStrokeColor(option.SplitLineColor) y0 = 0 splitLineHeight := height - labelHeight - tickLengthValue if position == PositionTop { @@ -377,22 +380,23 @@ func (a *axis) axisTick(renderOpt *axisRenderOption) { x := v y := y0 - d.moveTo(x, y) - d.lineTo(x, y0+splitLineHeight) - r.Stroke() + p.MoveTo(x, y) + p.LineTo(x, y0+splitLineHeight) + p.Stroke() } } } } func (a *axis) measureTextMaxWidthHeight() (int, int) { - d := a.d - r := d.Render - s := a.option.Style(d.Font) + // d := a.d + // r := d.Render + p := a.painter + s := a.option.Style(p.font) data := a.data - s.GetStrokeOptions().WriteDrawingOptionsToRenderer(r) - s.GetTextOptions().WriteTextOptionsToRenderer(r) - return measureTextMaxWidthHeight(data.TextList(), r) + p.SetDrawingStyle(s.GetStrokeOptions()) + p.SetTextStyle(s.GetTextOptions()) + return measureTextMaxWidthHeight(data.TextList(), p) } // measure returns the measurement of axis. @@ -429,7 +433,7 @@ func (a *axis) Render() { } unitCount := chart.MaxInt(option.SplitNumber, 1) - width := a.d.Box.Width() + width := a.painter.Width() textList := a.data.TextList() count := len(textList) diff --git a/axis_test.go b/axis_test.go index 37c8314..fe253e9 100644 --- a/axis_test.go +++ b/axis_test.go @@ -207,10 +207,10 @@ func TestAxis(t *testing.T) { }, } for _, tt := range tests { - d, err := NewDraw(DrawOption{ + p, err := NewPainter(PainterOptions{ Width: 400, Height: 300, - }, PaddingOption(chart.Box{ + }, PainterPaddingOption(chart.Box{ Left: 5, Top: 5, Right: 5, @@ -222,9 +222,9 @@ func TestAxis(t *testing.T) { if tt.newData != nil { data = tt.newData() } - NewAxis(d, data, style).Render() + NewAxis(p, data, style).Render() - result, err := d.Bytes() + result, err := p.Bytes() assert.Nil(err) assert.Equal(tt.result, string(result)) } @@ -233,7 +233,7 @@ func TestAxis(t *testing.T) { func TestMeasureAxis(t *testing.T) { assert := assert.New(t) - d, err := NewDraw(DrawOption{ + p, err := NewPainter(PainterOptions{ Width: 400, Height: 300, }) @@ -243,14 +243,14 @@ func TestMeasureAxis(t *testing.T) { "Sun", }) f, _ := chart.GetDefaultFont() - width := NewAxis(d, data, AxisOption{ + width := NewAxis(p, data, AxisOption{ FontSize: 12, Font: f, Position: PositionLeft, }).measure().Width assert.Equal(44, width) - height := NewAxis(d, data, AxisOption{ + height := NewAxis(p, data, AxisOption{ FontSize: 12, Font: f, Position: PositionTop, diff --git a/painer.go b/painter.go similarity index 88% rename from painer.go rename to painter.go index 670f6f6..639371e 100644 --- a/painer.go +++ b/painter.go @@ -38,14 +38,15 @@ type Painter struct { parent *Painter style Style previousStyle Style + theme *Theme } type PainterOptions struct { // Draw type, "svg" or "png", default type is "svg" Type string - // The width of draw canvas + // The width of draw painter Width int - // The height of draw canvas + // The height of draw painter Height int // The font for painter Font *truetype.Font @@ -53,7 +54,7 @@ type PainterOptions struct { type PainterOption func(*Painter) -// PainterPaddingOption sets the padding of draw canvas +// PainterPaddingOption sets the padding of draw painter func PainterPaddingOption(padding Box) PainterOption { return func(p *Painter) { p.box.Left += padding.Left @@ -63,7 +64,7 @@ func PainterPaddingOption(padding Box) PainterOption { } } -// PainterBoxOption sets the box of draw canvas +// PainterBoxOption sets the box of draw painter func PainterBoxOption(box Box) PainterOption { return func(p *Painter) { if box.IsZero() { @@ -73,21 +74,45 @@ func PainterBoxOption(box Box) PainterOption { } } -// PainterFontOption sets the font of draw canvas +// PainterFontOption sets the font of draw painter func PainterFontOption(font *truetype.Font) PainterOption { return func(p *Painter) { + if font == nil { + return + } p.font = font } } -// PainterStyleOption sets the style of draw canvas +// PainterStyleOption sets the style of draw painter func PainterStyleOption(style Style) PainterOption { return func(p *Painter) { p.SetDrawingStyle(style) } } -// NewPainter creates a new Painter +// PainterThemeOption sets the theme of draw painter +func PainterThemeOption(theme *Theme) PainterOption { + return func(p *Painter) { + if theme == nil { + return + } + p.theme = theme + } +} + +func PainterWidthHeightOption(width, height int) PainterOption { + return func(p *Painter) { + if width > 0 { + p.box.Right = p.box.Left + width + } + if height > 0 { + p.box.Bottom = p.box.Top + height + } + } +} + +// NewPainter creates a new painter func NewPainter(opts PainterOptions, opt ...PainterOption) (*Painter, error) { if opts.Width <= 0 || opts.Height <= 0 { return nil, errors.New("width/height can not be nil") @@ -135,6 +160,7 @@ func (p *Painter) Child(opt ...PainterOption) *Painter { parent: p, style: p.style, previousStyle: p.previousStyle, + theme: p.theme, } child.setOptions(opt...) return child @@ -286,6 +312,22 @@ func (p *Painter) Fill() { p.render.Fill() } +func (p *Painter) Width() int { + return p.box.Width() +} + +func (p *Painter) Height() int { + return p.box.Height() +} + +func (p *Painter) MeasureText(text string) Box { + return p.render.MeasureText(text) +} + +func (p *Painter) SetStrokeColor(color Color) { + p.render.SetStrokeColor(color) +} + func (p *Painter) LineStroke(points []Point, style LineStyle) { s := style.Style() if !s.ShouldDrawStroke() { diff --git a/painer_test.go b/painter_test.go similarity index 100% rename from painer_test.go rename to painter_test.go diff --git a/util.go b/util.go index c895cc3..d35b4b0 100644 --- a/util.go +++ b/util.go @@ -76,11 +76,11 @@ func autoDivide(max, size int) []int { } // measureTextMaxWidthHeight returns maxWidth and maxHeight of text list -func measureTextMaxWidthHeight(textList []string, r chart.Renderer) (int, int) { +func measureTextMaxWidthHeight(textList []string, p *Painter) (int, int) { maxWidth := 0 maxHeight := 0 for _, text := range textList { - box := r.MeasureText(text) + box := p.MeasureText(text) maxWidth = chart.MaxInt(maxWidth, box.Width()) maxHeight = chart.MaxInt(maxHeight, box.Height()) } diff --git a/xaxis.go b/xaxis.go index edd017f..d79f40e 100644 --- a/xaxis.go +++ b/xaxis.go @@ -24,7 +24,6 @@ package charts import ( "github.com/golang/freetype/truetype" - "github.com/wcharczuk/go-chart/v2" ) type XAxisOption struct { @@ -53,27 +52,19 @@ func NewXAxisOption(data []string, boundaryGap ...*bool) XAxisOption { } // drawXAxis draws x axis, and returns the height, range of if. -func drawXAxis(p *Draw, opt *XAxisOption, yAxisCount int) (int, *Range, error) { +func drawXAxis(p *Painter, opt *XAxisOption, yAxisCount int) (int, *Range, error) { if opt.Hidden { return 0, nil, nil } left := YAxisWidth right := (yAxisCount - 1) * YAxisWidth - dXAxis, err := NewDraw( - DrawOption{ - Parent: p, - }, - PaddingOption(chart.Box{ + pXAxis := p.Child( + PainterPaddingOption(Box{ Left: left, Right: right, }), + PainterFontOption(opt.Font), ) - if opt.Font != nil { - dXAxis.Font = opt.Font - } - if err != nil { - return 0, nil, err - } theme := NewTheme(opt.Theme) data := NewAxisDataListFromStringList(opt.Data) style := AxisOption{ @@ -90,13 +81,13 @@ func drawXAxis(p *Draw, opt *XAxisOption, yAxisCount int) (int, *Range, error) { boundary = false max-- } - axis := NewAxis(dXAxis, data, style) + axis := NewAxis(pXAxis, data, style) axis.Render() return axis.measure().Height, &Range{ divideCount: len(opt.Data), Min: 0, Max: max, - Size: dXAxis.Box.Width(), + Size: pXAxis.Width(), Boundary: boundary, }, nil } diff --git a/yaxis.go b/yaxis.go index a14e409..5d55440 100644 --- a/yaxis.go +++ b/yaxis.go @@ -45,7 +45,7 @@ type YAxisOption struct { // TODO 长度是否可以变化 const YAxisWidth = 40 -func drawYAxis(p *Draw, opt *ChartOption, axisIndex, xAxisHeight int, padding chart.Box) (*Range, error) { +func drawYAxis(p *Painter, opt *ChartOption, axisIndex, xAxisHeight int, padding chart.Box) (*Range, error) { theme := NewTheme(opt.Theme) yRange := opt.newYRange(axisIndex) values := yRange.Values() @@ -74,32 +74,22 @@ func drawYAxis(p *Draw, opt *ChartOption, axisIndex, xAxisHeight int, padding ch width := NewAxis(p, data, style).measure().Width yAxisCount := len(opt.YAxisList) - boxWidth := p.Box.Width() + boxWidth := p.Width() if axisIndex > 0 { style.SplitLineShow = false style.Position = PositionRight padding.Right += (axisIndex - 1) * YAxisWidth } else { - boxWidth = p.Box.Width() - (yAxisCount-1)*YAxisWidth + boxWidth = p.Width() - (yAxisCount-1)*YAxisWidth padding.Left += (YAxisWidth - width) } - dYAxis, err := NewDraw( - DrawOption{ - Parent: p, - Width: boxWidth, - // 减去x轴的高 - Height: p.Box.Height() - xAxisHeight, - }, - PaddingOption(padding), + pYAxis := p.Child( + PainterWidthHeightOption(boxWidth, p.Height()-xAxisHeight), + PainterPaddingOption(padding), + PainterFontOption(opt.Font), ) - if err != nil { - return nil, err - } - if opt.Font != nil { - dYAxis.Font = opt.Font - } - NewAxis(dYAxis, data, style).Render() - yRange.Size = dYAxis.Box.Height() + NewAxis(pYAxis, data, style).Render() + yRange.Size = pYAxis.Height() return &yRange, nil }