Skip to content

Commit

Permalink
removing 3rd party deps.
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Charczuk committed Jul 9, 2016
1 parent 7f36c08 commit 8bc8b10
Show file tree
Hide file tree
Showing 27 changed files with 1,995 additions and 54 deletions.
26 changes: 21 additions & 5 deletions chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Chart struct {

Width int
Height int
DPI float64

Background Style
Canvas Style
Expand All @@ -28,14 +29,25 @@ type Chart struct {
Series []Series
}

// GetDPI returns the dpi for the chart.
func (c Chart) GetDPI(defaults ...float64) float64 {
if c.DPI == 0 {
if len(defaults) > 0 {
return defaults[0]
}
return DefaultDPI
}
return c.DPI
}

// GetFont returns the text font.
func (c Chart) GetFont() (*truetype.Font, error) {
if c.Font == nil {
f, err := GetDefaultFont()
if err != nil {
return nil, err
}
c.Font = f
return f, nil
}
return c.Font, nil
}
Expand All @@ -45,14 +57,18 @@ func (c *Chart) Render(provider RendererProvider, w io.Writer) error {
if len(c.Series) == 0 {
return errors.New("Please provide at least one series")
}
r := provider(c.Width, c.Height)
r, err := provider(c.Width, c.Height)
if err != nil {
return err
}
if c.hasText() {
font, err := c.GetFont()
if err != nil {
return err
}
r.SetFont(font)
}
r.SetDPI(c.GetDPI(DefaultDPI))

canvasBox := c.calculateCanvasBox(r)
xrange, yrange := c.initRanges(canvasBox)
Expand Down Expand Up @@ -122,7 +138,7 @@ func (c Chart) calculateFinalLabelWidth(r Renderer) int {
}

r.SetFontSize(c.FinalValueLabel.GetFontSize(DefaultFinalLabelFontSize))
textWidth := r.MeasureText(finalLabelText)
textWidth, _ := r.MeasureText(finalLabelText)
asw := c.getAxisWidth()

pl := c.FinalValueLabel.Padding.GetLeft(DefaultFinalLabelPadding.Left)
Expand Down Expand Up @@ -355,7 +371,7 @@ func (c Chart) drawFinalValueLabel(r Renderer, canvasBox Box, index int, s Serie
ly := yrange.Translate(lv) + py

r.SetFontSize(c.FinalValueLabel.GetFontSize(DefaultFinalLabelFontSize))
textWidth := r.MeasureText(ll)
textWidth, _ := r.MeasureText(ll)
textHeight := int(math.Floor(DefaultFinalLabelFontSize))
halfTextHeight := textHeight >> 1

Expand Down Expand Up @@ -409,7 +425,7 @@ func (c Chart) drawTitle(r Renderer) error {
r.SetFontColor(c.Canvas.GetFontColor(DefaultTextColor))
titleFontSize := c.Canvas.GetFontSize(DefaultTitleFontSize)
r.SetFontSize(titleFontSize)
textWidth := r.MeasureText(c.Title)
textWidth, _ := r.MeasureText(c.Title)
titleX := (c.Width >> 1) - (textWidth >> 1)
titleY := c.TitleStyle.Padding.GetTop(DefaultTitleTop) + int(titleFontSize)
r.Text(c.Title, titleX, titleY)
Expand Down
2 changes: 1 addition & 1 deletion defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const (
// DefaultAxisLineWidth is the line width of the axis lines.
DefaultAxisLineWidth = 1.0
//DefaultDPI is the default dots per inch for the chart.
DefaultDPI = 120.0
DefaultDPI = 92.0
// DefaultMinimumFontSize is the default minimum font size.
DefaultMinimumFontSize = 8.0
// DefaultFontSize is the default font size.
Expand Down
9 changes: 9 additions & 0 deletions drawing/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package drawing

const (
// DefaultDPI is the default image DPI.
DefaultDPI = 96.0

// EMRatio is the ratio of something to something else.
EMRatio = 64.0 / 72.0
)
158 changes: 158 additions & 0 deletions drawing/curve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package drawing

import (
"math"
)

const (
// CurveRecursionLimit represents the maximum recursion that is really necessary to subsivide a curve into straight lines
CurveRecursionLimit = 32
)

// Cubic
// x1, y1, cpx1, cpy1, cpx2, cpy2, x2, y2 float64

// SubdivideCubic a Bezier cubic curve in 2 equivalents Bezier cubic curves.
// c1 and c2 parameters are the resulting curves
func SubdivideCubic(c, c1, c2 []float64) {
// First point of c is the first point of c1
c1[0], c1[1] = c[0], c[1]
// Last point of c is the last point of c2
c2[6], c2[7] = c[6], c[7]

// Subdivide segment using midpoints
c1[2] = (c[0] + c[2]) / 2
c1[3] = (c[1] + c[3]) / 2

midX := (c[2] + c[4]) / 2
midY := (c[3] + c[5]) / 2

c2[4] = (c[4] + c[6]) / 2
c2[5] = (c[5] + c[7]) / 2

c1[4] = (c1[2] + midX) / 2
c1[5] = (c1[3] + midY) / 2

c2[2] = (midX + c2[4]) / 2
c2[3] = (midY + c2[5]) / 2

c1[6] = (c1[4] + c2[2]) / 2
c1[7] = (c1[5] + c2[3]) / 2

// Last Point of c1 is equal to the first point of c2
c2[0], c2[1] = c1[6], c1[7]
}

// TraceCubic generate lines subdividing the cubic curve using a Liner
// flattening_threshold helps determines the flattening expectation of the curve
func TraceCubic(t Liner, cubic []float64, flatteningThreshold float64) {
// Allocation curves
var curves [CurveRecursionLimit * 8]float64
copy(curves[0:8], cubic[0:8])
i := 0

// current curve
var c []float64

var dx, dy, d2, d3 float64

for i >= 0 {
c = curves[i*8:]
dx = c[6] - c[0]
dy = c[7] - c[1]

d2 = math.Abs((c[2]-c[6])*dy - (c[3]-c[7])*dx)
d3 = math.Abs((c[4]-c[6])*dy - (c[5]-c[7])*dx)

// if it's flat then trace a line
if (d2+d3)*(d2+d3) < flatteningThreshold*(dx*dx+dy*dy) || i == len(curves)-1 {
t.LineTo(c[6], c[7])
i--
} else {
// second half of bezier go lower onto the stack
SubdivideCubic(c, curves[(i+1)*8:], curves[i*8:])
i++
}
}
}

// Quad
// x1, y1, cpx1, cpy2, x2, y2 float64

// SubdivideQuad a Bezier quad curve in 2 equivalents Bezier quad curves.
// c1 and c2 parameters are the resulting curves
func SubdivideQuad(c, c1, c2 []float64) {
// First point of c is the first point of c1
c1[0], c1[1] = c[0], c[1]
// Last point of c is the last point of c2
c2[4], c2[5] = c[4], c[5]

// Subdivide segment using midpoints
c1[2] = (c[0] + c[2]) / 2
c1[3] = (c[1] + c[3]) / 2
c2[2] = (c[2] + c[4]) / 2
c2[3] = (c[3] + c[5]) / 2
c1[4] = (c1[2] + c2[2]) / 2
c1[5] = (c1[3] + c2[3]) / 2
c2[0], c2[1] = c1[4], c1[5]
return
}

// TraceQuad generate lines subdividing the curve using a Liner
// flattening_threshold helps determines the flattening expectation of the curve
func TraceQuad(t Liner, quad []float64, flatteningThreshold float64) {
// Allocates curves stack
var curves [CurveRecursionLimit * 6]float64
copy(curves[0:6], quad[0:6])
i := 0
// current curve
var c []float64
var dx, dy, d float64

for i >= 0 {
c = curves[i*6:]
dx = c[4] - c[0]
dy = c[5] - c[1]

d = math.Abs(((c[2]-c[4])*dy - (c[3]-c[5])*dx))

// if it's flat then trace a line
if (d*d) < flatteningThreshold*(dx*dx+dy*dy) || i == len(curves)-1 {
t.LineTo(c[4], c[5])
i--
} else {
// second half of bezier go lower onto the stack
SubdivideQuad(c, curves[(i+1)*6:], curves[i*6:])
i++
}
}
}

// TraceArc trace an arc using a Liner
func TraceArc(t Liner, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) {
end := start + angle
clockWise := true
if angle < 0 {
clockWise = false
}
ra := (math.Abs(rx) + math.Abs(ry)) / 2
da := math.Acos(ra/(ra+0.125/scale)) * 2
//normalize
if !clockWise {
da = -da
}
angle = start + da
var curX, curY float64
for {
if (angle < end-da/4) != clockWise {
curX = x + math.Cos(end)*rx
curY = y + math.Sin(end)*ry
return curX, curY
}
curX = x + math.Cos(angle)*rx
curY = y + math.Sin(angle)*ry

angle += da
t.LineTo(curX, curY)
}
}
89 changes: 89 additions & 0 deletions drawing/dasher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package drawing

// NewDashVertexConverter creates a new dash converter.
func NewDashVertexConverter(dash []float64, dashOffset float64, flattener Flattener) *DashVertexConverter {
var dasher DashVertexConverter
dasher.dash = dash
dasher.currentDash = 0
dasher.dashOffset = dashOffset
dasher.next = flattener
return &dasher
}

// DashVertexConverter is a converter for dash vertexes.
type DashVertexConverter struct {
next Flattener
x, y, distance float64
dash []float64
currentDash int
dashOffset float64
}

// LineTo implements the pathbuilder interface.
func (dasher *DashVertexConverter) LineTo(x, y float64) {
dasher.lineTo(x, y)
}

// MoveTo implements the pathbuilder interface.
func (dasher *DashVertexConverter) MoveTo(x, y float64) {
dasher.next.MoveTo(x, y)
dasher.x, dasher.y = x, y
dasher.distance = dasher.dashOffset
dasher.currentDash = 0
}

// LineJoin implements the pathbuilder interface.
func (dasher *DashVertexConverter) LineJoin() {
dasher.next.LineJoin()
}

// Close implements the pathbuilder interface.
func (dasher *DashVertexConverter) Close() {
dasher.next.Close()
}

// End implements the pathbuilder interface.
func (dasher *DashVertexConverter) End() {
dasher.next.End()
}

func (dasher *DashVertexConverter) lineTo(x, y float64) {
rest := dasher.dash[dasher.currentDash] - dasher.distance
for rest < 0 {
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
rest = dasher.dash[dasher.currentDash] - dasher.distance
}
d := distance(dasher.x, dasher.y, x, y)
for d >= rest {
k := rest / d
lx := dasher.x + k*(x-dasher.x)
ly := dasher.y + k*(y-dasher.y)
if dasher.currentDash%2 == 0 {
// line
dasher.next.LineTo(lx, ly)
} else {
// gap
dasher.next.End()
dasher.next.MoveTo(lx, ly)
}
d = d - rest
dasher.x, dasher.y = lx, ly
dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
rest = dasher.dash[dasher.currentDash]
}
dasher.distance = d
if dasher.currentDash%2 == 0 {
// line
dasher.next.LineTo(x, y)
} else {
// gap
dasher.next.End()
dasher.next.MoveTo(x, y)
}
if dasher.distance >= dasher.dash[dasher.currentDash] {
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
}
dasher.x, dasher.y = x, y
}
41 changes: 41 additions & 0 deletions drawing/demux_flattener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package drawing

// DemuxFlattener is a flattener
type DemuxFlattener struct {
Flatteners []Flattener
}

// MoveTo implements the path builder interface.
func (dc DemuxFlattener) MoveTo(x, y float64) {
for _, flattener := range dc.Flatteners {
flattener.MoveTo(x, y)
}
}

// LineTo implements the path builder interface.
func (dc DemuxFlattener) LineTo(x, y float64) {
for _, flattener := range dc.Flatteners {
flattener.LineTo(x, y)
}
}

// LineJoin implements the path builder interface.
func (dc DemuxFlattener) LineJoin() {
for _, flattener := range dc.Flatteners {
flattener.LineJoin()
}
}

// Close implements the path builder interface.
func (dc DemuxFlattener) Close() {
for _, flattener := range dc.Flatteners {
flattener.Close()
}
}

// End implements the path builder interface.
func (dc DemuxFlattener) End() {
for _, flattener := range dc.Flatteners {
flattener.End()
}
}
Loading

0 comments on commit 8bc8b10

Please sign in to comment.