diff --git a/_examples/text_rotation/main.go b/_examples/text_rotation/main.go
index b30c41f..c2a2b6c 100644
--- a/_examples/text_rotation/main.go
+++ b/_examples/text_rotation/main.go
@@ -14,13 +14,31 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
graph := chart.Chart{
YAxis: chart.YAxis{
Style: chart.Style{
- Show: true,
- TextRotationDegrees: 45.0,
+ Show: true,
},
Range: &chart.ContinuousRange{
Min: 0.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{
{0.0, "0.00"},
{2.0, "2.00"},
@@ -39,6 +57,20 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
}
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)
}
diff --git a/box.go b/box.go
index 68b71a8..0b55023 100644
--- a/box.go
+++ b/box.go
@@ -76,8 +76,8 @@ func (b Box) Height() int {
// Center returns the center of the box
func (b Box) Center() (x, y int) {
- w, h := b.Width(), b.Height()
- return b.Left + w>>1, b.Top + h>>1
+ w2, h2 := b.Width()>>1, b.Height()>>1
+ return b.Left + w2, b.Top + h2
}
// 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)
rbx, rby := Math.RotateCoordinate(cx, cy, b.Right, b.Bottom, radians)
return Box{
- Top: lty,
Left: ltx,
+ Top: lty,
Right: rbx,
Bottom: rby,
}
diff --git a/box_test.go b/box_test.go
index 5f4df40..020dc22 100644
--- a/box_test.go
+++ b/box_test.go
@@ -144,6 +144,20 @@ func TestBoxShift(t *testing.T) {
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) {
assert := assert.New(t)
@@ -156,6 +170,6 @@ func TestBoxRotate(t *testing.T) {
rotated := b.Rotate(Math.DegreesToRadians(45))
assert.Equal(1, rotated.Top)
assert.Equal(4, rotated.Left)
- assert.Equal(11, rotated.Right)
- assert.Equal(15, rotated.Bottom)
+ assert.Equal(10, rotated.Right)
+ assert.Equal(14, rotated.Bottom)
}
diff --git a/math.go b/math.go
index bc4456c..8d745a1 100644
--- a/math.go
+++ b/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) {
tempX, tempY := float64(x-cx), float64(y-cy)
- rotatedX := int(math.Ceil(tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)))
- rotatedY := int(math.Ceil(tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)))
- rx = rotatedX + cy
- ry = rotatedY + cy
+ rotatedX := tempX*math.Cos(thetaRadians) - tempY*math.Sin(thetaRadians)
+ rotatedY := tempX*math.Sin(thetaRadians) + tempY*math.Cos(thetaRadians)
+ rx = int(rotatedX) + cy
+ ry = int(rotatedY) + cy
return
}
diff --git a/math_test.go b/math_test.go
index 88cf860..c629af4 100644
--- a/math_test.go
+++ b/math_test.go
@@ -161,13 +161,24 @@ func TestRadianAdd(t *testing.T) {
assert.Equal(_pi, Math.RadianAdd(_pi, -_2pi))
}
-func TestRotateCoordinate(t *testing.T) {
+func TestRotateCoordinate90(t *testing.T) {
assert := assert.New(t)
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))
- assert.Equal(10, rx)
- assert.Equal(3, ry)
+ assert.Equal(7, rx)
+ assert.Equal(7, ry)
}
diff --git a/raster_renderer.go b/raster_renderer.go
index cc32ebb..fecd851 100644
--- a/raster_renderer.go
+++ b/raster_renderer.go
@@ -28,7 +28,7 @@ type rasterRenderer struct {
i *image.RGBA
gc *drawing.RasterGraphicContext
- rotateRadians float64
+ rotateRadians *float64
s Style
}
@@ -183,34 +183,34 @@ func (rr *rasterRenderer) MeasureText(body string) Box {
Right: int(math.Ceil(r)),
Bottom: int(math.Ceil(b)),
}
- if rr.rotateRadians == 0 {
+ if rr.rotateRadians == nil {
return textBox
}
- return textBox.Rotate(rr.rotateRadians)
+ return textBox.Rotate(*rr.rotateRadians)
}
// SetTextRotation sets a text rotation.
func (rr *rasterRenderer) SetTextRotation(radians float64) {
- rr.rotateRadians = radians
+ rr.rotateRadians = &radians
}
func (rr *rasterRenderer) getCoords(x, y int) (xf, yf int) {
- if rr.rotateRadians == 0 {
+ if rr.rotateRadians == nil {
xf = x
yf = y
return
}
rr.gc.Translate(float64(x), float64(y))
- rr.gc.Rotate(rr.rotateRadians)
+ rr.gc.Rotate(*rr.rotateRadians)
return
}
// ClearTextRotation clears text rotation.
func (rr *rasterRenderer) ClearTextRotation() {
rr.gc.SetMatrixTransform(drawing.NewIdentityMatrix())
- rr.rotateRadians = 0
+ rr.rotateRadians = nil
}
// Save implements the interface method.
diff --git a/style.go b/style.go
index df4794f..d1db16b 100644
--- a/style.go
+++ b/style.go
@@ -33,7 +33,7 @@ type Style struct {
TextVerticalAlign TextVerticalAlign
TextWrap TextWrap
TextLineSpacing int
- TextRotationDegrees float64
+ TextRotationDegrees float64 //0 is unset or normal
}
// IsZero returns if the object is set or not.
@@ -262,9 +262,8 @@ func (s Style) WriteToRenderer(r Renderer) {
r.SetFontColor(s.GetFontColor())
r.SetFontSize(s.GetFontSize())
- if s.GetTextRotationDegrees() == 0 {
- r.ClearTextRotation()
- } else {
+ r.ClearTextRotation()
+ if s.GetTextRotationDegrees() != 0 {
r.SetTextRotation(Math.DegreesToRadians(s.GetTextRotationDegrees()))
}
}
diff --git a/vector_renderer.go b/vector_renderer.go
index d65e497..4afad26 100644
--- a/vector_renderer.go
+++ b/vector_renderer.go
@@ -32,7 +32,6 @@ type vectorRenderer struct {
b *bytes.Buffer
c *canvas
s *Style
- r float64
p []string
fc *font.Drawer
}
@@ -168,18 +167,22 @@ func (vr *vectorRenderer) MeasureText(body string) (box Box) {
box.Right = w
box.Bottom = int(drawing.PointsToPixels(vr.dpi, vr.s.FontSize))
+ if vr.c.textTheta == nil {
+ return
+ }
+ box = box.Rotate(*vr.c.textTheta)
}
return
}
// SetTextRotation sets the text rotation.
func (vr *vectorRenderer) SetTextRotation(radians float64) {
- vr.c.r = radians
+ vr.c.textTheta = &radians
}
// ClearTextRotation clears the text rotation.
func (vr *vectorRenderer) ClearTextRotation() {
- vr.c.r = 0
+ vr.c.textTheta = nil
}
// Save saves the renderer's contents to a writer.
@@ -196,11 +199,11 @@ func newCanvas(w io.Writer) *canvas {
}
type canvas struct {
- w io.Writer
- dpi float64
- r float64
- width int
- height int
+ w io.Writer
+ dpi float64
+ textTheta *float64
+ width int
+ 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) {
- if c.r == 0 {
+ if c.textTheta == nil {
c.w.Write([]byte(fmt.Sprintf(`%s`, x, y, c.styleAsSVG(style), body)))
} 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(`%s`, x, y, c.styleAsSVG(style), transform, body)))
}
}
diff --git a/xaxis.go b/xaxis.go
index f1326db..7276840 100644
--- a/xaxis.go
+++ b/xaxis.go
@@ -7,13 +7,15 @@ import (
// XAxis represents the horizontal axis.
type XAxis struct {
- Name string
- NameStyle Style
+ Name string
+ NameStyle Style
+
Style Style
ValueFormatter ValueFormatter
Range Range
- Ticks []Tick
+ TickStyle Style
+ Ticks []Tick
TickPosition TickPosition
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.Stroke()
- tickStyle.GetTextOptions().WriteToRenderer(r)
+ xa.TickStyle.InheritFrom(xa.Style.InheritFrom(defaults)).WriteToRenderer(r)
tb := r.MeasureText(t.Label)
switch tp {
diff --git a/yaxis.go b/yaxis.go
index d2b6a72..2ca5073 100644
--- a/yaxis.go
+++ b/yaxis.go
@@ -22,8 +22,8 @@ type YAxis struct {
TickStyle Style
Ticks []Tick
- GridLines []GridLine
+ GridLines []GridLine
GridMajorStyle Style
GridMinorStyle Style
}
@@ -149,11 +149,10 @@ func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
var maxTextWidth int
for _, t := range ticks {
+ ya.TickStyle.InheritFrom(ya.Style.InheritFrom(defaults)).WriteToRenderer(r)
v := t.Value
ly := canvasBox.Bottom - ra.Translate(v)
-
- ya.TickStyle.InheritFrom(ya.Style.InheritFrom(defaults)).WriteToRenderer(r)
tb := r.MeasureText(t.Label)
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.ClearTextRotation()
+
+ ya.Style.InheritFrom(defaults).WriteToRenderer(r)
r.MoveTo(lx, ly)
if ya.AxisType == YAxisPrimary {