From 966a9b73f79707d21089030b25161f4cdf2b5c5a Mon Sep 17 00:00:00 2001 From: Laurent Le Goff Date: Wed, 29 Apr 2015 14:33:32 +0200 Subject: [PATCH] remove path package and create draw2dimg package --- curve.go | 161 ++++++++++++++++++ curve/arc.go | 70 -------- curve/cubic.go | 80 --------- curve/line.go | 7 - curve/quad.go | 60 ------- curve/curve_test.go => curve_test.go | 32 ++-- {path => draw2dbase}/dasher.go | 14 +- draw2dbase/demux_flattener.go | 39 +++++ draw2dbase/ftpath.go | 29 ++++ stack_gc.go => draw2dbase/stack_gc.go | 86 +++++----- {path => draw2dbase}/stroker.go | 50 +++--- draw2dgl/gc.go | 47 ++--- fileutil.go => draw2dimg/fileutil.go | 2 +- ftgc.go => draw2dimg/ftgc.go | 111 ++++++------ .../rgba_interpolation.go | 6 +- path/drawing_kit.go => drawing_kit.go | 2 +- flattener.go | 59 +++++++ gc.go | 13 +- math.go | 11 -- path/path.go => path.go | 31 ++-- path/flattening.go | 56 ------ path/ftpath.go | 73 -------- path/utils.go | 13 -- transform.go | 52 +++--- 24 files changed, 506 insertions(+), 598 deletions(-) create mode 100644 curve.go delete mode 100644 curve/arc.go delete mode 100644 curve/cubic.go delete mode 100644 curve/line.go delete mode 100644 curve/quad.go rename curve/curve_test.go => curve_test.go (84%) rename {path => draw2dbase}/dasher.go (90%) create mode 100644 draw2dbase/demux_flattener.go create mode 100644 draw2dbase/ftpath.go rename stack_gc.go => draw2dbase/stack_gc.go (61%) rename {path => draw2dbase}/stroker.go (77%) rename fileutil.go => draw2dimg/fileutil.go (97%) rename ftgc.go => draw2dimg/ftgc.go (66%) rename rgba_interpolation.go => draw2dimg/rgba_interpolation.go (96%) rename path/drawing_kit.go => drawing_kit.go (98%) create mode 100644 flattener.go delete mode 100644 math.go rename path/path.go => path.go (89%) delete mode 100644 path/flattening.go delete mode 100644 path/ftpath.go delete mode 100644 path/utils.go diff --git a/curve.go b/curve.go new file mode 100644 index 0000000..3599144 --- /dev/null +++ b/curve.go @@ -0,0 +1,161 @@ +// Copyright 2010 The draw2d Authors. All rights reserved. +// created: 17/05/2011 by Laurent Le Goff + +package draw2d + +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/curve/arc.go b/curve/arc.go deleted file mode 100644 index 1d84d86..0000000 --- a/curve/arc.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package curve - -import ( - "math" - - "code.google.com/p/freetype-go/freetype/raster" -) - -// TraceArc trace an arc using a LineBuilder -func TraceArc(t LineBuilder, 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 -} - -// TraceArc trace an arc using a Freetype -func TraceArcFt(adder raster.Adder, x, y, rx, ry, start, angle, scale float64) raster.Point { - 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 raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)} - } - curX = x + math.Cos(angle)*rx - curY = y + math.Sin(angle)*ry - - angle += da - adder.Add1(raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)}) - } - return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)} -} diff --git a/curve/cubic.go b/curve/cubic.go deleted file mode 100644 index 89d6575..0000000 --- a/curve/cubic.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 17/05/2011 by Laurent Le Goff - -// Package curve implements Bezier Curve Subdivision using De Casteljau's algorithm -package curve - -import ( - "math" -) - -const ( - CurveRecursionLimit = 32 -) - -// x1, y1, cpx1, cpy1, cpx2, cpy2, x2, y2 float64 -// type Cubic []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 LineBuilder -// flattening_threshold helps determines the flattening expectation of the curve -func TraceCubic(t LineBuilder, 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++ - } - } -} diff --git a/curve/line.go b/curve/line.go deleted file mode 100644 index 177ba98..0000000 --- a/curve/line.go +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 12f66c5..0000000 --- a/curve/quad.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 17/05/2011 by Laurent Le Goff - -package curve - -import ( - "math" -) - -// x1, y1, cpx1, cpy2, x2, y2 float64 -// type Quad [6]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 LineBuilder -// flattening_threshold helps determines the flattening expectation of the curve -func TraceQuad(t LineBuilder, 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++ - } - } -} diff --git a/curve/curve_test.go b/curve_test.go similarity index 84% rename from curve/curve_test.go rename to curve_test.go index f3a8fd0..c4d2603 100644 --- a/curve/curve_test.go +++ b/curve_test.go @@ -1,4 +1,4 @@ -package curve +package draw2d import ( "bufio" @@ -34,18 +34,6 @@ var ( } ) -type Path struct { - points []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) -} - func init() { os.Mkdir("test_results", 0666) f, err := os.Create("test_results/_test.html") @@ -85,32 +73,32 @@ 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 + 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...) + 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...) + 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)) + 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 Path + 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...) + 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...) + 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)) + log.Printf("Num of points: %d\n", len(p.Points)) } fmt.Println() } @@ -118,7 +106,7 @@ func TestQuadCurve(t *testing.T) { func BenchmarkCubicCurve(b *testing.B) { for i := 0; i < b.N; i++ { for i := 0; i < len(testsCubicFloat64); i += 8 { - var p Path + var p SegmentedPath p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1]) TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold) } diff --git a/path/dasher.go b/draw2dbase/dasher.go similarity index 90% rename from path/dasher.go rename to draw2dbase/dasher.go index 8e5c115..d0da022 100644 --- a/path/dasher.go +++ b/draw2dbase/dasher.go @@ -1,17 +1,21 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 13/12/2010 by Laurent Le Goff -package path +package draw2dbase + +import ( + "github.com/llgcode/draw2d" +) type DashVertexConverter struct { - next LineBuilder + next draw2d.Flattener x, y, distance float64 dash []float64 currentDash int dashOffset float64 } -func NewDashConverter(dash []float64, dashOffset float64, converter LineBuilder) *DashVertexConverter { +func NewDashConverter(dash []float64, dashOffset float64, converter draw2d.Flattener) *DashVertexConverter { var dasher DashVertexConverter dasher.dash = dash dasher.currentDash = 0 @@ -83,3 +87,7 @@ func (dasher *DashVertexConverter) lineTo(x, y float64) { } dasher.x, dasher.y = x, y } + +func distance(x1, y1, x2, y2 float64) float64 { + return vectorDistance(x2-x1, y2-y1) +} diff --git a/draw2dbase/demux_flattener.go b/draw2dbase/demux_flattener.go new file mode 100644 index 0000000..6537797 --- /dev/null +++ b/draw2dbase/demux_flattener.go @@ -0,0 +1,39 @@ +package draw2dbase + +import ( + "github.com/llgcode/draw2d" +) + +type DemuxFlattener struct { + Flatteners []draw2d.Flattener +} + +func (dc DemuxFlattener) MoveTo(x, y float64) { + for _, flattener := range dc.Flatteners { + flattener.MoveTo(x, y) + } +} + +func (dc DemuxFlattener) LineTo(x, y float64) { + for _, flattener := range dc.Flatteners { + flattener.LineTo(x, y) + } +} + +func (dc DemuxFlattener) LineJoin() { + for _, flattener := range dc.Flatteners { + flattener.LineJoin() + } +} + +func (dc DemuxFlattener) Close() { + for _, flattener := range dc.Flatteners { + flattener.Close() + } +} + +func (dc DemuxFlattener) End() { + for _, flattener := range dc.Flatteners { + flattener.End() + } +} diff --git a/draw2dbase/ftpath.go b/draw2dbase/ftpath.go new file mode 100644 index 0000000..8e5601c --- /dev/null +++ b/draw2dbase/ftpath.go @@ -0,0 +1,29 @@ +// Copyright 2010 The draw2d Authors. All rights reserved. +// created: 13/12/2010 by Laurent Le Goff + +package draw2dbase + +import ( + "code.google.com/p/freetype-go/freetype/raster" +) + +type FtLineBuilder struct { + Adder raster.Adder +} + +func (liner FtLineBuilder) MoveTo(x, y float64) { + liner.Adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) +} + +func (liner FtLineBuilder) LineTo(x, y float64) { + liner.Adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) +} + +func (liner FtLineBuilder) LineJoin() { +} + +func (liner FtLineBuilder) Close() { +} + +func (liner FtLineBuilder) End() { +} diff --git a/stack_gc.go b/draw2dbase/stack_gc.go similarity index 61% rename from stack_gc.go rename to draw2dbase/stack_gc.go index ea34e79..6170d42 100644 --- a/stack_gc.go +++ b/draw2dbase/stack_gc.go @@ -1,40 +1,42 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 21/11/2010 by Laurent Le Goff -package draw2d +package draw2dbase import ( - "github.com/llgcode/draw2d/path" + "github.com/llgcode/draw2d" "image" "image/color" "code.google.com/p/freetype-go/freetype/truetype" ) +var DefaultFontData = draw2d.FontData{"luxi", draw2d.FontFamilySans, draw2d.FontStyleNormal} + type StackGraphicContext struct { Current *ContextStack } type ContextStack struct { - Tr MatrixTransform - Path *path.Path + Tr draw2d.MatrixTransform + Path *draw2d.Path LineWidth float64 Dash []float64 DashOffset float64 StrokeColor color.Color FillColor color.Color - FillRule FillRule - Cap path.Cap - Join path.Join + FillRule draw2d.FillRule + Cap draw2d.LineCap + Join draw2d.LineJoin FontSize float64 - FontData FontData + FontData draw2d.FontData - font *truetype.Font + Font *truetype.Font // fontSize and dpi are used to calculate scale. scale is the number of // 26.6 fixed point units in 1 em. - scale int32 + Scale int32 - previous *ContextStack + Previous *ContextStack } /** @@ -43,41 +45,41 @@ type ContextStack struct { func NewStackGraphicContext() *StackGraphicContext { gc := &StackGraphicContext{} gc.Current = new(ContextStack) - gc.Current.Tr = NewIdentityMatrix() - gc.Current.Path = new(path.Path) + gc.Current.Tr = draw2d.NewIdentityMatrix() + gc.Current.Path = new(draw2d.Path) gc.Current.LineWidth = 1.0 gc.Current.StrokeColor = image.Black gc.Current.FillColor = image.White - gc.Current.Cap = path.RoundCap - gc.Current.FillRule = FillRuleEvenOdd - gc.Current.Join = path.RoundJoin + gc.Current.Cap = draw2d.RoundCap + gc.Current.FillRule = draw2d.FillRuleEvenOdd + gc.Current.Join = draw2d.RoundJoin gc.Current.FontSize = 10 - gc.Current.FontData = defaultFontData + gc.Current.FontData = DefaultFontData return gc } -func (gc *StackGraphicContext) GetMatrixTransform() MatrixTransform { +func (gc *StackGraphicContext) GetMatrixTransform() draw2d.MatrixTransform { return gc.Current.Tr } -func (gc *StackGraphicContext) SetMatrixTransform(Tr MatrixTransform) { +func (gc *StackGraphicContext) SetMatrixTransform(Tr draw2d.MatrixTransform) { gc.Current.Tr = Tr } -func (gc *StackGraphicContext) ComposeMatrixTransform(Tr MatrixTransform) { +func (gc *StackGraphicContext) ComposeMatrixTransform(Tr draw2d.MatrixTransform) { gc.Current.Tr = Tr.Multiply(gc.Current.Tr) } func (gc *StackGraphicContext) Rotate(angle float64) { - gc.Current.Tr = NewRotationMatrix(angle).Multiply(gc.Current.Tr) + gc.Current.Tr = draw2d.NewRotationMatrix(angle).Multiply(gc.Current.Tr) } func (gc *StackGraphicContext) Translate(tx, ty float64) { - gc.Current.Tr = NewTranslationMatrix(tx, ty).Multiply(gc.Current.Tr) + gc.Current.Tr = draw2d.NewTranslationMatrix(tx, ty).Multiply(gc.Current.Tr) } func (gc *StackGraphicContext) Scale(sx, sy float64) { - gc.Current.Tr = NewScaleMatrix(sx, sy).Multiply(gc.Current.Tr) + gc.Current.Tr = draw2d.NewScaleMatrix(sx, sy).Multiply(gc.Current.Tr) } func (gc *StackGraphicContext) SetStrokeColor(c color.Color) { @@ -88,40 +90,40 @@ func (gc *StackGraphicContext) SetFillColor(c color.Color) { gc.Current.FillColor = c } -func (gc *StackGraphicContext) SetFillRule(f FillRule) { +func (gc *StackGraphicContext) SetFillRule(f draw2d.FillRule) { gc.Current.FillRule = f } -func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) { - gc.Current.LineWidth = LineWidth +func (gc *StackGraphicContext) SetLineWidth(lineWidth float64) { + gc.Current.LineWidth = lineWidth } -func (gc *StackGraphicContext) SetLineCap(cap path.Cap) { +func (gc *StackGraphicContext) SetLineCap(cap draw2d.LineCap) { gc.Current.Cap = cap } -func (gc *StackGraphicContext) SetLineJoin(join path.Join) { +func (gc *StackGraphicContext) SetLineJoin(join draw2d.LineJoin) { gc.Current.Join = join } -func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) { - gc.Current.Dash = Dash - gc.Current.DashOffset = DashOffset +func (gc *StackGraphicContext) SetLineDash(dash []float64, dashOffset float64) { + gc.Current.Dash = dash + gc.Current.DashOffset = dashOffset } -func (gc *StackGraphicContext) SetFontSize(FontSize float64) { - gc.Current.FontSize = FontSize +func (gc *StackGraphicContext) SetFontSize(fontSize float64) { + gc.Current.FontSize = fontSize } func (gc *StackGraphicContext) GetFontSize() float64 { return gc.Current.FontSize } -func (gc *StackGraphicContext) SetFontData(FontData FontData) { - gc.Current.FontData = FontData +func (gc *StackGraphicContext) SetFontData(fontData draw2d.FontData) { + gc.Current.FontData = fontData } -func (gc *StackGraphicContext) GetFontData() FontData { +func (gc *StackGraphicContext) GetFontData() draw2d.FontData { return gc.Current.FontData } @@ -174,17 +176,17 @@ func (gc *StackGraphicContext) Save() { context.Cap = gc.Current.Cap context.Join = gc.Current.Join context.Path = gc.Current.Path.Copy() - context.font = gc.Current.font - context.scale = gc.Current.scale + context.Font = gc.Current.Font + context.Scale = gc.Current.Scale copy(context.Tr[:], gc.Current.Tr[:]) - context.previous = gc.Current + context.Previous = gc.Current gc.Current = context } func (gc *StackGraphicContext) Restore() { - if gc.Current.previous != nil { + if gc.Current.Previous != nil { oldContext := gc.Current - gc.Current = gc.Current.previous - oldContext.previous = nil + gc.Current = gc.Current.Previous + oldContext.Previous = nil } } diff --git a/path/stroker.go b/draw2dbase/stroker.go similarity index 77% rename from path/stroker.go rename to draw2dbase/stroker.go index 60dfac4..c61c65a 100644 --- a/path/stroker.go +++ b/draw2dbase/stroker.go @@ -1,66 +1,50 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 13/12/2010 by Laurent Le Goff -package path +package draw2dbase import ( "code.google.com/p/freetype-go/freetype/raster" + "github.com/llgcode/draw2d" + "math" ) -type Cap int - -const ( - RoundCap Cap = iota - ButtCap - SquareCap -) - -func (c Cap) Convert() raster.Capper { +func toFtCap(c draw2d.LineCap) raster.Capper { switch c { - case RoundCap: + case draw2d.RoundCap: return raster.RoundCapper - case ButtCap: + case draw2d.ButtCap: return raster.ButtCapper - case SquareCap: + case draw2d.SquareCap: return raster.SquareCapper } return raster.RoundCapper } -type Join int - -const ( - BevelJoin Join = iota - RoundJoin - MiterJoin -) - -func (j Join) Convert() raster.Joiner { +func toFtJoin(j draw2d.LineJoin) raster.Joiner { switch j { - case RoundJoin: + case draw2d.RoundJoin: return raster.RoundJoiner - case BevelJoin: + case draw2d.BevelJoin: return raster.BevelJoiner } return raster.RoundJoiner } type LineStroker struct { - Next LineBuilder + Next draw2d.Flattener HalfLineWidth float64 - Cap Cap - Join Join + Cap draw2d.LineCap + Join draw2d.LineJoin vertices []float64 rewind []float64 x, y, nx, ny float64 } -func NewLineStroker(c Cap, j Join, converter LineBuilder) *LineStroker { +func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener draw2d.Flattener) *LineStroker { l := new(LineStroker) - l.Next = converter + l.Next = flattener l.HalfLineWidth = 0.5 - l.vertices = make([]float64, 0, 256) - l.rewind = make([]float64, 0, 256) l.Cap = c l.Join = j return l @@ -122,3 +106,7 @@ func (l *LineStroker) appendVertex(vertices ...float64) { l.vertices = append(l.vertices, vertices[:s]...) l.rewind = append(l.rewind, vertices[s:]...) } + +func vectorDistance(dx, dy float64) float64 { + return float64(math.Sqrt(dx*dx + dy*dy)) +} diff --git a/draw2dgl/gc.go b/draw2dgl/gc.go index 5297a2b..cc9bdc5 100644 --- a/draw2dgl/gc.go +++ b/draw2dgl/gc.go @@ -9,7 +9,7 @@ import ( "code.google.com/p/freetype-go/freetype/raster" "github.com/go-gl/gl/v2.1/gl" "github.com/llgcode/draw2d" - "github.com/llgcode/draw2d/path" + "github.com/llgcode/draw2d/draw2dbase" ) func init() { @@ -113,7 +113,7 @@ func NewPainter() *Painter { } type GraphicContext struct { - *draw2d.StackGraphicContext + *draw2dbase.StackGraphicContext painter *Painter fillRasterizer *raster.Rasterizer strokeRasterizer *raster.Rasterizer @@ -122,7 +122,7 @@ type GraphicContext struct { // NewGraphicContext creates a new Graphic context from an image. func NewGraphicContext(width, height int) *GraphicContext { gc := &GraphicContext{ - draw2d.NewStackGraphicContext(), + draw2dbase.NewStackGraphicContext(), NewPainter(), raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), @@ -184,20 +184,19 @@ func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color gc.Current.Path.Clear() } -func (gc *GraphicContext) Stroke(paths ...*path.Path) { +func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) { paths = append(paths, gc.Current.Path) gc.strokeRasterizer.UseNonZeroWinding = true - stroker := path.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.strokeRasterizer))) + stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}}) stroker.HalfLineWidth = gc.Current.LineWidth / 2 - var liner path.LineBuilder + var liner draw2d.Flattener if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { - liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) + liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) } else { liner = stroker } - for _, p := range paths { p.Flatten(liner, gc.Current.Tr.GetScale()) } @@ -205,37 +204,37 @@ func (gc *GraphicContext) Stroke(paths ...*path.Path) { gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } -func (gc *GraphicContext) Fill(paths ...*path.Path) { +func (gc *GraphicContext) Fill(paths ...*draw2d.Path) { paths = append(paths, gc.Current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() + gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule) - flattener := draw2d.NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer)) + /**** first method ****/ + flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}} for _, p := range paths { p.Flatten(flattener, gc.Current.Tr.GetScale()) } gc.paint(gc.fillRasterizer, gc.Current.FillColor) - gc.Current.Path.Clear() } -func (gc *GraphicContext) FillStroke(paths ...*path.Path) { +func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) { paths = append(paths, gc.Current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() + gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule) gc.strokeRasterizer.UseNonZeroWinding = true - flattener := draw2d.NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer)) + flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}} - stroker := path.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.strokeRasterizer))) + stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}}) stroker.HalfLineWidth = gc.Current.LineWidth / 2 - var liner path.LineBuilder + var liner draw2d.Flattener if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { - liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) + liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) } else { liner = stroker } - demux := path.NewLineBuilders(flattener, liner) + demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}} for _, p := range paths { p.Flatten(demux, gc.Current.Tr.GetScale()) } @@ -245,3 +244,13 @@ func (gc *GraphicContext) FillStroke(paths ...*path.Path) { // Stroke gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } + +func useNonZeroWinding(f draw2d.FillRule) bool { + switch f { + case draw2d.FillRuleEvenOdd: + return false + case draw2d.FillRuleWinding: + return true + } + return false +} diff --git a/fileutil.go b/draw2dimg/fileutil.go similarity index 97% rename from fileutil.go rename to draw2dimg/fileutil.go index a97d590..b6dccb9 100644 --- a/fileutil.go +++ b/draw2dimg/fileutil.go @@ -1,4 +1,4 @@ -package draw2d +package draw2dimg import ( "bufio" diff --git a/ftgc.go b/draw2dimg/ftgc.go similarity index 66% rename from ftgc.go rename to draw2dimg/ftgc.go index 86647cf..9982d92 100644 --- a/ftgc.go +++ b/draw2dimg/ftgc.go @@ -1,11 +1,12 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 21/11/2010 by Laurent Le Goff -package draw2d +package draw2dimg import ( "errors" - "github.com/llgcode/draw2d/path" + "github.com/llgcode/draw2d" + "github.com/llgcode/draw2d/draw2dbase" "image" "image/color" "image/draw" @@ -21,12 +22,8 @@ type Painter interface { SetColor(color color.Color) } -var ( - defaultFontData = FontData{"luxi", FontFamilySans, FontStyleNormal} -) - -type ImageGraphicContext struct { - *StackGraphicContext +type GraphicContext struct { + *draw2dbase.StackGraphicContext img draw.Image painter Painter fillRasterizer *raster.Rasterizer @@ -38,7 +35,7 @@ type ImageGraphicContext struct { /** * Create a new Graphic context from an image */ -func NewGraphicContext(img draw.Image) *ImageGraphicContext { +func NewGraphicContext(img draw.Image) *GraphicContext { var painter Painter switch selectImage := img.(type) { case *image.RGBA: @@ -50,11 +47,11 @@ func NewGraphicContext(img draw.Image) *ImageGraphicContext { } // Create a new Graphic context from an image and a Painter (see Freetype-go) -func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext { +func NewGraphicContextWithPainter(img draw.Image, painter Painter) *GraphicContext { width, height := img.Bounds().Dx(), img.Bounds().Dy() dpi := 92 - gc := &ImageGraphicContext{ - NewStackGraphicContext(), + gc := &GraphicContext{ + draw2dbase.NewStackGraphicContext(), img, painter, raster.NewRasterizer(width, height), @@ -65,48 +62,48 @@ func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphic return gc } -func (gc *ImageGraphicContext) GetDPI() int { +func (gc *GraphicContext) GetDPI() int { return gc.DPI } -func (gc *ImageGraphicContext) Clear() { +func (gc *GraphicContext) Clear() { width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy() gc.ClearRect(0, 0, width, height) } -func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) { +func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) { imageColor := image.NewUniform(gc.Current.FillColor) draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over) } -func (gc *ImageGraphicContext) DrawImage(img image.Image) { +func (gc *GraphicContext) DrawImage(img image.Image) { DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter) } -func (gc *ImageGraphicContext) FillString(text string) (cursor float64) { +func (gc *GraphicContext) FillString(text string) (cursor float64) { return gc.FillStringAt(text, 0, 0) } -func (gc *ImageGraphicContext) FillStringAt(text string, x, y float64) (cursor float64) { +func (gc *GraphicContext) FillStringAt(text string, x, y float64) (cursor float64) { width := gc.CreateStringPath(text, x, y) gc.Fill() return width } -func (gc *ImageGraphicContext) StrokeString(text string) (cursor float64) { +func (gc *GraphicContext) StrokeString(text string) (cursor float64) { return gc.StrokeStringAt(text, 0, 0) } -func (gc *ImageGraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) { +func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) { width := gc.CreateStringPath(text, x, y) gc.Stroke() return width } -func (gc *ImageGraphicContext) loadCurrentFont() (*truetype.Font, error) { - font := GetFont(gc.Current.FontData) +func (gc *GraphicContext) loadCurrentFont() (*truetype.Font, error) { + font := draw2d.GetFont(gc.Current.FontData) if font == nil { - font = GetFont(defaultFontData) + font = draw2d.GetFont(draw2dbase.DefaultFontData) } if font == nil { return nil, errors.New("No font set, and no default font available.") @@ -129,7 +126,7 @@ func pointToF64Point(p truetype.Point) (x, y float64) { } // drawContour draws the given closed contour at the given sub-pixel offset. -func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) { +func (gc *GraphicContext) drawContour(ps []truetype.Point, dx, dy float64) { if len(ps) == 0 { return } @@ -164,8 +161,8 @@ func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) } } -func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error { - if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, glyph, truetype.NoHinting); err != nil { +func (gc *GraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error { + if err := gc.glyphBuf.Load(gc.Current.Font, gc.Current.Scale, glyph, truetype.NoHinting); err != nil { return err } e0 := 0 @@ -182,7 +179,7 @@ func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) e // above and to the right of the point, but some may be below or to the left. // For example, drawing a string that starts with a 'J' in an italic font may // affect pixels below and left of the point. -func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 { +func (gc *GraphicContext) CreateStringPath(s string, x, y float64) float64 { font, err := gc.loadCurrentFont() if err != nil { log.Println(err) @@ -193,14 +190,14 @@ func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 for _, rune := range s { index := font.Index(rune) if hasPrev { - x += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index)) + x += fUnitsToFloat64(font.Kerning(gc.Current.Scale, prev, index)) } err := gc.drawGlyph(index, x, y) if err != nil { log.Println(err) return startx - x } - x += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth) + x += fUnitsToFloat64(font.HMetric(gc.Current.Scale, index).AdvanceWidth) prev, hasPrev = index, true } return x - startx @@ -210,7 +207,7 @@ func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 // The the left edge of the em square of the first character of s // and the baseline intersect at 0, 0 in the returned coordinates. // Therefore the top and left coordinates may well be negative. -func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) { +func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) { font, err := gc.loadCurrentFont() if err != nil { log.Println(err) @@ -222,9 +219,9 @@ func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bott for _, rune := range s { index := font.Index(rune) if hasPrev { - cursor += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index)) + cursor += fUnitsToFloat64(font.Kerning(gc.Current.Scale, prev, index)) } - if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, index, truetype.NoHinting); err != nil { + if err := gc.glyphBuf.Load(gc.Current.Font, gc.Current.Scale, index, truetype.NoHinting); err != nil { log.Println(err) return 0, 0, 0, 0 } @@ -239,7 +236,7 @@ func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bott right = math.Max(right, x+cursor) } } - cursor += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth) + cursor += fUnitsToFloat64(font.HMetric(gc.Current.Scale, index).AdvanceWidth) prev, hasPrev = index, true } return left, top, right, bottom @@ -247,44 +244,44 @@ func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bott // recalc recalculates scale and bounds values from the font size, screen // resolution and font metrics, and invalidates the glyph cache. -func (gc *ImageGraphicContext) recalc() { - gc.Current.scale = int32(gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0)) +func (gc *GraphicContext) recalc() { + gc.Current.Scale = int32(gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0)) } // SetDPI sets the screen resolution in dots per inch. -func (gc *ImageGraphicContext) SetDPI(dpi int) { +func (gc *GraphicContext) SetDPI(dpi int) { gc.DPI = dpi gc.recalc() } // SetFont sets the font used to draw text. -func (gc *ImageGraphicContext) SetFont(font *truetype.Font) { - gc.Current.font = font +func (gc *GraphicContext) SetFont(font *truetype.Font) { + gc.Current.Font = font } // SetFontSize sets the font size in points (as in ``a 12 point font''). -func (gc *ImageGraphicContext) SetFontSize(fontSize float64) { +func (gc *GraphicContext) SetFontSize(fontSize float64) { gc.Current.FontSize = fontSize gc.recalc() } -func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) { +func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) { gc.painter.SetColor(color) rasterizer.Rasterize(gc.painter) rasterizer.Clear() gc.Current.Path.Clear() } -func (gc *ImageGraphicContext) Stroke(paths ...*path.Path) { +func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) { paths = append(paths, gc.Current.Path) gc.strokeRasterizer.UseNonZeroWinding = true - stroker := path.NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.strokeRasterizer))) + stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}}) stroker.HalfLineWidth = gc.Current.LineWidth / 2 - var liner path.LineBuilder + var liner draw2d.Flattener if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { - liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) + liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) } else { liner = stroker } @@ -295,12 +292,12 @@ func (gc *ImageGraphicContext) Stroke(paths ...*path.Path) { gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } -func (gc *ImageGraphicContext) Fill(paths ...*path.Path) { +func (gc *GraphicContext) Fill(paths ...*draw2d.Path) { paths = append(paths, gc.Current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() + gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule) /**** first method ****/ - flattener := NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer)) + flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}} for _, p := range paths { p.Flatten(flattener, gc.Current.Tr.GetScale()) } @@ -308,24 +305,24 @@ func (gc *ImageGraphicContext) Fill(paths ...*path.Path) { gc.paint(gc.fillRasterizer, gc.Current.FillColor) } -func (gc *ImageGraphicContext) FillStroke(paths ...*path.Path) { +func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) { paths = append(paths, gc.Current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() + gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule) gc.strokeRasterizer.UseNonZeroWinding = true - flattener := NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer)) + flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}} - stroker := path.NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.strokeRasterizer))) + stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}}) stroker.HalfLineWidth = gc.Current.LineWidth / 2 - var liner path.LineBuilder + var liner draw2d.Flattener if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { - liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) + liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) } else { liner = stroker } - demux := path.NewLineBuilders(flattener, liner) + demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}} for _, p := range paths { p.Flatten(demux, gc.Current.Tr.GetScale()) } @@ -336,11 +333,11 @@ func (gc *ImageGraphicContext) FillStroke(paths ...*path.Path) { gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } -func (f FillRule) UseNonZeroWinding() bool { +func useNonZeroWinding(f draw2d.FillRule) bool { switch f { - case FillRuleEvenOdd: + case draw2d.FillRuleEvenOdd: return false - case FillRuleWinding: + case draw2d.FillRuleWinding: return true } return false diff --git a/rgba_interpolation.go b/draw2dimg/rgba_interpolation.go similarity index 96% rename from rgba_interpolation.go rename to draw2dimg/rgba_interpolation.go index 92534e7..fc59581 100644 --- a/rgba_interpolation.go +++ b/draw2dimg/rgba_interpolation.go @@ -2,9 +2,10 @@ // created: 21/11/2010 by Laurent Le Goff // see http://pippin.gimp.org/image_processing/chap_resampling.html -package draw2d +package draw2dimg import ( + "github.com/llgcode/draw2d" "image" "image/color" "image/draw" @@ -17,6 +18,7 @@ const ( LinearFilter ImageFilter = iota BilinearFilter BicubicFilter + M = 1<<16 - 1 ) //see http://pippin.gimp.org/image_processing/chap_resampling.html @@ -103,7 +105,7 @@ func cubic(offset, v0, v1, v2, v3 float64) uint32 { (-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0) } -func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) { +func DrawImage(src image.Image, dest draw.Image, tr draw2d.MatrixTransform, op draw.Op, filter ImageFilter) { bounds := src.Bounds() x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y) tr.TransformRectangle(&x0, &y0, &x1, &y1) diff --git a/path/drawing_kit.go b/drawing_kit.go similarity index 98% rename from path/drawing_kit.go rename to drawing_kit.go index 89fc3c7..fe496af 100644 --- a/path/drawing_kit.go +++ b/drawing_kit.go @@ -1,7 +1,7 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 13/12/2010 by Laurent Le Goff -package path +package draw2d import ( "math" diff --git a/flattener.go b/flattener.go new file mode 100644 index 0000000..d948ae5 --- /dev/null +++ b/flattener.go @@ -0,0 +1,59 @@ +// 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 6b91f9a..0388708 100644 --- a/gc.go +++ b/gc.go @@ -4,7 +4,6 @@ package draw2d import ( - "github.com/llgcode/draw2d/path" "image" "image/color" ) @@ -17,7 +16,7 @@ const ( ) type GraphicContext interface { - path.PathBuilder + PathBuilder // Create a new path BeginPath() GetMatrixTransform() MatrixTransform @@ -30,8 +29,8 @@ type GraphicContext interface { SetFillColor(c color.Color) SetFillRule(f FillRule) SetLineWidth(lineWidth float64) - SetLineCap(cap path.Cap) - SetLineJoin(join path.Join) + SetLineCap(cap LineCap) + SetLineJoin(join LineJoin) SetLineDash(dash []float64, dashOffset float64) SetFontSize(fontSize float64) GetFontSize() float64 @@ -50,7 +49,7 @@ type GraphicContext interface { FillStringAt(text string, x, y float64) (cursor float64) StrokeString(text string) (cursor float64) StrokeStringAt(text string, x, y float64) (cursor float64) - Stroke(paths ...*path.Path) - Fill(paths ...*path.Path) - FillStroke(paths ...*path.Path) + Stroke(paths ...*Path) + Fill(paths ...*Path) + FillStroke(paths ...*Path) } diff --git a/math.go b/math.go deleted file mode 100644 index 8e15c14..0000000 --- a/math.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -func minMax(x, y float64) (min, max float64) { - if x > y { - return y, x - } - return x, y -} diff --git a/path/path.go b/path.go similarity index 89% rename from path/path.go rename to path.go index 203837a..7a5b38e 100644 --- a/path/path.go +++ b/path.go @@ -2,11 +2,10 @@ // created: 21/11/2010 by Laurent Le Goff // Package path implements function to build path -package path +package draw2d import ( "fmt" - "github.com/llgcode/draw2d/curve" "math" ) @@ -177,7 +176,7 @@ func (p *Path) String() string { } // Flatten convert curves in straight segments keeping join segements -func (path *Path) Flatten(liner LineBuilder, scale float64) { +func (path *Path) Flatten(flattener Flattener, scale float64) { // First Point var startX, startY float64 = 0, 0 // Current Point @@ -189,33 +188,33 @@ func (path *Path) Flatten(liner LineBuilder, scale float64) { x, y = path.Points[i], path.Points[i+1] startX, startY = x, y if i != 0 { - liner.End() + flattener.End() } - liner.MoveTo(x, y) + flattener.MoveTo(x, y) i += 2 case LineToCmp: x, y = path.Points[i], path.Points[i+1] - liner.LineTo(x, y) - liner.LineJoin() + flattener.LineTo(x, y) + flattener.LineJoin() i += 2 case QuadCurveToCmp: - curve.TraceQuad(liner, path.Points[i-2:], 0.5) + TraceQuad(flattener, path.Points[i-2:], 0.5) x, y = path.Points[i+2], path.Points[i+3] - liner.LineTo(x, y) + flattener.LineTo(x, y) i += 4 case CubicCurveToCmp: - curve.TraceCubic(liner, path.Points[i-2:], 0.5) + TraceCubic(flattener, path.Points[i-2:], 0.5) x, y = path.Points[i+4], path.Points[i+5] - liner.LineTo(x, y) + flattener.LineTo(x, y) i += 6 case ArcToCmp: - x, y = curve.TraceArc(liner, path.Points[i], path.Points[i+1], path.Points[i+2], path.Points[i+3], path.Points[i+4], path.Points[i+5], scale) - liner.LineTo(x, y) + 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: - liner.LineTo(startX, startY) - liner.Close() + flattener.LineTo(startX, startY) + flattener.Close() } } - liner.End() + flattener.End() } diff --git a/path/flattening.go b/path/flattening.go deleted file mode 100644 index 9f18061..0000000 --- a/path/flattening.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 06/12/2010 by Laurent Le Goff - -package path - -// LineBuilder defines drawing line methods -type LineBuilder 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 LineBuilders struct { - builders []LineBuilder -} - -func NewLineBuilders(builders ...LineBuilder) *LineBuilders { - return &LineBuilders{builders} -} - -func (dc *LineBuilders) MoveTo(x, y float64) { - for _, converter := range dc.builders { - converter.MoveTo(x, y) - } -} - -func (dc *LineBuilders) LineTo(x, y float64) { - for _, converter := range dc.builders { - converter.LineTo(x, y) - } -} - -func (dc *LineBuilders) LineJoin() { - for _, converter := range dc.builders { - converter.LineJoin() - } -} - -func (dc *LineBuilders) Close() { - for _, converter := range dc.builders { - converter.Close() - } -} - -func (dc *LineBuilders) End() { - for _, converter := range dc.builders { - converter.End() - } -} diff --git a/path/ftpath.go b/path/ftpath.go deleted file mode 100644 index d2b40d5..0000000 --- a/path/ftpath.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package path - -import ( - "code.google.com/p/freetype-go/freetype/raster" - "github.com/llgcode/draw2d/curve" -) - -type FtLineBuilder struct { - adder raster.Adder -} - -func NewFtLineBuilder(adder raster.Adder) *FtLineBuilder { - return &FtLineBuilder{adder} -} - -func (FtLineBuilder *FtLineBuilder) MoveTo(x, y float64) { - FtLineBuilder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) -} - -func (FtLineBuilder *FtLineBuilder) LineTo(x, y float64) { - FtLineBuilder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) -} - -func (FtLineBuilder *FtLineBuilder) LineJoin() { -} - -func (FtLineBuilder *FtLineBuilder) Close() { -} - -func (FtLineBuilder *FtLineBuilder) End() { -} - -type PathAdder struct { - adder raster.Adder - firstPoint raster.Point - ApproximationScale float64 -} - -func NewPathAdder(adder raster.Adder) *PathAdder { - return &PathAdder{adder, raster.Point{0, 0}, 1} -} - -func (pathAdder *PathAdder) Convert(paths ...*Path) { - for _, apath := range paths { - j := 0 - for _, cmd := range apath.Components { - switch cmd { - case MoveToCmp: - pathAdder.firstPoint = raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)} - pathAdder.adder.Start(pathAdder.firstPoint) - j += 2 - case LineToCmp: - pathAdder.adder.Add1(raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)}) - j += 2 - case QuadCurveToCmp: - pathAdder.adder.Add2(raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)}, raster.Point{raster.Fix32(apath.Points[j+2] * 256), raster.Fix32(apath.Points[j+3] * 256)}) - j += 4 - case CubicCurveToCmp: - pathAdder.adder.Add3(raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)}, raster.Point{raster.Fix32(apath.Points[j+2] * 256), raster.Fix32(apath.Points[j+3] * 256)}, raster.Point{raster.Fix32(apath.Points[j+4] * 256), raster.Fix32(apath.Points[j+5] * 256)}) - j += 6 - case ArcToCmp: - lastPoint := curve.TraceArcFt(pathAdder.adder, apath.Points[j], apath.Points[j+1], apath.Points[j+2], apath.Points[j+3], apath.Points[j+4], apath.Points[j+5], pathAdder.ApproximationScale) - pathAdder.adder.Add1(lastPoint) - j += 6 - case CloseCmp: - pathAdder.adder.Add1(pathAdder.firstPoint) - } - } - } -} diff --git a/path/utils.go b/path/utils.go deleted file mode 100644 index 68dbc35..0000000 --- a/path/utils.go +++ /dev/null @@ -1,13 +0,0 @@ -package path - -import ( - "math" -) - -func distance(x1, y1, x2, y2 float64) float64 { - return vectorDistance(x2-x1, y2-y1) -} - -func vectorDistance(dx, dy float64) float64 { - return float64(math.Sqrt(dx*dx + dy*dy)) -} diff --git a/transform.go b/transform.go index e3a8a86..e1d82fe 100644 --- a/transform.go +++ b/transform.go @@ -7,7 +7,6 @@ import ( "math" "code.google.com/p/freetype-go/freetype/raster" - "github.com/llgcode/draw2d/path" ) type MatrixTransform [6]float64 @@ -38,6 +37,13 @@ func (tr MatrixTransform) TransformArray(points []float64) { } } +func minMax(x, y float64) (min, max float64) { + if x > y { + return y, x + } + return x, y +} + func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2 *float64) { x1 := *x2 y1 := *y0 @@ -252,38 +258,34 @@ func fequals(float1, float2 float64) bool { return math.Abs(float1-float2) <= epsilon } -// this VertexConverter apply the Matrix transformation tr -type VertexMatrixTransform struct { - tr MatrixTransform - Next path.LineBuilder +// Transformer apply the Matrix transformation tr +type Transformer struct { + Tr MatrixTransform + Flattener Flattener } -func NewVertexMatrixTransform(tr MatrixTransform, converter path.LineBuilder) *VertexMatrixTransform { - return &VertexMatrixTransform{tr, converter} +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 (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.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 (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) +func (t Transformer) LineJoin() { + t.Flattener.LineJoin() } -func (vmt *VertexMatrixTransform) LineJoin() { - vmt.Next.LineJoin() +func (t Transformer) Close() { + t.Flattener.Close() } -func (vmt *VertexMatrixTransform) Close() { - vmt.Next.Close() -} - -func (vmt *VertexMatrixTransform) End() { - vmt.Next.End() +func (t Transformer) End() { + t.Flattener.End() } // this adder apply a Matrix transformation to points @@ -292,10 +294,6 @@ type MatrixTransformAdder struct { next raster.Adder } -func NewMatrixTransformAdder(tr MatrixTransform, adder raster.Adder) *MatrixTransformAdder { - return &MatrixTransformAdder{tr, adder} -} - // Start starts a new curve at the given point. func (mta MatrixTransformAdder) Start(a raster.Point) { mta.tr.TransformRasterPoint(&a)