chore: support axias ticks render

This commit is contained in:
vicanso 2022-05-24 23:25:08 +08:00
parent ddd5cf6d43
commit 4201c7d439
3 changed files with 165 additions and 69 deletions

View file

@ -37,7 +37,6 @@ type Painter struct {
font *truetype.Font font *truetype.Font
parent *Painter parent *Painter
style Style style Style
previousStyle Style
theme ColorPalette theme ColorPalette
} }
@ -54,6 +53,12 @@ type PainterOptions struct {
type PainterOption func(*Painter) type PainterOption func(*Painter)
type TicksOption struct {
Length int
Orient string
Count int
}
// PainterPaddingOption sets the padding of draw painter // PainterPaddingOption sets the padding of draw painter
func PainterPaddingOption(padding Box) PainterOption { func PainterPaddingOption(padding Box) PainterOption {
return func(p *Painter) { return func(p *Painter) {
@ -87,7 +92,7 @@ func PainterFontOption(font *truetype.Font) PainterOption {
// PainterStyleOption sets the style of draw painter // PainterStyleOption sets the style of draw painter
func PainterStyleOption(style Style) PainterOption { func PainterStyleOption(style Style) PainterOption {
return func(p *Painter) { return func(p *Painter) {
p.SetDrawingStyle(style) p.SetStyle(style)
} }
} }
@ -161,7 +166,6 @@ func (p *Painter) Child(opt ...PainterOption) *Painter {
font: p.font, font: p.font,
parent: p, parent: p,
style: p.style, style: p.style,
previousStyle: p.previousStyle,
theme: p.theme, theme: p.theme,
} }
child.setOptions(opt...) child.setOptions(opt...)
@ -172,29 +176,65 @@ func (p *Painter) SetStyle(style Style) {
if style.Font == nil { if style.Font == nil {
style.Font = p.font style.Font = p.font
} }
p.previousStyle = p.style
p.style = style p.style = style
style.WriteToRenderer(p.render) style.WriteToRenderer(p.render)
} }
func (p *Painter) SetDrawingStyle(style Style) { func overrideStyle(defaultStyle Style, style Style) Style {
p.previousStyle = p.style if style.StrokeWidth == 0 {
p.style = style style.StrokeWidth = defaultStyle.StrokeWidth
style.WriteDrawingOptionsToRenderer(p.render) }
if style.StrokeColor.IsZero() {
style.StrokeColor = defaultStyle.StrokeColor
}
if style.StrokeDashArray == nil {
style.StrokeDashArray = defaultStyle.StrokeDashArray
}
if style.DotColor.IsZero() {
style.DotColor = defaultStyle.DotColor
}
if style.DotWidth == 0 {
style.DotWidth = defaultStyle.DotWidth
}
if style.FillColor.IsZero() {
style.FillColor = defaultStyle.FillColor
}
if style.FontSize == 0 {
style.FontSize = defaultStyle.FontSize
}
if style.FontColor.IsZero() {
style.FontColor = defaultStyle.FontColor
} }
func (p *Painter) SetTextStyle(style Style) {
if style.Font == nil { if style.Font == nil {
style.Font = p.font style.Font = defaultStyle.Font
} }
p.previousStyle = p.style return style
p.style = style
style.WriteTextOptionsToRenderer(p.render)
} }
func (p *Painter) RestoreStyle() { func (p *Painter) OverrideDrawingStyle(style Style) *Painter {
p.style = p.previousStyle s := overrideStyle(p.style, style)
p.SetDrawingStyle(s)
return p
}
func (p *Painter) SetDrawingStyle(style Style) *Painter {
style.WriteDrawingOptionsToRenderer(p.render)
return p
}
func (p *Painter) SetTextStyle(style Style) *Painter {
style.WriteTextOptionsToRenderer(p.render)
return p
}
func (p *Painter) OverrideTextStyle(style Style) *Painter {
s := overrideStyle(p.style, style)
p.SetTextStyle(s)
return p
}
func (p *Painter) ResetStyle() *Painter {
p.style.WriteToRenderer(p.render) p.style.WriteToRenderer(p.render)
return p
} }
// Bytes returns the data of draw canvas // Bytes returns the data of draw canvas
@ -208,19 +248,22 @@ func (p *Painter) Bytes() ([]byte, error) {
} }
// MoveTo moves the cursor to a given point // MoveTo moves the cursor to a given point
func (p *Painter) MoveTo(x, y int) { func (p *Painter) MoveTo(x, y int) *Painter {
p.render.MoveTo(x+p.box.Left, y+p.box.Top) p.render.MoveTo(x+p.box.Left, y+p.box.Top)
return p
} }
func (p *Painter) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) { func (p *Painter) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) *Painter {
p.render.ArcTo(cx+p.box.Left, cy+p.box.Top, rx, ry, startAngle, delta) p.render.ArcTo(cx+p.box.Left, cy+p.box.Top, rx, ry, startAngle, delta)
return p
} }
func (p *Painter) LineTo(x, y int) { func (p *Painter) LineTo(x, y int) *Painter {
p.render.LineTo(x+p.box.Left, y+p.box.Top) p.render.LineTo(x+p.box.Left, y+p.box.Top)
return p
} }
func (p *Painter) Pin(x, y, width int) { func (p *Painter) Pin(x, y, width int) *Painter {
r := float64(width) / 2 r := float64(width) / 2
y -= width / 4 y -= width / 4
angle := chart.DegreesToRadians(15) angle := chart.DegreesToRadians(15)
@ -246,9 +289,10 @@ func (p *Painter) Pin(x, y, width int) {
p.render.QuadCurveTo(cx+left, cy+top, endX+left, endY+top) p.render.QuadCurveTo(cx+left, cy+top, endX+left, endY+top)
p.Close() p.Close()
p.Fill() p.Fill()
return p
} }
func (p *Painter) arrow(x, y, width, height int, direction string) { func (p *Painter) arrow(x, y, width, height int, direction string) *Painter {
halfWidth := width >> 1 halfWidth := width >> 1
halfHeight := height >> 1 halfHeight := height >> 1
if direction == PositionTop || direction == PositionBottom { if direction == PositionTop || direction == PositionBottom {
@ -284,41 +328,51 @@ func (p *Painter) arrow(x, y, width, height int, direction string) {
p.LineTo(x0, y0) p.LineTo(x0, y0)
} }
p.FillStroke() p.FillStroke()
return p
} }
func (p *Painter) ArrowLeft(x, y, width, height int) { func (p *Painter) ArrowLeft(x, y, width, height int) *Painter {
p.arrow(x, y, width, height, PositionLeft) p.arrow(x, y, width, height, PositionLeft)
return p
} }
func (p *Painter) ArrowRight(x, y, width, height int) { func (p *Painter) ArrowRight(x, y, width, height int) *Painter {
p.arrow(x, y, width, height, PositionRight) p.arrow(x, y, width, height, PositionRight)
return p
} }
func (p *Painter) ArrowTop(x, y, width, height int) { func (p *Painter) ArrowTop(x, y, width, height int) *Painter {
p.arrow(x, y, width, height, PositionTop) p.arrow(x, y, width, height, PositionTop)
return p
} }
func (p *Painter) ArrowBottom(x, y, width, height int) { func (p *Painter) ArrowBottom(x, y, width, height int) *Painter {
p.arrow(x, y, width, height, PositionBottom) p.arrow(x, y, width, height, PositionBottom)
return p
} }
func (p *Painter) Circle(radius float64, x, y int) { func (p *Painter) Circle(radius float64, x, y int) *Painter {
p.render.Circle(radius, x+p.box.Left, y+p.box.Top) p.render.Circle(radius, x+p.box.Left, y+p.box.Top)
return p
} }
func (p *Painter) Stroke() { func (p *Painter) Stroke() *Painter {
p.render.Stroke() p.render.Stroke()
return p
} }
func (p *Painter) Close() { func (p *Painter) Close() *Painter {
p.render.Close() p.render.Close()
return p
} }
func (p *Painter) FillStroke() { func (p *Painter) FillStroke() *Painter {
p.render.FillStroke() p.render.FillStroke()
return p
} }
func (p *Painter) Fill() { func (p *Painter) Fill() *Painter {
p.render.Fill() p.render.Fill()
return p
} }
func (p *Painter) Width() int { func (p *Painter) Width() int {
@ -333,11 +387,7 @@ func (p *Painter) MeasureText(text string) Box {
return p.render.MeasureText(text) return p.render.MeasureText(text)
} }
func (p *Painter) SetStrokeColor(color Color) { func (p *Painter) LineStroke(points []Point) *Painter {
p.render.SetStrokeColor(color)
}
func (p *Painter) LineStroke(points []Point) {
for index, point := range points { for index, point := range points {
x := point.X x := point.X
y := point.Y y := point.Y
@ -348,16 +398,17 @@ func (p *Painter) LineStroke(points []Point) {
} }
} }
p.Stroke() p.Stroke()
return p
} }
func (p *Painter) SetBackground(width, height int, color Color) { func (p *Painter) SetBackground(width, height int, color Color) *Painter {
r := p.render r := p.render
s := chart.Style{ s := chart.Style{
FillColor: color, FillColor: color,
} }
// 背景色 // 背景色
p.SetStyle(s) p.SetDrawingStyle(s)
defer p.RestoreStyle() defer p.ResetStyle()
// 设置背景色不使用box因此不直接使用Painter // 设置背景色不使用box因此不直接使用Painter
r.MoveTo(0, 0) r.MoveTo(0, 0)
r.LineTo(width, 0) r.LineTo(width, 0)
@ -365,21 +416,23 @@ func (p *Painter) SetBackground(width, height int, color Color) {
r.LineTo(0, height) r.LineTo(0, height)
r.LineTo(0, 0) r.LineTo(0, 0)
p.FillStroke() p.FillStroke()
return p
} }
func (p *Painter) MarkLine(x, y, width int) { func (p *Painter) MarkLine(x, y, width int) *Painter {
arrowWidth := 16 arrowWidth := 16
arrowHeight := 10 arrowHeight := 10
endX := x + width endX := x + width
p.Circle(3, x, y) radius := 3
p.Circle(3, x+radius, y)
p.render.Fill() p.render.Fill()
p.MoveTo(x+5, y) p.MoveTo(x+radius*3, y)
p.LineTo(endX-arrowWidth, y) p.LineTo(endX-arrowWidth, y)
p.Stroke() p.Stroke()
p.render.SetStrokeDashArray([]float64{})
p.ArrowRight(endX, y, arrowWidth, arrowHeight) p.ArrowRight(endX, y, arrowWidth, arrowHeight)
return p
} }
func (p *Painter) Polygon(center Point, radius float64, sides int) { func (p *Painter) Polygon(center Point, radius float64, sides int) *Painter {
points := getPolygonPoints(center, radius, sides) points := getPolygonPoints(center, radius, sides)
for i, item := range points { for i, item := range points {
if i == 0 { if i == 0 {
@ -390,9 +443,10 @@ func (p *Painter) Polygon(center Point, radius float64, sides int) {
} }
p.LineTo(points[0].X, points[0].Y) p.LineTo(points[0].X, points[0].Y)
p.Stroke() p.Stroke()
return p
} }
func (p *Painter) FillArea(points []Point) { func (p *Painter) FillArea(points []Point) *Painter {
var x, y int var x, y int
for index, point := range points { for index, point := range points {
x = point.X x = point.X
@ -404,10 +458,12 @@ func (p *Painter) FillArea(points []Point) {
} }
} }
p.Fill() p.Fill()
return p
} }
func (p *Painter) Text(body string, x, y int) { func (p *Painter) Text(body string, x, y int) *Painter {
p.render.Text(body, x+p.box.Left, y+p.box.Top) p.render.Text(body, x+p.box.Left, y+p.box.Top)
return p
} }
func (p *Painter) TextFit(body string, x, y, width int) chart.Box { func (p *Painter) TextFit(body string, x, y, width int) chart.Box {
@ -433,3 +489,46 @@ func (p *Painter) TextFit(body string, x, y, width int) chart.Box {
p.style.TextWrap = textWarp p.style.TextWrap = textWarp
return output return output
} }
func (p *Painter) Ticks(opt TicksOption) *Painter {
if opt.Count <= 0 || opt.Length <= 0 {
return p
}
count := opt.Count - 1
width := p.Width()
height := p.Height()
var values []int
if opt.Orient == OrientHorizontal {
values = autoDivide(height, count)
} else {
values = autoDivide(width, count)
}
for _, value := range values {
if opt.Orient == OrientVertical {
p.LineStroke([]Point{
{
X: 0,
Y: value,
},
{
X: opt.Length,
Y: value,
},
})
} else {
p.LineStroke([]Point{
{
X: value,
Y: opt.Length,
},
{
X: value,
Y: 0,
},
})
}
}
return p
}

View file

@ -271,7 +271,7 @@ func TestPainter(t *testing.T) {
}) })
p.MarkLine(0, 20, 300) p.MarkLine(0, 20, 300)
}, },
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<circle cx=\"5\" cy=\"30\" r=\"3\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"M 10 30\nL 289 30\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path d=\"M 289 25\nL 305 30\nL 289 35\nL 294 30\nL 289 25\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/></svg>", result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<circle cx=\"8\" cy=\"30\" r=\"3\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"M 14 30\nL 289 30\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"M 289 25\nL 305 30\nL 289 35\nL 294 30\nL 289 25\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/></svg>",
}, },
// polygon // polygon
{ {

15
util.go
View file

@ -59,19 +59,16 @@ func getDefaultInt(value, defaultValue int) int {
} }
func autoDivide(max, size int) []int { func autoDivide(max, size int) []int {
unit := max / size unit := float64(max) / float64(size)
rest := max - unit*size
values := make([]int, size+1) values := make([]int, size+1)
value := 0 for i := 0; i < size+1; i++ {
for i := 0; i < size; i++ { if i == size {
values[i] = value values[i] = max
if i < rest { } else {
value++ values[i] = int(float64(i) * unit)
} }
value += unit
} }
values[size] = max
return values return values
} }