snapshot.
This commit is contained in:
parent
b78f2327aa
commit
8f56e5939b
10 changed files with 105 additions and 44 deletions
|
@ -14,13 +14,31 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
graph := chart.Chart{
|
graph := chart.Chart{
|
||||||
YAxis: chart.YAxis{
|
YAxis: chart.YAxis{
|
||||||
Style: chart.Style{
|
Style: chart.Style{
|
||||||
Show: true,
|
Show: true,
|
||||||
TextRotationDegrees: 45.0,
|
|
||||||
},
|
},
|
||||||
Range: &chart.ContinuousRange{
|
Range: &chart.ContinuousRange{
|
||||||
Min: 0.0,
|
Min: 0.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
},
|
},
|
||||||
|
TickStyle: chart.Style{
|
||||||
|
TextRotationDegrees: 45.0,
|
||||||
|
},
|
||||||
|
Ticks: []chart.Tick{
|
||||||
|
{0.0, "0.00"},
|
||||||
|
{2.0, "2.00"},
|
||||||
|
{4.0, "4.00"},
|
||||||
|
{6.0, "6.00"},
|
||||||
|
{8.0, "Eight"},
|
||||||
|
{10.0, "Ten"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
XAxis: chart.XAxis{
|
||||||
|
Style: chart.Style{
|
||||||
|
Show: true,
|
||||||
|
},
|
||||||
|
TickStyle: chart.Style{
|
||||||
|
TextRotationDegrees: 45.0,
|
||||||
|
},
|
||||||
Ticks: []chart.Tick{
|
Ticks: []chart.Tick{
|
||||||
{0.0, "0.00"},
|
{0.0, "0.00"},
|
||||||
{2.0, "2.00"},
|
{2.0, "2.00"},
|
||||||
|
@ -39,6 +57,20 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Header().Set("Content-Type", "image/png")
|
res.Header().Set("Content-Type", "image/png")
|
||||||
|
graph.Elements = []chart.Renderable{
|
||||||
|
func(r chart.Renderer, cb chart.Box, defaults chart.Style) {
|
||||||
|
|
||||||
|
b := chart.Box{50, 50, 90, 110}
|
||||||
|
chart.Draw.Box(r, b, chart.Style{
|
||||||
|
StrokeWidth: 2,
|
||||||
|
StrokeColor: chart.ColorBlue,
|
||||||
|
})
|
||||||
|
chart.Draw.Box(r, b.Rotate(chart.Math.DegreesToRadians(45)), chart.Style{
|
||||||
|
StrokeWidth: 2,
|
||||||
|
StrokeColor: chart.ColorRed,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
graph.Render(chart.PNG, res)
|
graph.Render(chart.PNG, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
box.go
6
box.go
|
@ -76,8 +76,8 @@ func (b Box) Height() int {
|
||||||
|
|
||||||
// Center returns the center of the box
|
// Center returns the center of the box
|
||||||
func (b Box) Center() (x, y int) {
|
func (b Box) Center() (x, y int) {
|
||||||
w, h := b.Width(), b.Height()
|
w2, h2 := b.Width()>>1, b.Height()>>1
|
||||||
return b.Left + w>>1, b.Top + h>>1
|
return b.Left + w2, b.Top + h2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aspect returns the aspect ratio of the box.
|
// Aspect returns the aspect ratio of the box.
|
||||||
|
@ -227,8 +227,8 @@ func (b Box) Rotate(radians float64) Box {
|
||||||
ltx, lty := Math.RotateCoordinate(cx, cy, b.Left, b.Top, radians)
|
ltx, lty := Math.RotateCoordinate(cx, cy, b.Left, b.Top, radians)
|
||||||
rbx, rby := Math.RotateCoordinate(cx, cy, b.Right, b.Bottom, radians)
|
rbx, rby := Math.RotateCoordinate(cx, cy, b.Right, b.Bottom, radians)
|
||||||
return Box{
|
return Box{
|
||||||
Top: lty,
|
|
||||||
Left: ltx,
|
Left: ltx,
|
||||||
|
Top: lty,
|
||||||
Right: rbx,
|
Right: rbx,
|
||||||
Bottom: rby,
|
Bottom: rby,
|
||||||
}
|
}
|
||||||
|
|
18
box_test.go
18
box_test.go
|
@ -144,6 +144,20 @@ func TestBoxShift(t *testing.T) {
|
||||||
assert.Equal(12, shifted.Bottom)
|
assert.Equal(12, shifted.Bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBoxCenter(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
b := Box{
|
||||||
|
Top: 10,
|
||||||
|
Left: 10,
|
||||||
|
Right: 20,
|
||||||
|
Bottom: 30,
|
||||||
|
}
|
||||||
|
cx, cy := b.Center()
|
||||||
|
assert.Equal(15, cx)
|
||||||
|
assert.Equal(20, cy)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBoxRotate(t *testing.T) {
|
func TestBoxRotate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -156,6 +170,6 @@ func TestBoxRotate(t *testing.T) {
|
||||||
rotated := b.Rotate(Math.DegreesToRadians(45))
|
rotated := b.Rotate(Math.DegreesToRadians(45))
|
||||||
assert.Equal(1, rotated.Top)
|
assert.Equal(1, rotated.Top)
|
||||||
assert.Equal(4, rotated.Left)
|
assert.Equal(4, rotated.Left)
|
||||||
assert.Equal(11, rotated.Right)
|
assert.Equal(10, rotated.Right)
|
||||||
assert.Equal(15, rotated.Bottom)
|
assert.Equal(14, rotated.Bottom)
|
||||||
}
|
}
|
||||||
|
|
8
math.go
8
math.go
|
@ -223,9 +223,9 @@ func (m mathUtil) CirclePoint(cx, cy int, radius, thetaRadians float64) (x, y in
|
||||||
|
|
||||||
func (m mathUtil) RotateCoordinate(cx, cy, x, y int, thetaRadians float64) (rx, ry int) {
|
func (m mathUtil) RotateCoordinate(cx, cy, x, y int, thetaRadians float64) (rx, ry int) {
|
||||||
tempX, tempY := float64(x-cx), float64(y-cy)
|
tempX, tempY := float64(x-cx), float64(y-cy)
|
||||||
rotatedX := int(math.Ceil(tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)))
|
rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
|
||||||
rotatedY := int(math.Ceil(tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)))
|
rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
|
||||||
rx = rotatedX + cy
|
rx = int(rotatedX) + cy
|
||||||
ry = rotatedY + cy
|
ry = int(rotatedY) + cy
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
19
math_test.go
19
math_test.go
|
@ -161,13 +161,24 @@ func TestRadianAdd(t *testing.T) {
|
||||||
assert.Equal(_pi, Math.RadianAdd(_pi, -_2pi))
|
assert.Equal(_pi, Math.RadianAdd(_pi, -_2pi))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRotateCoordinate(t *testing.T) {
|
func TestRotateCoordinate90(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cx, cy := 10, 10
|
cx, cy := 10, 10
|
||||||
x, y := 5, 5
|
x, y := 5, 10
|
||||||
|
|
||||||
|
rx, ry := Math.RotateCoordinate(cx, cy, x, y, Math.DegreesToRadians(90))
|
||||||
|
assert.Equal(10, rx)
|
||||||
|
assert.Equal(5, ry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRotateCoordinate45(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cx, cy := 10, 10
|
||||||
|
x, y := 5, 10
|
||||||
|
|
||||||
rx, ry := Math.RotateCoordinate(cx, cy, x, y, Math.DegreesToRadians(45))
|
rx, ry := Math.RotateCoordinate(cx, cy, x, y, Math.DegreesToRadians(45))
|
||||||
assert.Equal(10, rx)
|
assert.Equal(7, rx)
|
||||||
assert.Equal(3, ry)
|
assert.Equal(7, ry)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ type rasterRenderer struct {
|
||||||
i *image.RGBA
|
i *image.RGBA
|
||||||
gc *drawing.RasterGraphicContext
|
gc *drawing.RasterGraphicContext
|
||||||
|
|
||||||
rotateRadians float64
|
rotateRadians *float64
|
||||||
|
|
||||||
s Style
|
s Style
|
||||||
}
|
}
|
||||||
|
@ -183,34 +183,34 @@ func (rr *rasterRenderer) MeasureText(body string) Box {
|
||||||
Right: int(math.Ceil(r)),
|
Right: int(math.Ceil(r)),
|
||||||
Bottom: int(math.Ceil(b)),
|
Bottom: int(math.Ceil(b)),
|
||||||
}
|
}
|
||||||
if rr.rotateRadians == 0 {
|
if rr.rotateRadians == nil {
|
||||||
return textBox
|
return textBox
|
||||||
}
|
}
|
||||||
|
|
||||||
return textBox.Rotate(rr.rotateRadians)
|
return textBox.Rotate(*rr.rotateRadians)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTextRotation sets a text rotation.
|
// SetTextRotation sets a text rotation.
|
||||||
func (rr *rasterRenderer) SetTextRotation(radians float64) {
|
func (rr *rasterRenderer) SetTextRotation(radians float64) {
|
||||||
rr.rotateRadians = radians
|
rr.rotateRadians = &radians
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *rasterRenderer) getCoords(x, y int) (xf, yf int) {
|
func (rr *rasterRenderer) getCoords(x, y int) (xf, yf int) {
|
||||||
if rr.rotateRadians == 0 {
|
if rr.rotateRadians == nil {
|
||||||
xf = x
|
xf = x
|
||||||
yf = y
|
yf = y
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rr.gc.Translate(float64(x), float64(y))
|
rr.gc.Translate(float64(x), float64(y))
|
||||||
rr.gc.Rotate(rr.rotateRadians)
|
rr.gc.Rotate(*rr.rotateRadians)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearTextRotation clears text rotation.
|
// ClearTextRotation clears text rotation.
|
||||||
func (rr *rasterRenderer) ClearTextRotation() {
|
func (rr *rasterRenderer) ClearTextRotation() {
|
||||||
rr.gc.SetMatrixTransform(drawing.NewIdentityMatrix())
|
rr.gc.SetMatrixTransform(drawing.NewIdentityMatrix())
|
||||||
rr.rotateRadians = 0
|
rr.rotateRadians = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save implements the interface method.
|
// Save implements the interface method.
|
||||||
|
|
7
style.go
7
style.go
|
@ -33,7 +33,7 @@ type Style struct {
|
||||||
TextVerticalAlign TextVerticalAlign
|
TextVerticalAlign TextVerticalAlign
|
||||||
TextWrap TextWrap
|
TextWrap TextWrap
|
||||||
TextLineSpacing int
|
TextLineSpacing int
|
||||||
TextRotationDegrees float64
|
TextRotationDegrees float64 //0 is unset or normal
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsZero returns if the object is set or not.
|
// IsZero returns if the object is set or not.
|
||||||
|
@ -262,9 +262,8 @@ func (s Style) WriteToRenderer(r Renderer) {
|
||||||
r.SetFontColor(s.GetFontColor())
|
r.SetFontColor(s.GetFontColor())
|
||||||
r.SetFontSize(s.GetFontSize())
|
r.SetFontSize(s.GetFontSize())
|
||||||
|
|
||||||
if s.GetTextRotationDegrees() == 0 {
|
r.ClearTextRotation()
|
||||||
r.ClearTextRotation()
|
if s.GetTextRotationDegrees() != 0 {
|
||||||
} else {
|
|
||||||
r.SetTextRotation(Math.DegreesToRadians(s.GetTextRotationDegrees()))
|
r.SetTextRotation(Math.DegreesToRadians(s.GetTextRotationDegrees()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ type vectorRenderer struct {
|
||||||
b *bytes.Buffer
|
b *bytes.Buffer
|
||||||
c *canvas
|
c *canvas
|
||||||
s *Style
|
s *Style
|
||||||
r float64
|
|
||||||
p []string
|
p []string
|
||||||
fc *font.Drawer
|
fc *font.Drawer
|
||||||
}
|
}
|
||||||
|
@ -168,18 +167,22 @@ func (vr *vectorRenderer) MeasureText(body string) (box Box) {
|
||||||
|
|
||||||
box.Right = w
|
box.Right = w
|
||||||
box.Bottom = int(drawing.PointsToPixels(vr.dpi, vr.s.FontSize))
|
box.Bottom = int(drawing.PointsToPixels(vr.dpi, vr.s.FontSize))
|
||||||
|
if vr.c.textTheta == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
box = box.Rotate(*vr.c.textTheta)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTextRotation sets the text rotation.
|
// SetTextRotation sets the text rotation.
|
||||||
func (vr *vectorRenderer) SetTextRotation(radians float64) {
|
func (vr *vectorRenderer) SetTextRotation(radians float64) {
|
||||||
vr.c.r = radians
|
vr.c.textTheta = &radians
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearTextRotation clears the text rotation.
|
// ClearTextRotation clears the text rotation.
|
||||||
func (vr *vectorRenderer) ClearTextRotation() {
|
func (vr *vectorRenderer) ClearTextRotation() {
|
||||||
vr.c.r = 0
|
vr.c.textTheta = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save saves the renderer's contents to a writer.
|
// Save saves the renderer's contents to a writer.
|
||||||
|
@ -196,11 +199,11 @@ func newCanvas(w io.Writer) *canvas {
|
||||||
}
|
}
|
||||||
|
|
||||||
type canvas struct {
|
type canvas struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
dpi float64
|
dpi float64
|
||||||
r float64
|
textTheta *float64
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canvas) Start(width, height int) {
|
func (c *canvas) Start(width, height int) {
|
||||||
|
@ -218,10 +221,10 @@ func (c *canvas) Path(d string, style Style) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canvas) Text(x, y int, body string, style Style) {
|
func (c *canvas) Text(x, y int, body string, style Style) {
|
||||||
if c.r == 0 {
|
if c.textTheta == nil {
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
|
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
|
||||||
} else {
|
} else {
|
||||||
transform := fmt.Sprintf(` transform="rotate(%0.2f,%d,%d)"`, Math.RadiansToDegrees(c.r), x, y)
|
transform := fmt.Sprintf(` transform="rotate(%0.2f,%d,%d)"`, Math.RadiansToDegrees(*c.textTheta), x, y)
|
||||||
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s"%s>%s</text>`, x, y, c.styleAsSVG(style), transform, body)))
|
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s"%s>%s</text>`, x, y, c.styleAsSVG(style), transform, body)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
xaxis.go
10
xaxis.go
|
@ -7,13 +7,15 @@ import (
|
||||||
|
|
||||||
// XAxis represents the horizontal axis.
|
// XAxis represents the horizontal axis.
|
||||||
type XAxis struct {
|
type XAxis struct {
|
||||||
Name string
|
Name string
|
||||||
NameStyle Style
|
NameStyle Style
|
||||||
|
|
||||||
Style Style
|
Style Style
|
||||||
ValueFormatter ValueFormatter
|
ValueFormatter ValueFormatter
|
||||||
Range Range
|
Range Range
|
||||||
Ticks []Tick
|
|
||||||
|
|
||||||
|
TickStyle Style
|
||||||
|
Ticks []Tick
|
||||||
TickPosition TickPosition
|
TickPosition TickPosition
|
||||||
|
|
||||||
GridLines []GridLine
|
GridLines []GridLine
|
||||||
|
@ -139,7 +141,7 @@ func (xa XAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
|
||||||
r.LineTo(tx, canvasBox.Bottom+DefaultVerticalTickHeight)
|
r.LineTo(tx, canvasBox.Bottom+DefaultVerticalTickHeight)
|
||||||
r.Stroke()
|
r.Stroke()
|
||||||
|
|
||||||
tickStyle.GetTextOptions().WriteToRenderer(r)
|
xa.TickStyle.InheritFrom(xa.Style.InheritFrom(defaults)).WriteToRenderer(r)
|
||||||
tb := r.MeasureText(t.Label)
|
tb := r.MeasureText(t.Label)
|
||||||
|
|
||||||
switch tp {
|
switch tp {
|
||||||
|
|
8
yaxis.go
8
yaxis.go
|
@ -22,8 +22,8 @@ type YAxis struct {
|
||||||
|
|
||||||
TickStyle Style
|
TickStyle Style
|
||||||
Ticks []Tick
|
Ticks []Tick
|
||||||
GridLines []GridLine
|
|
||||||
|
|
||||||
|
GridLines []GridLine
|
||||||
GridMajorStyle Style
|
GridMajorStyle Style
|
||||||
GridMinorStyle Style
|
GridMinorStyle Style
|
||||||
}
|
}
|
||||||
|
@ -149,11 +149,10 @@ func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
|
||||||
|
|
||||||
var maxTextWidth int
|
var maxTextWidth int
|
||||||
for _, t := range ticks {
|
for _, t := range ticks {
|
||||||
|
ya.TickStyle.InheritFrom(ya.Style.InheritFrom(defaults)).WriteToRenderer(r)
|
||||||
|
|
||||||
v := t.Value
|
v := t.Value
|
||||||
ly := canvasBox.Bottom - ra.Translate(v)
|
ly := canvasBox.Bottom - ra.Translate(v)
|
||||||
|
|
||||||
ya.TickStyle.InheritFrom(ya.Style.InheritFrom(defaults)).WriteToRenderer(r)
|
|
||||||
tb := r.MeasureText(t.Label)
|
tb := r.MeasureText(t.Label)
|
||||||
|
|
||||||
if tb.Width() > maxTextWidth {
|
if tb.Width() > maxTextWidth {
|
||||||
|
@ -167,7 +166,8 @@ func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Text(t.Label, finalTextX, finalTextY)
|
r.Text(t.Label, finalTextX, finalTextY)
|
||||||
r.ClearTextRotation()
|
|
||||||
|
ya.Style.InheritFrom(defaults).WriteToRenderer(r)
|
||||||
|
|
||||||
r.MoveTo(lx, ly)
|
r.MoveTo(lx, ly)
|
||||||
if ya.AxisType == YAxisPrimary {
|
if ya.AxisType == YAxisPrimary {
|
||||||
|
|
Loading…
Reference in a new issue