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
parent *Painter
style Style
previousStyle Style
theme ColorPalette
}
@ -54,6 +53,12 @@ type PainterOptions struct {
type PainterOption func(*Painter)
type TicksOption struct {
Length int
Orient string
Count int
}
// PainterPaddingOption sets the padding of draw painter
func PainterPaddingOption(padding Box) PainterOption {
return func(p *Painter) {
@ -87,7 +92,7 @@ func PainterFontOption(font *truetype.Font) PainterOption {
// PainterStyleOption sets the style of draw painter
func PainterStyleOption(style Style) PainterOption {
return func(p *Painter) {
p.SetDrawingStyle(style)
p.SetStyle(style)
}
}
@ -161,7 +166,6 @@ func (p *Painter) Child(opt ...PainterOption) *Painter {
font: p.font,
parent: p,
style: p.style,
previousStyle: p.previousStyle,
theme: p.theme,
}
child.setOptions(opt...)
@ -172,29 +176,65 @@ func (p *Painter) SetStyle(style Style) {
if style.Font == nil {
style.Font = p.font
}
p.previousStyle = p.style
p.style = style
style.WriteToRenderer(p.render)
}
func (p *Painter) SetDrawingStyle(style Style) {
p.previousStyle = p.style
p.style = style
style.WriteDrawingOptionsToRenderer(p.render)
func overrideStyle(defaultStyle Style, style Style) Style {
if style.StrokeWidth == 0 {
style.StrokeWidth = defaultStyle.StrokeWidth
}
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 {
style.Font = p.font
style.Font = defaultStyle.Font
}
p.previousStyle = p.style
p.style = style
style.WriteTextOptionsToRenderer(p.render)
return style
}
func (p *Painter) RestoreStyle() {
p.style = p.previousStyle
func (p *Painter) OverrideDrawingStyle(style Style) *Painter {
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)
return p
}
// 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
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)
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)
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)
return p
}
func (p *Painter) Pin(x, y, width int) {
func (p *Painter) Pin(x, y, width int) *Painter {
r := float64(width) / 2
y -= width / 4
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.Close()
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
halfHeight := height >> 1
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.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)
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)
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)
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)
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)
return p
}
func (p *Painter) Stroke() {
func (p *Painter) Stroke() *Painter {
p.render.Stroke()
return p
}
func (p *Painter) Close() {
func (p *Painter) Close() *Painter {
p.render.Close()
return p
}
func (p *Painter) FillStroke() {
func (p *Painter) FillStroke() *Painter {
p.render.FillStroke()
return p
}
func (p *Painter) Fill() {
func (p *Painter) Fill() *Painter {
p.render.Fill()
return p
}
func (p *Painter) Width() int {
@ -333,11 +387,7 @@ 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) {
func (p *Painter) LineStroke(points []Point) *Painter {
for index, point := range points {
x := point.X
y := point.Y
@ -348,16 +398,17 @@ func (p *Painter) LineStroke(points []Point) {
}
}
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
s := chart.Style{
FillColor: color,
}
// 背景色
p.SetStyle(s)
defer p.RestoreStyle()
p.SetDrawingStyle(s)
defer p.ResetStyle()
// 设置背景色不使用box因此不直接使用Painter
r.MoveTo(0, 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, 0)
p.FillStroke()
return p
}
func (p *Painter) MarkLine(x, y, width int) {
func (p *Painter) MarkLine(x, y, width int) *Painter {
arrowWidth := 16
arrowHeight := 10
endX := x + width
p.Circle(3, x, y)
radius := 3
p.Circle(3, x+radius, y)
p.render.Fill()
p.MoveTo(x+5, y)
p.MoveTo(x+radius*3, y)
p.LineTo(endX-arrowWidth, y)
p.Stroke()
p.render.SetStrokeDashArray([]float64{})
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)
for i, item := range points {
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.Stroke()
return p
}
func (p *Painter) FillArea(points []Point) {
func (p *Painter) FillArea(points []Point) *Painter {
var x, y int
for index, point := range points {
x = point.X
@ -404,10 +458,12 @@ func (p *Painter) FillArea(points []Point) {
}
}
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)
return p
}
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
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)
},
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
{

15
util.go
View file

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