From 8f56e5939b78f0ecbe3b74040c9bb1e448452925 Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Mon, 5 Sep 2016 13:26:12 -0700 Subject: [PATCH] snapshot. --- _examples/text_rotation/main.go | 36 +++++++++++++++++++++++++++++++-- box.go | 6 +++--- box_test.go | 18 +++++++++++++++-- math.go | 8 ++++---- math_test.go | 19 +++++++++++++---- raster_renderer.go | 14 ++++++------- style.go | 7 +++---- vector_renderer.go | 23 ++++++++++++--------- xaxis.go | 10 +++++---- yaxis.go | 8 ++++---- 10 files changed, 105 insertions(+), 44 deletions(-) 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 {