Skip to content
This repository has been archived by the owner on Aug 23, 2024. It is now read-only.

Commit

Permalink
can rotate text + add y axis names
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Charczuk committed Aug 7, 2016
1 parent 3607d73 commit 718678b
Show file tree
Hide file tree
Showing 34 changed files with 102 additions and 20 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 9 additions & 3 deletions examples/min_max/main.go → _examples/min_max/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
}

graph := chart.Chart{
Width: 1920,
Height: 1080,
YAxis: chart.YAxis{
Style: chart.StyleShow(),
Name: "Random Values",
NameStyle: chart.StyleShow(),
Style: chart.StyleShow(),
Range: &chart.ContinuousRange{
Min: 25,
Max: 175,
Expand All @@ -51,8 +55,10 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
},
}

res.Header().Set("Content-Type", "image/png")
graph.Render(chart.PNG, res)
graph.Elements = []chart.Renderable{chart.Legend(&graph)}

res.Header().Set("Content-Type", "image/svg+xml")
graph.Render(chart.SVG, res)
}

func main() {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
8 changes: 4 additions & 4 deletions drawing/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ func (tr *Matrix) Translate(tx, ty float64) {
tr[5] = ty*tr[3] + tx*tr[1] + tr[5]
}

// Rotate adds a rotation to the matrix. angle is in radian
func (tr *Matrix) Rotate(angle float64) {
c := math.Cos(angle)
s := math.Sin(angle)
// Rotate adds a rotation to the matrix.
func (tr *Matrix) Rotate(radians float64) {
c := math.Cos(radians)
s := math.Sin(radians)
t0 := c*tr[0] + s*tr[2]
t1 := s*tr[3] + c*tr[1]
t2 := c*tr[2] - s*tr[0]
Expand Down
12 changes: 4 additions & 8 deletions drawing/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@ func DrawContour(path PathBuilder, ps []truetype.Point, dx, dy float64) {
} else {
path.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy)
}
} else {
if on0 {
// No-op.
} else {
midX := (q0X + qX) / 2
midY := (q0Y + qY) / 2
path.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
}
} else if !on0 {
midX := (q0X + qX) / 2
midY := (q0Y + qY) / 2
path.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
}
q0X, q0Y, on0 = qX, qY, on
}
Expand Down
28 changes: 27 additions & 1 deletion raster_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type rasterRenderer struct {
i *image.RGBA
gc *drawing.RasterGraphicContext

rotateRadians float64

s Style
}

Expand Down Expand Up @@ -139,10 +141,11 @@ func (rr *rasterRenderer) SetFontColor(c drawing.Color) {

// Text implements the interface method.
func (rr *rasterRenderer) Text(body string, x, y int) {
xf, yf := rr.getCoords(x, y)
rr.gc.SetFont(rr.s.Font)
rr.gc.SetFontSize(rr.s.FontSize)
rr.gc.SetFillColor(rr.s.FontColor)
rr.gc.CreateStringPath(body, float64(x), float64(y))
rr.gc.CreateStringPath(body, float64(xf), float64(yf))
rr.gc.Fill()
}

Expand Down Expand Up @@ -182,6 +185,29 @@ func (rr *rasterRenderer) MeasureText(body string) Box {
}
}

// SetTextRotation sets a text rotation.
func (rr *rasterRenderer) SetTextRotation(radians float64) {
rr.rotateRadians = radians
}

func (rr *rasterRenderer) getCoords(x, y int) (xf, yf int) {
if rr.rotateRadians == 0 {
xf = x
yf = y
return
}

rr.gc.Translate(float64(x), float64(y))
rr.gc.Rotate(rr.rotateRadians)
return
}

// ClearTextRotation clears text rotation.
func (rr *rasterRenderer) ClearTextRotation() {
rr.gc.SetMatrixTransform(drawing.NewIdentityMatrix())
rr.rotateRadians = 0
}

// Save implements the interface method.
func (rr *rasterRenderer) Save(w io.Writer) error {
return png.Encode(w, rr.i)
Expand Down
6 changes: 6 additions & 0 deletions renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ type Renderer interface {
// MeasureText measures text.
MeasureText(body string) Box

// SetTextRotatation sets a rotation for drawing elements.
SetTextRotation(radians float64)

// ClearTextRotation clears rotation.
ClearTextRotation()

// Save writes the image to the given writer.
Save(w io.Writer) error
}
19 changes: 18 additions & 1 deletion vector_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type vectorRenderer struct {
b *bytes.Buffer
c *canvas
s *Style
r float64
p []string
fc *font.Drawer
}
Expand Down Expand Up @@ -171,6 +172,16 @@ func (vr *vectorRenderer) MeasureText(body string) (box Box) {
return
}

// SetTextRotation sets the text rotation.
func (vr *vectorRenderer) SetTextRotation(radians float64) {
vr.c.r = radians
}

// ClearTextRotation clears the text rotation.
func (vr *vectorRenderer) ClearTextRotation() {
vr.c.r = 0
}

// Save saves the renderer's contents to a writer.
func (vr *vectorRenderer) Save(w io.Writer) error {
vr.c.End()
Expand All @@ -187,6 +198,7 @@ func newCanvas(w io.Writer) *canvas {
type canvas struct {
w io.Writer
dpi float64
r float64
width int
height int
}
Expand All @@ -206,7 +218,12 @@ func (c *canvas) Path(d string, style Style) {
}

func (c *canvas) Text(x, y int, body string, style Style) {
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
if c.r == 0 {
c.w.Write([]byte(fmt.Sprintf(`<text x="%d" y="%d" style="%s">%s</text>`, x, y, c.styleAsSVG(style), body)))
} else {
transform := fmt.Sprintf(` transform="rotate(%0.2f,%d,%d)"`, Math.RadiansToDegrees(c.r), 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)))
}
}

func (c *canvas) Circle(x, y, r int, style Style) {
Expand Down
37 changes: 34 additions & 3 deletions yaxis.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
// YAxis is a veritcal rule of the range.
// There can be (2) y-axes; a primary and secondary.
type YAxis struct {
Name string
Name string
NameStyle Style

Style Style

Zero GridLine
Expand All @@ -30,6 +32,11 @@ func (ya YAxis) GetName() string {
return ya.Name
}

// GetNameStyle returns the name style.
func (ya YAxis) GetNameStyle() Style {
return ya.NameStyle
}

// GetStyle returns the style.
func (ya YAxis) GetStyle() Style {
return ya.Style
Expand Down Expand Up @@ -73,6 +80,7 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
}

var minx, maxx, miny, maxy = math.MaxInt32, 0, math.MaxInt32, 0
var maxTextHeight int
for _, t := range ticks {
v := t.Value
ly := canvasBox.Bottom - ra.Translate(v)
Expand All @@ -83,6 +91,10 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
finalTextX = tx - tb.Width()
}

if tb.Height() > maxTextHeight {
maxTextHeight = tb.Height()
}

if ya.AxisType == YAxisPrimary {
minx = canvasBox.Right
maxx = Math.MaxInt(maxx, tx+tb.Width())
Expand All @@ -94,6 +106,10 @@ func (ya YAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
maxy = Math.MaxInt(maxy, ly+tb.Height()>>1)
}

if ya.NameStyle.Show && len(ya.Name) > 0 {
maxx += (DefaultYAxisMargin + maxTextHeight)
}

return Box{
Top: miny,
Left: minx,
Expand Down Expand Up @@ -124,12 +140,17 @@ func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
r.LineTo(lx, canvasBox.Top)
r.Stroke()

var maxTextWidth int
for _, t := range ticks {
v := t.Value
ly := canvasBox.Bottom - ra.Translate(v)

tb := r.MeasureText(t.Label)

if tb.Width() > maxTextWidth {
maxTextWidth = tb.Width()
}

finalTextX := tx
finalTextY := ly + tb.Height()>>1
if ya.AxisType == YAxisSecondary {
Expand All @@ -147,8 +168,18 @@ func (ya YAxis) Render(r Renderer, canvasBox Box, ra Range, defaults Style, tick
r.Stroke()
}

if ya.Zero.Style.Show {
ya.Zero.Render(r, canvasBox, ra, Style{})
nameStyle := ya.NameStyle.InheritFrom(defaults)
if ya.NameStyle.Show && len(ya.Name) > 0 {
nameStyle.GetTextOptions().WriteToRenderer(r)

r.SetTextRotation(Math.DegreesToRadians(90))

tb := r.MeasureText(ya.Name)
tx := canvasBox.Right + int(sw) + DefaultYAxisMargin + maxTextWidth + DefaultYAxisMargin
ty := canvasBox.Bottom - (canvasBox.Height()>>1 + tb.Width()>>1)

r.Text(ya.Name, tx, ty)
r.ClearTextRotation()
}

if ya.GridMajorStyle.Show || ya.GridMinorStyle.Show {
Expand Down

0 comments on commit 718678b

Please sign in to comment.