diff --git a/draw2dbase/curve.go b/draw2dbase/curve.go
new file mode 100644
index 0000000..2b10ba7
--- /dev/null
+++ b/draw2dbase/curve.go
@@ -0,0 +1,161 @@
+// Copyright 2010 The draw2d Authors. All rights reserved.
+// created: 17/05/2011 by Laurent Le Goff
+
+package draw2dbase
+
+import (
+ "math"
+)
+
+const (
+ CurveRecursionLimit = 32
+)
+
+// Cubic
+// x1, y1, cpx1, cpy1, cpx2, cpy2, x2, y2 float64
+
+// Subdivide 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 Flattener
+// flattening_threshold helps determines the flattening expectation of the curve
+func TraceCubic(t Flattener, cubic []float64, flattening_threshold 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) < flattening_threshold*(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
+
+// Subdivide 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
+}
+
+// Trace generate lines subdividing the curve using a Flattener
+// flattening_threshold helps determines the flattening expectation of the curve
+func TraceQuad(t Flattener, quad []float64, flattening_threshold 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) < flattening_threshold*(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 Flattener
+func TraceArc(t Flattener, 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)
+ }
+ return curX, curY
+}
diff --git a/draw2dbase/curve_test.go b/draw2dbase/curve_test.go
new file mode 100644
index 0000000..b737862
--- /dev/null
+++ b/draw2dbase/curve_test.go
@@ -0,0 +1,136 @@
+package draw2dbase
+
+import (
+ "bufio"
+ "fmt"
+ "image"
+ "image/color"
+ "image/draw"
+ "image/png"
+ "log"
+ "os"
+ "testing"
+
+ "github.com/llgcode/draw2d/raster"
+)
+
+var (
+ flattening_threshold float64 = 0.5
+ testsCubicFloat64 = []float64{
+ 100, 100, 200, 100, 100, 200, 200, 200,
+ 100, 100, 300, 200, 200, 200, 300, 100,
+ 100, 100, 0, 300, 200, 0, 300, 300,
+ 150, 290, 10, 10, 290, 10, 150, 290,
+ 10, 290, 10, 10, 290, 10, 290, 290,
+ 100, 290, 290, 10, 10, 10, 200, 290,
+ }
+ testsQuadFloat64 = []float64{
+ 100, 100, 200, 100, 200, 200,
+ 100, 100, 290, 200, 290, 100,
+ 100, 100, 0, 290, 200, 290,
+ 150, 290, 10, 10, 290, 290,
+ 10, 290, 10, 10, 290, 290,
+ 100, 290, 290, 10, 120, 290,
+ }
+)
+
+func init() {
+ os.Mkdir("test_results", 0666)
+ f, err := os.Create("test_results/_test.html")
+ if err != nil {
+ log.Println(err)
+ os.Exit(1)
+ }
+ defer f.Close()
+ log.Printf("Create html viewer")
+ f.Write([]byte("
"))
+ for i := 0; i < len(testsCubicFloat64)/8; i++ {
+ f.Write([]byte(fmt.Sprintf("\n", i)))
+ }
+ for i := 0; i < len(testsQuadFloat64); i++ {
+ f.Write([]byte(fmt.Sprintf("\n
\n", i)))
+ }
+ f.Write([]byte(""))
+
+}
+
+func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image {
+ for i := 0; i < len(s); i += 2 {
+ x, y := int(s[i]+0.5), int(s[i+1]+0.5)
+ img.Set(x, y, c)
+ img.Set(x, y+1, c)
+ img.Set(x, y-1, c)
+ img.Set(x+1, y, c)
+ img.Set(x+1, y+1, c)
+ img.Set(x+1, y-1, c)
+ img.Set(x-1, y, c)
+ img.Set(x-1, y+1, c)
+ img.Set(x-1, y-1, c)
+
+ }
+ return img
+}
+
+func TestCubicCurve(t *testing.T) {
+ for i := 0; i < len(testsCubicFloat64); i += 8 {
+ var p SegmentedPath
+ 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]...)
+ raster.PolylineBresenham(img, image.Black, p.Points...)
+ //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
+ drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
+ SaveToPngFile(fmt.Sprintf("test_results/_test%d.png", i/8), img)
+ log.Printf("Num of points: %d\n", len(p.Points))
+ }
+ fmt.Println()
+}
+
+func TestQuadCurve(t *testing.T) {
+ for i := 0; i < len(testsQuadFloat64); i += 6 {
+ var p SegmentedPath
+ 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]...)
+ raster.PolylineBresenham(img, image.Black, p.Points...)
+ //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
+ drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
+ SaveToPngFile(fmt.Sprintf("test_results/_testQuad%d.png", i), img)
+ log.Printf("Num of points: %d\n", len(p.Points))
+ }
+ fmt.Println()
+}
+
+func BenchmarkCubicCurve(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for i := 0; i < len(testsCubicFloat64); i += 8 {
+ var p SegmentedPath
+ p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1])
+ TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold)
+ }
+ }
+}
+
+// SaveToPngFile create and save an image to a file using PNG format
+func SaveToPngFile(filePath string, m image.Image) error {
+ // Create the file
+ f, err := os.Create(filePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ // Create Writer from file
+ b := bufio.NewWriter(f)
+ // Write the image into the buffer
+ err = png.Encode(b, m)
+ if err != nil {
+ return err
+ }
+ err = b.Flush()
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/draw2dbase/dasher.go b/draw2dbase/dasher.go
index d0da022..6f8260c 100644
--- a/draw2dbase/dasher.go
+++ b/draw2dbase/dasher.go
@@ -3,24 +3,20 @@
package draw2dbase
-import (
- "github.com/llgcode/draw2d"
-)
-
type DashVertexConverter struct {
- next draw2d.Flattener
+ next Flattener
x, y, distance float64
dash []float64
currentDash int
dashOffset float64
}
-func NewDashConverter(dash []float64, dashOffset float64, converter draw2d.Flattener) *DashVertexConverter {
+func NewDashConverter(dash []float64, dashOffset float64, flattener Flattener) *DashVertexConverter {
var dasher DashVertexConverter
dasher.dash = dash
dasher.currentDash = 0
dasher.dashOffset = dashOffset
- dasher.next = converter
+ dasher.next = flattener
return &dasher
}
diff --git a/draw2dbase/demux_flattener.go b/draw2dbase/demux_flattener.go
index 6537797..13b6c40 100644
--- a/draw2dbase/demux_flattener.go
+++ b/draw2dbase/demux_flattener.go
@@ -1,11 +1,7 @@
package draw2dbase
-import (
- "github.com/llgcode/draw2d"
-)
-
type DemuxFlattener struct {
- Flatteners []draw2d.Flattener
+ Flatteners []Flattener
}
func (dc DemuxFlattener) MoveTo(x, y float64) {
diff --git a/draw2dbase/flattener.go b/draw2dbase/flattener.go
new file mode 100644
index 0000000..63c3823
--- /dev/null
+++ b/draw2dbase/flattener.go
@@ -0,0 +1,121 @@
+// Copyright 2010 The draw2d Authors. All rights reserved.
+// created: 06/12/2010 by Laurent Le Goff
+
+package draw2dbase
+
+import (
+ "github.com/llgcode/draw2d"
+)
+
+// Flattener receive segment definition
+type Flattener interface {
+ // MoveTo Start a New line from the point (x, y)
+ MoveTo(x, y float64)
+ // LineTo Draw a line from the current position to the point (x, y)
+ LineTo(x, y float64)
+ // LineJoin add the most recent starting point to close the path to create a polygon
+ LineJoin()
+ // Close add the most recent starting point to close the path to create a polygon
+ Close()
+ // End mark the current line as finished so we can draw caps
+ End()
+}
+
+// Flatten convert curves into straight segments keeping join segments info
+func Flatten(path *draw2d.Path, flattener Flattener, scale float64) {
+ // First Point
+ var startX, startY float64 = 0, 0
+ // Current Point
+ var x, y float64 = 0, 0
+ i := 0
+ for _, cmp := range path.Components {
+ switch cmp {
+ case draw2d.MoveToCmp:
+ x, y = path.Points[i], path.Points[i+1]
+ startX, startY = x, y
+ if i != 0 {
+ flattener.End()
+ }
+ flattener.MoveTo(x, y)
+ i += 2
+ case draw2d.LineToCmp:
+ x, y = path.Points[i], path.Points[i+1]
+ flattener.LineTo(x, y)
+ flattener.LineJoin()
+ i += 2
+ case draw2d.QuadCurveToCmp:
+ TraceQuad(flattener, path.Points[i-2:], 0.5)
+ x, y = path.Points[i+2], path.Points[i+3]
+ flattener.LineTo(x, y)
+ i += 4
+ case draw2d.CubicCurveToCmp:
+ TraceCubic(flattener, path.Points[i-2:], 0.5)
+ x, y = path.Points[i+4], path.Points[i+5]
+ flattener.LineTo(x, y)
+ i += 6
+ case draw2d.ArcToCmp:
+ x, y = TraceArc(flattener, path.Points[i], path.Points[i+1], path.Points[i+2], path.Points[i+3], path.Points[i+4], path.Points[i+5], scale)
+ flattener.LineTo(x, y)
+ i += 6
+ case draw2d.CloseCmp:
+ flattener.LineTo(startX, startY)
+ flattener.Close()
+ }
+ }
+ flattener.End()
+}
+
+// Transformer apply the Matrix transformation tr
+type Transformer struct {
+ Tr draw2d.MatrixTransform
+ Flattener Flattener
+}
+
+func (t Transformer) MoveTo(x, y float64) {
+ u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
+ v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
+ t.Flattener.MoveTo(u, v)
+}
+
+func (t Transformer) LineTo(x, y float64) {
+ u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
+ v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
+ t.Flattener.LineTo(u, v)
+}
+
+func (t Transformer) LineJoin() {
+ t.Flattener.LineJoin()
+}
+
+func (t Transformer) Close() {
+ t.Flattener.Close()
+}
+
+func (t Transformer) End() {
+ t.Flattener.End()
+}
+
+type SegmentedPath struct {
+ Points []float64
+}
+
+func (p *SegmentedPath) MoveTo(x, y float64) {
+ p.Points = append(p.Points, x, y)
+ // TODO need to mark this point as moveto
+}
+
+func (p *SegmentedPath) LineTo(x, y float64) {
+ p.Points = append(p.Points, x, y)
+}
+
+func (p *SegmentedPath) LineJoin() {
+ // TODO need to mark the current point as linejoin
+}
+
+func (p *SegmentedPath) Close() {
+ // TODO Close
+}
+
+func (p *SegmentedPath) End() {
+ // Nothing to do
+}
diff --git a/draw2dbase/stroker.go b/draw2dbase/stroker.go
index c61c65a..4d9be86 100644
--- a/draw2dbase/stroker.go
+++ b/draw2dbase/stroker.go
@@ -32,7 +32,7 @@ func toFtJoin(j draw2d.LineJoin) raster.Joiner {
}
type LineStroker struct {
- Next draw2d.Flattener
+ Flattener Flattener
HalfLineWidth float64
Cap draw2d.LineCap
Join draw2d.LineJoin
@@ -41,9 +41,9 @@ type LineStroker struct {
x, y, nx, ny float64
}
-func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener draw2d.Flattener) *LineStroker {
+func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener Flattener) *LineStroker {
l := new(LineStroker)
- l.Next = flattener
+ l.Flattener = flattener
l.HalfLineWidth = 0.5
l.Cap = c
l.Join = j
@@ -82,18 +82,18 @@ func (l *LineStroker) Close() {
func (l *LineStroker) End() {
if len(l.vertices) > 1 {
- l.Next.MoveTo(l.vertices[0], l.vertices[1])
+ l.Flattener.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.Flattener.LineTo(l.vertices[i], l.vertices[j])
}
}
for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 {
- l.Next.LineTo(l.rewind[i], l.rewind[j])
+ l.Flattener.LineTo(l.rewind[i], l.rewind[j])
}
if len(l.vertices) > 1 {
- l.Next.LineTo(l.vertices[0], l.vertices[1])
+ l.Flattener.LineTo(l.vertices[0], l.vertices[1])
}
- l.Next.End()
+ l.Flattener.End()
// reinit vertices
l.vertices = l.vertices[0:0]
l.rewind = l.rewind[0:0]
diff --git a/draw2dgl/gc.go b/draw2dgl/gc.go
index cc9bdc5..d9a055c 100644
--- a/draw2dgl/gc.go
+++ b/draw2dgl/gc.go
@@ -188,17 +188,17 @@ func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true
- stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
+ stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
- var liner draw2d.Flattener
+ var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
for _, p := range paths {
- p.Flatten(liner, gc.Current.Tr.GetScale())
+ draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
}
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
@@ -209,9 +209,9 @@ func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
/**** first method ****/
- flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
+ flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
for _, p := range paths {
- p.Flatten(flattener, gc.Current.Tr.GetScale())
+ draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
}
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
@@ -222,21 +222,21 @@ func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
gc.strokeRasterizer.UseNonZeroWinding = true
- flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
+ flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
- stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
+ stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
- var liner draw2d.Flattener
+ var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
- demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}}
+ demux := draw2dbase.DemuxFlattener{[]draw2dbase.Flattener{flattener, liner}}
for _, p := range paths {
- p.Flatten(demux, gc.Current.Tr.GetScale())
+ draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
}
// Fill
diff --git a/draw2dimg/ftgc.go b/draw2dimg/ftgc.go
index 9982d92..2e0da66 100644
--- a/draw2dimg/ftgc.go
+++ b/draw2dimg/ftgc.go
@@ -276,17 +276,17 @@ func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true
- stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
+ stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
- var liner draw2d.Flattener
+ var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
for _, p := range paths {
- p.Flatten(liner, gc.Current.Tr.GetScale())
+ draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
}
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
@@ -297,9 +297,9 @@ func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
/**** first method ****/
- flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
+ flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
for _, p := range paths {
- p.Flatten(flattener, gc.Current.Tr.GetScale())
+ draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
}
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
@@ -310,21 +310,21 @@ func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
gc.strokeRasterizer.UseNonZeroWinding = true
- flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
+ flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
- stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
+ stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
- var liner draw2d.Flattener
+ var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
- demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}}
+ demux := draw2dbase.DemuxFlattener{[]draw2dbase.Flattener{flattener, liner}}
for _, p := range paths {
- p.Flatten(demux, gc.Current.Tr.GetScale())
+ draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
}
// Fill
diff --git a/drawing_kit.go b/drawing_kit.go
index fe496af..f18d518 100644
--- a/drawing_kit.go
+++ b/drawing_kit.go
@@ -7,7 +7,7 @@ import (
"math"
)
-// Rectangle draw a rectangle using a PathBuilder
+// Rectangle draws a rectangle using a PathBuilder
func Rectangle(path PathBuilder, x1, y1, x2, y2 float64) {
path.MoveTo(x1, y1)
path.LineTo(x2, y1)
@@ -16,7 +16,7 @@ func Rectangle(path PathBuilder, x1, y1, x2, y2 float64) {
path.Close()
}
-// RoundedRectangle draw a rounded rectangle using a PathBuilder
+// RoundedRectangle draws a rounded rectangle using a PathBuilder
func RoundedRectangle(path PathBuilder, x1, y1, x2, y2, arcWidth, arcHeight float64) {
arcWidth = arcWidth / 2
arcHeight = arcHeight / 2
@@ -31,13 +31,13 @@ func RoundedRectangle(path PathBuilder, x1, y1, x2, y2, arcWidth, arcHeight floa
path.Close()
}
-// Ellipse draw an ellipse using a PathBuilder
+// Ellipse draws an ellipse using a PathBuilder
func Ellipse(path PathBuilder, cx, cy, rx, ry float64) {
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2)
path.Close()
}
-// Circle draw an circle using a PathBuilder
+// Circle draws a circle using a PathBuilder
func Circle(path PathBuilder, cx, cy, radius float64) {
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2)
path.Close()
diff --git a/flattener.go b/flattener.go
deleted file mode 100644
index d948ae5..0000000
--- a/flattener.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2010 The draw2d Authors. All rights reserved.
-// created: 06/12/2010 by Laurent Le Goff
-
-package draw2d
-
-// Flattener receive segment definition
-type Flattener interface {
- // MoveTo Start a New line from the point (x, y)
- MoveTo(x, y float64)
- // LineTo Draw a line from the current position to the point (x, y)
- LineTo(x, y float64)
- // LineJoin add the most recent starting point to close the path to create a polygon
- LineJoin()
- // Close add the most recent starting point to close the path to create a polygon
- Close()
- // End mark the current line as finished so we can draw caps
- End()
-}
-
-type SegmentedPath struct {
- Points []float64
-}
-
-func (p *SegmentedPath) MoveTo(x, y float64) {
- p.Points = append(p.Points, x, y)
- // TODO need to mark this point as moveto
-}
-
-func (p *SegmentedPath) LineTo(x, y float64) {
- p.Points = append(p.Points, x, y)
-}
-
-func (p *SegmentedPath) LineJoin() {
- // TODO need to mark the current point as linejoin
-}
-
-func (p *SegmentedPath) Close() {
- // TODO Close
-}
-
-func (p *SegmentedPath) End() {
- // Nothing to do
-}
-
-type LineCap int
-
-const (
- RoundCap LineCap = iota
- ButtCap
- SquareCap
-)
-
-type LineJoin int
-
-const (
- BevelJoin LineJoin = iota
- RoundJoin
- MiterJoin
-)
diff --git a/gc.go b/gc.go
index 0388708..463f6a8 100644
--- a/gc.go
+++ b/gc.go
@@ -15,6 +15,22 @@ const (
FillRuleWinding
)
+type LineCap int
+
+const (
+ RoundCap LineCap = iota
+ ButtCap
+ SquareCap
+)
+
+type LineJoin int
+
+const (
+ BevelJoin LineJoin = iota
+ RoundJoin
+ MiterJoin
+)
+
type GraphicContext interface {
PathBuilder
// Create a new path
diff --git a/path.go b/path.go
index 6ff5ccd..4f9b1b5 100644
--- a/path.go
+++ b/path.go
@@ -9,7 +9,7 @@ import (
"math"
)
-// PathBuilder define method that create path
+// PathBuilder defines methods that creates path
type PathBuilder interface {
// LastPoint returns the current point of the current path
LastPoint() (x, y float64)
@@ -33,7 +33,7 @@ type PathBuilder interface {
Close()
}
-// PathCmp represent components of a path
+// PathCmp represents component of a path
type PathCmp int
const (
@@ -174,47 +174,3 @@ func (p *Path) String() string {
}
return s
}
-
-// Flatten convert curves into straight segments keeping join segments info
-func (path *Path) Flatten(flattener Flattener, scale float64) {
- // First Point
- var startX, startY float64 = 0, 0
- // Current Point
- var x, y float64 = 0, 0
- i := 0
- for _, cmd := range path.Components {
- switch cmd {
- case MoveToCmp:
- x, y = path.Points[i], path.Points[i+1]
- startX, startY = x, y
- if i != 0 {
- flattener.End()
- }
- flattener.MoveTo(x, y)
- i += 2
- case LineToCmp:
- x, y = path.Points[i], path.Points[i+1]
- flattener.LineTo(x, y)
- flattener.LineJoin()
- i += 2
- case QuadCurveToCmp:
- TraceQuad(flattener, path.Points[i-2:], 0.5)
- x, y = path.Points[i+2], path.Points[i+3]
- flattener.LineTo(x, y)
- i += 4
- case CubicCurveToCmp:
- TraceCubic(flattener, path.Points[i-2:], 0.5)
- x, y = path.Points[i+4], path.Points[i+5]
- flattener.LineTo(x, y)
- i += 6
- case ArcToCmp:
- x, y = TraceArc(flattener, path.Points[i], path.Points[i+1], path.Points[i+2], path.Points[i+3], path.Points[i+4], path.Points[i+5], scale)
- flattener.LineTo(x, y)
- i += 6
- case CloseCmp:
- flattener.LineTo(startX, startY)
- flattener.Close()
- }
- }
- flattener.End()
-}
diff --git a/transform.go b/transform.go
index fb70941..9d924f3 100644
--- a/transform.go
+++ b/transform.go
@@ -246,33 +246,3 @@ func (tr MatrixTransform) IsTranslation() bool {
func fequals(float1, float2 float64) bool {
return math.Abs(float1-float2) <= epsilon
}
-
-// Transformer apply the Matrix transformation tr
-type Transformer struct {
- Tr MatrixTransform
- Flattener Flattener
-}
-
-func (t Transformer) MoveTo(x, y float64) {
- u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
- v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
- t.Flattener.MoveTo(u, v)
-}
-
-func (t Transformer) LineTo(x, y float64) {
- u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4]
- v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5]
- t.Flattener.LineTo(u, v)
-}
-
-func (t Transformer) LineJoin() {
- t.Flattener.LineJoin()
-}
-
-func (t Transformer) Close() {
- t.Flattener.Close()
-}
-
-func (t Transformer) End() {
- t.Flattener.End()
-}