Skip to content

Commit

Permalink
vector renderer works
Browse files Browse the repository at this point in the history
  • Loading branch information
wcharczuk committed Jul 28, 2016
1 parent b600cb1 commit 3d9cf0d
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 16 deletions.
7 changes: 4 additions & 3 deletions examples/pie_chart/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
FillColor: chart.ColorLightGray,
},
Values: []chart.PieChartValue{
{Value: 0.3, Label: "Blue"},
{Value: 0.2, Label: "Blue"},
{Value: 0.2, Label: "Green"},
{Value: 0.2, Label: "Gray"},
{Value: 0.1, Label: "Orange"},
{Value: 0.1, Label: "HEANG"},
{Value: 0.1, Label: "??"},
{Value: 0.1, Label: "!!"},
},
}

res.Header().Set("Content-Type", "image/png")
err := pie.Render(chart.PNG, res)
res.Header().Set("Content-Type", "image/svg+xml")
err := pie.Render(chart.SVG, res)
if err != nil {
fmt.Printf("Error rendering pie chart: %v\n", err)
}
Expand Down
13 changes: 7 additions & 6 deletions pie_chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package chart
import (
"errors"
"io"
"math"

"github.com/golang/freetype/truetype"
)
Expand Down Expand Up @@ -142,14 +141,15 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []PieChartValue)
cx, cy := canvasBox.Center()
diameter := MinInt(canvasBox.Width(), canvasBox.Height())
radius := float64(diameter >> 1)
radius2 := (radius * 2.0) / 3.0
labelRadius := (radius * 2.0) / 3.0

// draw the pie slices
var rads, delta, delta2, total float64
var lx, ly int
for index, v := range values {
v.Style.InheritFrom(pc.stylePieChartValue(index)).PersistToRenderer(r)
r.MoveTo(cx, cy)

r.MoveTo(cx, cy)
rads = PercentToRadians(total)
delta = PercentToRadians(v.Value)

Expand All @@ -161,13 +161,14 @@ func (pc PieChart) drawSlices(r Renderer, canvasBox Box, values []PieChartValue)
total = total + v.Value
}

// draw the labels
total = 0
for index, v := range values {
v.Style.InheritFrom(pc.stylePieChartValue(index)).PersistToRenderer(r)
if len(v.Label) > 0 {
delta2 = RadianAdd(PercentToRadians(total+(v.Value/2.0)), _pi2)
lx = cx + int(radius2*math.Sin(delta2))
ly = cy - int(radius2*math.Cos(delta2))
delta2 = PercentToRadians(total + (v.Value / 2.0))
delta2 = RadianAdd(delta2, _pi2)
lx, ly = CirclePoint(cx, cy, labelRadius, delta2)

tb := r.MeasureText(v.Label)
lx = lx - (tb.Width() >> 1)
Expand Down
48 changes: 42 additions & 6 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,30 @@ func PercentDifference(v1, v2 float64) float64 {
return (v2 - v1) / v1
}

// DegreesToRadians returns degrees as radians.
func DegreesToRadians(degrees float64) float64 {
return degrees * (math.Pi / 180.0)
}

const (
_pi = math.Pi
_2pi = 2 * math.Pi
_3pi4 = (3 * math.Pi) / 4.0
_4pi3 = (4 * math.Pi) / 3.0
_3pi2 = (3 * math.Pi) / 2.0
_5pi4 = (5 * math.Pi) / 4.0
_7pi4 = (7 * math.Pi) / 4.0
_pi2 = math.Pi / 2.0
_pi4 = math.Pi / 4.0
_d2r = (math.Pi / 180.0)
_r2d = (180.0 / math.Pi)
)

// DegreesToRadians returns degrees as radians.
func DegreesToRadians(degrees float64) float64 {
return degrees * _d2r
}

// RadiansToDegrees translates a radian value to a degree value.
func RadiansToDegrees(value float64) float64 {
return math.Mod(value, _2pi) * _r2d
}

// PercentToRadians converts a normalized value (0,1) to radians.
func PercentToRadians(pct float64) float64 {
return DegreesToRadians(360.0 * pct)
Expand All @@ -214,7 +226,31 @@ func RadianAdd(base, delta float64) float64 {
if value > _2pi {
return math.Mod(value, _2pi)
} else if value < 0 {
return _2pi + value
return math.Mod(_2pi+value, _2pi)
}
return value
}

// DegreesAdd adds a delta to a base in radians.
func DegreesAdd(baseDegrees, deltaDegrees float64) float64 {
value := baseDegrees + deltaDegrees
if value > _2pi {
return math.Mod(value, 360.0)
} else if value < 0 {
return math.Mod(360.0+value, 360.0)
}
return value
}

// DegreesToCompass returns the degree value in compass / clock orientation.
func DegreesToCompass(deg float64) float64 {
return DegreesAdd(deg, -90.0)
}

// CirclePoint returns the absolute position of a circle diameter point given
// by the radius and the angle.
func CirclePoint(cx, cy int, radius, angleRadians float64) (x, y int) {
x = cx + int(radius*math.Sin(angleRadians))
y = cy - int(radius*math.Cos(angleRadians))
return
}
57 changes: 57 additions & 0 deletions util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,60 @@ func TestPercentDifference(t *testing.T) {
assert.Equal(0.5, PercentDifference(1.0, 1.5))
assert.Equal(-0.5, PercentDifference(2.0, 1.0))
}

var (
_degreesToRadians = map[float64]float64{
0: 0, // !_2pi b/c no irrational nums in floats.
45: _pi4,
90: _pi2,
135: _3pi4,
180: _pi,
225: _5pi4,
270: _3pi2,
315: _7pi4,
}

_compassToRadians = map[float64]float64{
0: _pi2,
45: _pi4,
90: 0, // !_2pi b/c no irrational nums in floats.
135: _7pi4,
180: _3pi2,
225: _5pi4,
270: _pi,
315: _3pi4,
}
)

func TestDegreesToRadians(t *testing.T) {
assert := assert.New(t)

for d, r := range _degreesToRadians {
assert.Equal(r, DegreesToRadians(d))
}
}

func TestPercentToRadians(t *testing.T) {
assert := assert.New(t)

for d, r := range _degreesToRadians {
assert.Equal(r, PercentToRadians(d/360.0))
}
}

func TestRadiansToDegrees(t *testing.T) {
assert := assert.New(t)

for d, r := range _degreesToRadians {
assert.Equal(d, RadiansToDegrees(r))
}
}

func TestRadianAdd(t *testing.T) {
assert := assert.New(t)

assert.Equal(_pi, RadianAdd(_pi2, _pi2))
assert.Equal(_3pi2, RadianAdd(_pi2, _pi))
assert.Equal(_pi, RadianAdd(_pi, _2pi))
assert.Equal(_pi, RadianAdd(_pi, -_2pi))
}
20 changes: 19 additions & 1 deletion vector_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"math"
"strings"

"golang.org/x/image/font"
Expand Down Expand Up @@ -82,7 +83,24 @@ func (vr *vectorRenderer) QuadCurveTo(cx, cy, x, y int) {
}

func (vr *vectorRenderer) ArcTo(cx, cy int, rx, ry, startAngle, delta float64) {
vr.p = append(vr.p, fmt.Sprintf("A %d %d %0.2f 1 1 %d %d", int(rx), int(ry), delta, cx, cy))
startAngle = RadianAdd(startAngle, _pi2)
endAngle := RadianAdd(startAngle, delta)

startx := cx + int(rx*math.Sin(startAngle))
starty := cy - int(ry*math.Cos(startAngle))

if len(vr.p) > 0 {
vr.p = append(vr.p, fmt.Sprintf("L %d %d", startx, starty))
} else {
vr.p = append(vr.p, fmt.Sprintf("M %d %d", startx, starty))
}

endx := cx + int(rx*math.Sin(endAngle))
endy := cy - int(ry*math.Cos(endAngle))

dd := RadiansToDegrees(delta)

vr.p = append(vr.p, fmt.Sprintf("A %d %d %0.2f 0 1 %d %d", int(rx), int(ry), dd, endx, endy))
}

// Close closes a shape.
Expand Down

0 comments on commit 3d9cf0d

Please sign in to comment.