From 82ef300f1d8f4b7fdb6f47ea457f63e30e4b6336 Mon Sep 17 00:00:00 2001 From: Laurent Le Goff Date: Wed, 29 Apr 2015 17:16:15 +0200 Subject: [PATCH] move flattening code in draw2dbase --- draw2dbase/curve.go | 161 ++++++++++++++++++++++++++++++++++ draw2dbase/curve_test.go | 136 ++++++++++++++++++++++++++++ draw2dbase/dasher.go | 10 +-- draw2dbase/demux_flattener.go | 6 +- draw2dbase/flattener.go | 121 +++++++++++++++++++++++++ draw2dbase/stroker.go | 16 ++-- draw2dgl/gc.go | 20 ++--- draw2dimg/ftgc.go | 20 ++--- drawing_kit.go | 8 +- flattener.go | 59 ------------- gc.go | 16 ++++ path.go | 48 +--------- transform.go | 30 ------- 13 files changed, 472 insertions(+), 179 deletions(-) create mode 100644 draw2dbase/curve.go create mode 100644 draw2dbase/curve_test.go create mode 100644 draw2dbase/flattener.go delete mode 100644 flattener.go 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() -}