diff --git a/arc.go b/arc.go index a40c8b8..03213a6 100644 --- a/arc.go +++ b/arc.go @@ -9,7 +9,7 @@ import ( "code.google.com/p/freetype-go/freetype/raster" ) -func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) { +func arc(t LineBuilder, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) { end := start + angle clockWise := true if angle < 0 { @@ -33,7 +33,7 @@ func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float64) (lastX, l curY = y + math.Sin(angle)*ry angle += da - t.AddPoint(curX, curY) + t.LineTo(curX, curY) } return curX, curY } diff --git a/curve/arc.go b/curve/arc.go index 1ed1426..733bf3e 100644 --- a/curve/arc.go +++ b/curve/arc.go @@ -7,8 +7,8 @@ import ( "math" ) -// TraceArc trace an arc using a LineTracer -func TraceArc(t LineTracer, x, y, rx, ry, start, angle, scale float64) { +// TraceArc trace an arc using a LineBuilder +func TraceArc(t LineBuilder, x, y, rx, ry, start, angle, scale float64) { end := start + angle clockWise := true if angle < 0 { @@ -32,7 +32,7 @@ func TraceArc(t LineTracer, x, y, rx, ry, start, angle, scale float64) { curY = y + math.Sin(angle)*ry angle += da - t.AddPoint(curX, curY) + t.LineTo(curX, curY) } - t.AddPoint(curX, curY) + t.LineTo(curX, curY) } diff --git a/curve/cubic.go b/curve/cubic.go index af7995d..89d6575 100644 --- a/curve/cubic.go +++ b/curve/cubic.go @@ -46,9 +46,9 @@ func SubdivideCubic(c, c1, c2 []float64) { c2[0], c2[1] = c1[6], c1[7] } -// TraceCubic generate lines subdividing the cubic curve using a LineTracer +// TraceCubic generate lines subdividing the cubic curve using a LineBuilder // flattening_threshold helps determines the flattening expectation of the curve -func TraceCubic(t LineTracer, cubic []float64, flattening_threshold float64) { +func TraceCubic(t LineBuilder, cubic []float64, flattening_threshold float64) { // Allocation curves var curves [CurveRecursionLimit * 8]float64 copy(curves[0:8], cubic[0:8]) @@ -69,7 +69,7 @@ func TraceCubic(t LineTracer, cubic []float64, flattening_threshold float64) { // if it's flat then trace a line if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 { - t.AddPoint(c[6], c[7]) + t.LineTo(c[6], c[7]) i-- } else { // second half of bezier go lower onto the stack diff --git a/curve/curve_test.go b/curve/curve_test.go index e480a93..f3a8fd0 100644 --- a/curve/curve_test.go +++ b/curve/curve_test.go @@ -38,7 +38,11 @@ type Path struct { points []float64 } -func (p *Path) AddPoint(x, y float64) { +func (p *Path) MoveTo(x, y float64) { + p.points = append(p.points, x, y) +} + +func (p *Path) LineTo(x, y float64) { p.points = append(p.points, x, y) } @@ -82,7 +86,7 @@ func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image { func TestCubicCurve(t *testing.T) { for i := 0; i < len(testsCubicFloat64); i += 8 { var p Path - p.AddPoint(testsCubicFloat64[i], testsCubicFloat64[i+1]) + p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1]) TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold) img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, testsCubicFloat64[i:i+8]...) @@ -98,7 +102,7 @@ func TestCubicCurve(t *testing.T) { func TestQuadCurve(t *testing.T) { for i := 0; i < len(testsQuadFloat64); i += 6 { var p Path - p.AddPoint(testsQuadFloat64[i], testsQuadFloat64[i+1]) + p.MoveTo(testsQuadFloat64[i], testsQuadFloat64[i+1]) TraceQuad(&p, testsQuadFloat64[i:], flattening_threshold) img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, testsQuadFloat64[i:i+6]...) @@ -115,7 +119,7 @@ func BenchmarkCubicCurve(b *testing.B) { for i := 0; i < b.N; i++ { for i := 0; i < len(testsCubicFloat64); i += 8 { var p Path - p.AddPoint(testsCubicFloat64[i], testsCubicFloat64[i+1]) + p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1]) TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold) } } diff --git a/curve/line.go b/curve/line.go new file mode 100644 index 0000000..177ba98 --- /dev/null +++ b/curve/line.go @@ -0,0 +1,7 @@ +package curve + +// LineBuilder is an interface that help segmenting curve into small lines +type LineBuilder interface { + // LineTo a point + LineTo(x, y float64) +} diff --git a/curve/quad.go b/curve/quad.go index 6feb669..12f66c5 100644 --- a/curve/quad.go +++ b/curve/quad.go @@ -29,9 +29,9 @@ func SubdivideQuad(c, c1, c2 []float64) { return } -// Trace generate lines subdividing the curve using a LineTracer +// Trace generate lines subdividing the curve using a LineBuilder // flattening_threshold helps determines the flattening expectation of the curve -func TraceQuad(t LineTracer, quad []float64, flattening_threshold float64) { +func TraceQuad(t LineBuilder, quad []float64, flattening_threshold float64) { // Allocates curves stack var curves [CurveRecursionLimit * 6]float64 copy(curves[0:6], quad[0:6]) @@ -49,7 +49,7 @@ func TraceQuad(t LineTracer, quad []float64, flattening_threshold float64) { // if it's flat then trace a line if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 { - t.AddPoint(c[4], c[5]) + t.LineTo(c[4], c[5]) i-- } else { // second half of bezier go lower onto the stack diff --git a/curve/tracer.go b/curve/tracer.go deleted file mode 100644 index 7cfb997..0000000 --- a/curve/tracer.go +++ /dev/null @@ -1,7 +0,0 @@ -package curve - -// LineTracer is an interface that help segmenting curve into small lines -type LineTracer interface { - // AddPoint a point - AddPoint(x, y float64) -} diff --git a/curves.go b/curves.go index 7b8c0eb..4accdb1 100644 --- a/curves.go +++ b/curves.go @@ -33,7 +33,7 @@ var ( The more this value is the less sharp turns will be cut. Typically it should not exceed 10-15 degrees. */ -func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float64) { +func cubicBezier(v LineBuilder, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float64) { cuspLimit = computeCuspLimit(cuspLimit) distanceToleranceSquare := 0.5 / approximationScale distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare @@ -43,7 +43,7 @@ func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximatio /* * see cubicBezier comments for approximationScale and angleTolerance definition */ -func quadraticBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float64) { +func quadraticBezier(v LineBuilder, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float64) { distanceToleranceSquare := 0.5 / approximationScale distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare @@ -62,7 +62,7 @@ func computeCuspLimit(v float64) (r float64) { /** * http://www.antigrain.com/research/adaptive_bezier/index.html */ -func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 float64, level int, distanceToleranceSquare, angleTolerance float64) { +func recursiveQuadraticBezierBezier(v LineBuilder, x1, y1, x2, y2, x3, y3 float64, level int, distanceToleranceSquare, angleTolerance float64) { if level > CurveRecursionLimit { return } @@ -88,7 +88,7 @@ func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 fl // we tend to finish subdivisions. //---------------------- if angleTolerance < CurveAngleToleranceEpsilon { - v.AddPoint(x123, y123) + v.LineTo(x123, y123) return } @@ -102,7 +102,7 @@ func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 fl if da < angleTolerance { // Finally we can stop the recursion //---------------------- - v.AddPoint(x123, y123) + v.LineTo(x123, y123) return } } @@ -128,7 +128,7 @@ func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 fl } } if d < distanceToleranceSquare { - v.AddPoint(x2, y2) + v.LineTo(x2, y2) return } } @@ -142,7 +142,7 @@ func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 fl /** * http://www.antigrain.com/research/adaptive_bezier/index.html */ -func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 float64, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) { +func recursiveCubicBezier(v LineBuilder, x1, y1, x2, y2, x3, y3, x4, y4 float64, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) { if level > CurveRecursionLimit { return } @@ -209,12 +209,12 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa } if d2 > d3 { if d2 < distanceToleranceSquare { - v.AddPoint(x2, y2) + v.LineTo(x2, y2) return } } else { if d3 < distanceToleranceSquare { - v.AddPoint(x3, y3) + v.LineTo(x3, y3) return } } @@ -225,7 +225,7 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa //---------------------- if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) { if angleTolerance < CurveAngleToleranceEpsilon { - v.AddPoint(x23, y23) + v.LineTo(x23, y23) return } @@ -237,14 +237,14 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa } if da1 < angleTolerance { - v.AddPoint(x2, y2) - v.AddPoint(x3, y3) + v.LineTo(x2, y2) + v.LineTo(x3, y3) return } if cuspLimit != 0.0 { if da1 > cuspLimit { - v.AddPoint(x3, y3) + v.LineTo(x3, y3) return } } @@ -256,7 +256,7 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa //---------------------- if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) { if angleTolerance < CurveAngleToleranceEpsilon { - v.AddPoint(x23, y23) + v.LineTo(x23, y23) return } @@ -268,14 +268,14 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa } if da1 < angleTolerance { - v.AddPoint(x2, y2) - v.AddPoint(x3, y3) + v.LineTo(x2, y2) + v.LineTo(x3, y3) return } if cuspLimit != 0.0 { if da1 > cuspLimit { - v.AddPoint(x2, y2) + v.LineTo(x2, y2) return } } @@ -290,7 +290,7 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa // we tend to finish subdivisions. //---------------------- if angleTolerance < CurveAngleToleranceEpsilon { - v.AddPoint(x23, y23) + v.LineTo(x23, y23) return } @@ -309,18 +309,18 @@ func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 floa if da1+da2 < angleTolerance { // Finally we can stop the recursion //---------------------- - v.AddPoint(x23, y23) + v.LineTo(x23, y23) return } if cuspLimit != 0.0 { if da1 > cuspLimit { - v.AddPoint(x2, y2) + v.LineTo(x2, y2) return } if da2 > cuspLimit { - v.AddPoint(x3, y3) + v.LineTo(x3, y3) return } } diff --git a/dasher.go b/dasher.go index b4d4977..ba2686b 100644 --- a/dasher.go +++ b/dasher.go @@ -4,15 +4,15 @@ package draw2d type DashVertexConverter struct { - command VertexCommand - next VertexConverter + command LineMarker + next LineBuilder x, y, distance float64 dash []float64 currentDash int dashOffset float64 } -func NewDashConverter(dash []float64, dashOffset float64, converter VertexConverter) *DashVertexConverter { +func NewDashConverter(dash []float64, dashOffset float64, converter LineBuilder) *DashVertexConverter { var dasher DashVertexConverter dasher.dash = dash dasher.currentDash = 0 @@ -21,26 +21,20 @@ func NewDashConverter(dash []float64, dashOffset float64, converter VertexConver return &dasher } -func (dasher *DashVertexConverter) NextCommand(cmd VertexCommand) { +func (dasher *DashVertexConverter) NextCommand(cmd LineMarker) { dasher.command = cmd - if dasher.command == VertexStopCommand { - dasher.next.NextCommand(VertexStopCommand) + if dasher.command == LineEndMarker { + dasher.next.NextCommand(LineEndMarker) } } -func (dasher *DashVertexConverter) AddPoint(x, y float64) { - switch dasher.command { - case VertexStartCommand: - dasher.start(x, y) - default: - dasher.lineTo(x, y) - } - dasher.command = VertexNoCommand +func (dasher *DashVertexConverter) LineTo(x, y float64) { + dasher.lineTo(x, y) + dasher.command = LineNoneMarker } -func (dasher *DashVertexConverter) start(x, y float64) { - dasher.next.NextCommand(VertexStartCommand) - dasher.next.AddPoint(x, y) +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 @@ -60,12 +54,11 @@ func (dasher *DashVertexConverter) lineTo(x, y float64) { ly := dasher.y + k*(y-dasher.y) if dasher.currentDash%2 == 0 { // line - dasher.next.AddPoint(lx, ly) + dasher.next.LineTo(lx, ly) } else { // gap - dasher.next.NextCommand(VertexStopCommand) - dasher.next.NextCommand(VertexStartCommand) - dasher.next.AddPoint(lx, ly) + dasher.next.NextCommand(LineEndMarker) + dasher.next.MoveTo(lx, ly) } d = d - rest dasher.x, dasher.y = lx, ly @@ -75,12 +68,11 @@ func (dasher *DashVertexConverter) lineTo(x, y float64) { dasher.distance = d if dasher.currentDash%2 == 0 { // line - dasher.next.AddPoint(x, y) + dasher.next.LineTo(x, y) } else { // gap - dasher.next.NextCommand(VertexStopCommand) - dasher.next.NextCommand(VertexStartCommand) - dasher.next.AddPoint(x, y) + dasher.next.NextCommand(LineEndMarker) + dasher.next.MoveTo(x, y) } if dasher.distance >= dasher.dash[dasher.currentDash] { dasher.distance = dasher.distance - dasher.dash[dasher.currentDash] diff --git a/demux_converter.go b/demux_converter.go index 98524c8..6315842 100644 --- a/demux_converter.go +++ b/demux_converter.go @@ -4,20 +4,27 @@ package draw2d type DemuxConverter struct { - converters []VertexConverter + converters []LineBuilder } -func NewDemuxConverter(converters ...VertexConverter) *DemuxConverter { +func NewDemuxConverter(converters ...LineBuilder) *DemuxConverter { return &DemuxConverter{converters} } -func (dc *DemuxConverter) NextCommand(cmd VertexCommand) { +func (dc *DemuxConverter) NextCommand(cmd LineMarker) { for _, converter := range dc.converters { converter.NextCommand(cmd) } } -func (dc *DemuxConverter) AddPoint(x, y float64) { + +func (dc *DemuxConverter) MoveTo(x, y float64) { for _, converter := range dc.converters { - converter.AddPoint(x, y) + converter.MoveTo(x, y) + } +} + +func (dc *DemuxConverter) LineTo(x, y float64) { + for _, converter := range dc.converters { + converter.LineTo(x, y) } } diff --git a/path_adder.go b/path_adder.go index c40c039..aa85b9d 100644 --- a/path_adder.go +++ b/path_adder.go @@ -8,26 +8,23 @@ import ( ) type VertexAdder struct { - command VertexCommand - adder raster.Adder + adder raster.Adder } func NewVertexAdder(adder raster.Adder) *VertexAdder { - return &VertexAdder{VertexNoCommand, adder} + return &VertexAdder{adder} } -func (vertexAdder *VertexAdder) NextCommand(cmd VertexCommand) { - vertexAdder.command = cmd +func (vertexAdder *VertexAdder) NextCommand(cmd LineMarker) { + } -func (vertexAdder *VertexAdder) AddPoint(x, y float64) { - switch vertexAdder.command { - case VertexStartCommand: - vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) - default: - vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) - } - vertexAdder.command = VertexNoCommand +func (vertexAdder *VertexAdder) MoveTo(x, y float64) { + vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) +} + +func (vertexAdder *VertexAdder) LineTo(x, y float64) { + vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) } type PathAdder struct { diff --git a/path_converter.go b/path_converter.go index b1bd9a4..1c88695 100644 --- a/path_converter.go +++ b/path_converter.go @@ -9,12 +9,12 @@ import ( ) type PathConverter struct { - converter VertexConverter + converter LineBuilder ApproximationScale float64 startX, startY, x, y float64 } -func NewPathConverter(converter VertexConverter) *PathConverter { +func NewPathConverter(converter LineBuilder) *PathConverter { return &PathConverter{converter, 1, 0, 0, 0, 0} } @@ -26,47 +26,48 @@ func (c *PathConverter) Convert(paths ...*PathStorage) { case MoveTo: c.x, c.y = path.vertices[i], path.vertices[i+1] c.startX, c.startY = c.x, c.y - c.converter.NextCommand(VertexStopCommand) - c.converter.NextCommand(VertexStartCommand) - c.converter.AddPoint(c.x, c.y) + if i != 0 { + c.converter.NextCommand(LineEndMarker) + } + c.converter.MoveTo(c.x, c.y) i += 2 case LineTo: c.x, c.y = path.vertices[i], path.vertices[i+1] if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) - c.converter.NextCommand(VertexJoinCommand) + c.converter.LineTo(c.x, c.y) + c.converter.NextCommand(LineJoinMarker) i += 2 case QuadCurveTo: curve.TraceQuad(c.converter, path.vertices[i-2:], 0.5) c.x, c.y = path.vertices[i+2], path.vertices[i+3] if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) + c.converter.LineTo(c.x, c.y) i += 4 case CubicCurveTo: curve.TraceCubic(c.converter, path.vertices[i-2:], 0.5) c.x, c.y = path.vertices[i+4], path.vertices[i+5] if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) + c.converter.LineTo(c.x, c.y) i += 6 case ArcTo: c.x, c.y = arc(c.converter, path.vertices[i], path.vertices[i+1], path.vertices[i+2], path.vertices[i+3], path.vertices[i+4], path.vertices[i+5], c.ApproximationScale) if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) + c.converter.LineTo(c.x, c.y) i += 6 case Close: - c.converter.NextCommand(VertexCloseCommand) - c.converter.AddPoint(c.startX, c.startY) + c.converter.NextCommand(LineCloseMarker) + c.converter.LineTo(c.startX, c.startY) } } - c.converter.NextCommand(VertexStopCommand) + c.converter.NextCommand(LineEndMarker) } } @@ -77,9 +78,8 @@ func (c *PathConverter) convertCommand(cmd PathCmd, vertices ...float64) int { func (c *PathConverter) MoveTo(x, y float64) *PathConverter { c.x, c.y = x, y c.startX, c.startY = c.x, c.y - c.converter.NextCommand(VertexStopCommand) - c.converter.NextCommand(VertexStartCommand) - c.converter.AddPoint(c.x, c.y) + c.converter.NextCommand(LineEndMarker) + c.converter.MoveTo(c.x, c.y) return c } @@ -91,10 +91,10 @@ func (c *PathConverter) RMoveTo(dx, dy float64) *PathConverter { func (c *PathConverter) LineTo(x, y float64) *PathConverter { c.x, c.y = x, y if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) - c.converter.NextCommand(VertexJoinCommand) + c.converter.LineTo(c.x, c.y) + c.converter.NextCommand(LineJoinMarker) return c } @@ -107,9 +107,9 @@ func (c *PathConverter) QuadCurveTo(cx, cy, x, y float64) *PathConverter { curve.TraceQuad(c.converter, []float64{c.x, c.y, cx, cy, x, y}, 0.5) c.x, c.y = x, y if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) + c.converter.LineTo(c.x, c.y) return c } @@ -122,9 +122,9 @@ func (c *PathConverter) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathConv curve.TraceCubic(c.converter, []float64{c.x, c.y, cx1, cy1, cx2, cy2, x, y}, 0.5) c.x, c.y = x, y if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) + c.converter.LineTo(c.x, c.y) return c } @@ -154,9 +154,9 @@ func (c *PathConverter) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathCo c.MoveTo(startX, startY) c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale) if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) + c.converter.NextCommand(LineCloseMarker) } - c.converter.AddPoint(c.x, c.y) + c.converter.LineTo(c.x, c.y) return c } @@ -166,7 +166,7 @@ func (c *PathConverter) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *Pat } func (c *PathConverter) Close() *PathConverter { - c.converter.NextCommand(VertexCloseCommand) - c.converter.AddPoint(c.startX, c.startY) + c.converter.NextCommand(LineCloseMarker) + c.converter.LineTo(c.startX, c.startY) return c } diff --git a/path_storage.go b/path_storage.go index b773d2b..5fc8890 100644 --- a/path_storage.go +++ b/path_storage.go @@ -60,74 +60,65 @@ func (p *PathStorage) IsEmpty() bool { return len(p.commands) == 0 } -func (p *PathStorage) Close() *PathStorage { +func (p *PathStorage) Close() { p.appendToPath(Close) - return p } -func (p *PathStorage) MoveTo(x, y float64) *PathStorage { +func (p *PathStorage) MoveTo(x, y float64) { p.appendToPath(MoveTo, x, y) p.x = x p.y = y - return p } -func (p *PathStorage) RMoveTo(dx, dy float64) *PathStorage { +func (p *PathStorage) RMoveTo(dx, dy float64) { x, y := p.LastPoint() p.MoveTo(x+dx, y+dy) - return p } -func (p *PathStorage) LineTo(x, y float64) *PathStorage { +func (p *PathStorage) LineTo(x, y float64) { if len(p.commands) == 0 { //special case when no move has been done p.MoveTo(0, 0) } p.appendToPath(LineTo, x, y) p.x = x p.y = y - return p } -func (p *PathStorage) RLineTo(dx, dy float64) *PathStorage { +func (p *PathStorage) RLineTo(dx, dy float64) { x, y := p.LastPoint() p.LineTo(x+dx, y+dy) - return p } -func (p *PathStorage) QuadCurveTo(cx, cy, x, y float64) *PathStorage { +func (p *PathStorage) QuadCurveTo(cx, cy, x, y float64) { if len(p.commands) == 0 { //special case when no move has been done p.MoveTo(0, 0) } p.appendToPath(QuadCurveTo, cx, cy, x, y) p.x = x p.y = y - return p } -func (p *PathStorage) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathStorage { +func (p *PathStorage) RQuadCurveTo(dcx, dcy, dx, dy float64) { x, y := p.LastPoint() p.QuadCurveTo(x+dcx, y+dcy, x+dx, y+dy) - return p } -func (p *PathStorage) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathStorage { +func (p *PathStorage) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) { if len(p.commands) == 0 { //special case when no move has been done p.MoveTo(0, 0) } p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y) p.x = x p.y = y - return p } -func (p *PathStorage) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathStorage { +func (p *PathStorage) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) { x, y := p.LastPoint() p.CubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy) - return p } -func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathStorage { +func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float64) { endAngle := startAngle + angle clockWise := true if angle < 0 { @@ -153,13 +144,11 @@ func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathStor p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle) p.x = cx + math.Cos(endAngle)*rx p.y = cy + math.Sin(endAngle)*ry - return p } -func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathStorage { +func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) { x, y := p.LastPoint() p.ArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle) - return p } func (p *PathStorage) String() string { diff --git a/stroker.go b/stroker.go index b46586c..ef693a8 100644 --- a/stroker.go +++ b/stroker.go @@ -20,17 +20,17 @@ const ( ) type LineStroker struct { - Next VertexConverter + Next LineBuilder HalfLineWidth float64 Cap Cap Join Join vertices []float64 rewind []float64 x, y, nx, ny float64 - command VertexCommand + command LineMarker } -func NewLineStroker(c Cap, j Join, converter VertexConverter) *LineStroker { +func NewLineStroker(c Cap, j Join, converter LineBuilder) *LineStroker { l := new(LineStroker) l.Next = converter l.HalfLineWidth = 0.5 @@ -38,27 +38,29 @@ func NewLineStroker(c Cap, j Join, converter VertexConverter) *LineStroker { l.rewind = make([]float64, 0, 256) l.Cap = c l.Join = j - l.command = VertexNoCommand + l.command = LineNoneMarker return l } -func (l *LineStroker) NextCommand(command VertexCommand) { +func (l *LineStroker) NextCommand(command LineMarker) { l.command = command - if command == VertexStopCommand { - l.Next.NextCommand(VertexStartCommand) - for i, j := 0, 1; j < len(l.vertices); i, j = i+2, j+2 { - l.Next.AddPoint(l.vertices[i], l.vertices[j]) - l.Next.NextCommand(VertexNoCommand) + if command == LineEndMarker { + if len(l.vertices) > 1 { + l.Next.MoveTo(l.vertices[0], l.vertices[1]) + for i, j := 2, 3; j < len(l.vertices); i, j = i+2, j+2 { + l.Next.LineTo(l.vertices[i], l.vertices[j]) + l.Next.NextCommand(LineNoneMarker) + } } for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 { - l.Next.NextCommand(VertexNoCommand) - l.Next.AddPoint(l.rewind[i], l.rewind[j]) + l.Next.NextCommand(LineNoneMarker) + l.Next.LineTo(l.rewind[i], l.rewind[j]) } if len(l.vertices) > 1 { - l.Next.NextCommand(VertexNoCommand) - l.Next.AddPoint(l.vertices[0], l.vertices[1]) + l.Next.NextCommand(LineNoneMarker) + l.Next.LineTo(l.vertices[0], l.vertices[1]) } - l.Next.NextCommand(VertexStopCommand) + l.Next.NextCommand(LineEndMarker) // reinit vertices l.vertices = l.vertices[0:0] l.rewind = l.rewind[0:0] @@ -66,20 +68,22 @@ func (l *LineStroker) NextCommand(command VertexCommand) { } } -func (l *LineStroker) AddPoint(x, y float64) { +func (l *LineStroker) MoveTo(x, y float64) { + l.x, l.y = x, y +} + +func (l *LineStroker) LineTo(x, y float64) { switch l.command { - case VertexNoCommand: - l.line(l.x, l.y, x, y) - case VertexJoinCommand: + case LineJoinMarker: l.joinLine(l.x, l.y, l.nx, l.ny, x, y) - case VertexStartCommand: - l.x, l.y = x, y - case VertexCloseCommand: + case LineCloseMarker: l.line(l.x, l.y, x, y) l.joinLine(l.x, l.y, l.nx, l.ny, x, y) l.closePolygon() + default: + l.line(l.x, l.y, x, y) } - l.command = VertexNoCommand + l.command = LineNoneMarker } func (l *LineStroker) appendVertex(vertices ...float64) { diff --git a/transform.go b/transform.go index 3766a22..ac34224 100644 --- a/transform.go +++ b/transform.go @@ -254,22 +254,28 @@ func fequals(float1, float2 float64) bool { // this VertexConverter apply the Matrix transformation tr type VertexMatrixTransform struct { tr MatrixTransform - Next VertexConverter + Next LineBuilder } -func NewVertexMatrixTransform(tr MatrixTransform, converter VertexConverter) *VertexMatrixTransform { +func NewVertexMatrixTransform(tr MatrixTransform, converter LineBuilder) *VertexMatrixTransform { return &VertexMatrixTransform{tr, converter} } // Vertex Matrix Transform -func (vmt *VertexMatrixTransform) NextCommand(command VertexCommand) { +func (vmt *VertexMatrixTransform) NextCommand(command LineMarker) { vmt.Next.NextCommand(command) } -func (vmt *VertexMatrixTransform) AddPoint(x, y float64) { +func (vmt *VertexMatrixTransform) MoveTo(x, y float64) { u := x*vmt.tr[0] + y*vmt.tr[2] + vmt.tr[4] v := x*vmt.tr[1] + y*vmt.tr[3] + vmt.tr[5] - vmt.Next.AddPoint(u, v) + vmt.Next.MoveTo(u, v) +} + +func (vmt *VertexMatrixTransform) LineTo(x, y float64) { + u := x*vmt.tr[0] + y*vmt.tr[2] + vmt.tr[4] + v := x*vmt.tr[1] + y*vmt.tr[3] + vmt.tr[5] + vmt.Next.LineTo(u, v) } // this adder apply a Matrix transformation to points diff --git a/vertex2d.go b/vertex2d.go index 9cf928a..f07f5e8 100644 --- a/vertex2d.go +++ b/vertex2d.go @@ -3,17 +3,21 @@ package draw2d -type VertexCommand byte +type LineMarker byte const ( - VertexNoCommand VertexCommand = iota - VertexStartCommand - VertexJoinCommand - VertexCloseCommand - VertexStopCommand + LineNoneMarker LineMarker = iota + // Mark the current point of the line as a join to it can draw some specific join Bevel, Miter, Rount + LineJoinMarker + // Mark the current point of the line as closed so it draw a line from the current + // position to the point specified by the last start marker. + LineCloseMarker + // Mark the current point of the line as finished. This ending maker allow caps to be drawn + LineEndMarker ) -type VertexConverter interface { - NextCommand(cmd VertexCommand) - AddPoint(x, y float64) +type LineBuilder interface { + NextCommand(cmd LineMarker) + MoveTo(x, y float64) + LineTo(x, y float64) }