forked from wcharczuk/go-chart
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Will Charczuk
committed
Jul 9, 2016
1 parent
7f36c08
commit 8bc8b10
Showing
27 changed files
with
1,995 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} |
Oops, something went wrong.