diff --git a/arc.go b/arc.go deleted file mode 100644 index 03213a6..0000000 --- a/arc.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "math" - - "code.google.com/p/freetype-go/freetype/raster" -) - -func arc(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 -} - -func arcAdder(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/arc.go b/curve/arc.go index 733bf3e..1d84d86 100644 --- a/curve/arc.go +++ b/curve/arc.go @@ -5,10 +5,12 @@ 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) { +func TraceArc(t LineBuilder, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) { end := start + angle clockWise := true if angle < 0 { @@ -26,7 +28,7 @@ func TraceArc(t LineBuilder, x, y, rx, ry, start, angle, scale float64) { if (angle < end-da/4) != clockWise { curX = x + math.Cos(end)*rx curY = y + math.Sin(end)*ry - break + return curX, curY } curX = x + math.Cos(angle)*rx curY = y + math.Sin(angle)*ry @@ -34,5 +36,35 @@ func TraceArc(t LineBuilder, x, y, rx, ry, start, angle, scale float64) { angle += da t.LineTo(curX, curY) } - 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/curves.go b/curves.go deleted file mode 100644 index 4accdb1..0000000 --- a/curves.go +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "math" -) - -var ( - CurveRecursionLimit = 32 - CurveCollinearityEpsilon = 1e-30 - CurveAngleToleranceEpsilon = 0.01 -) - -/* - The function has the following parameters: - approximationScale : - Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one. - It always has some scaling coefficient. - The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels. - Usually it looks as follows: - curved.approximationScale(transform.scale()); - where transform is the affine matrix that includes all the transformations, including viewport and zoom. - angleTolerance : - You set it in radians. - The less this value is the more accurate will be the approximation at sharp turns. - But 0 means that we don't consider angle conditions at all. - cuspLimit : - An angle in radians. - If 0, only the real cusps will have bevel cuts. - If more than 0, it will restrict the sharpness. - The more this value is the less sharp turns will be cut. - Typically it should not exceed 10-15 degrees. -*/ -func cubicBezier(v LineBuilder, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float64) { - cuspLimit = computeCuspLimit(cuspLimit) - distanceToleranceSquare := 0.5 / approximationScale - distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare - recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit) -} - -/* - * see cubicBezier comments for approximationScale and angleTolerance definition - */ -func quadraticBezier(v LineBuilder, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float64) { - distanceToleranceSquare := 0.5 / approximationScale - distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare - - recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance) -} - -func computeCuspLimit(v float64) (r float64) { - if v == 0.0 { - r = 0.0 - } else { - r = math.Pi - v - } - return -} - -/** - * http://www.antigrain.com/research/adaptive_bezier/index.html - */ -func recursiveQuadraticBezierBezier(v LineBuilder, x1, y1, x2, y2, x3, y3 float64, level int, distanceToleranceSquare, angleTolerance float64) { - if level > CurveRecursionLimit { - return - } - - // Calculate all the mid-points of the line segments - //---------------------- - x12 := (x1 + x2) / 2 - y12 := (y1 + y2) / 2 - x23 := (x2 + x3) / 2 - y23 := (y2 + y3) / 2 - x123 := (x12 + x23) / 2 - y123 := (y12 + y23) / 2 - - dx := x3 - x1 - dy := y3 - y1 - d := math.Abs(((x2-x3)*dy - (y2-y3)*dx)) - - if d > CurveCollinearityEpsilon { - // Regular case - //----------------- - if d*d <= distanceToleranceSquare*(dx*dx+dy*dy) { - // If the curvature doesn't exceed the distanceTolerance value - // we tend to finish subdivisions. - //---------------------- - if angleTolerance < CurveAngleToleranceEpsilon { - v.LineTo(x123, y123) - return - } - - // Angle & Cusp Condition - //---------------------- - da := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1)) - if da >= math.Pi { - da = 2*math.Pi - da - } - - if da < angleTolerance { - // Finally we can stop the recursion - //---------------------- - v.LineTo(x123, y123) - return - } - } - } else { - // Collinear case - //------------------ - da := dx*dx + dy*dy - if da == 0 { - d = squareDistance(x1, y1, x2, y2) - } else { - d = ((x2-x1)*dx + (y2-y1)*dy) / da - if d > 0 && d < 1 { - // Simple collinear case, 1---2---3 - // We can leave just two endpoints - return - } - if d <= 0 { - d = squareDistance(x2, y2, x1, y1) - } else if d >= 1 { - d = squareDistance(x2, y2, x3, y3) - } else { - d = squareDistance(x2, y2, x1+d*dx, y1+d*dy) - } - } - if d < distanceToleranceSquare { - v.LineTo(x2, y2) - return - } - } - - // Continue subdivision - //---------------------- - recursiveQuadraticBezierBezier(v, x1, y1, x12, y12, x123, y123, level+1, distanceToleranceSquare, angleTolerance) - recursiveQuadraticBezierBezier(v, x123, y123, x23, y23, x3, y3, level+1, distanceToleranceSquare, angleTolerance) -} - -/** - * http://www.antigrain.com/research/adaptive_bezier/index.html - */ -func recursiveCubicBezier(v LineBuilder, x1, y1, x2, y2, x3, y3, x4, y4 float64, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) { - if level > CurveRecursionLimit { - return - } - - // Calculate all the mid-points of the line segments - //---------------------- - x12 := (x1 + x2) / 2 - y12 := (y1 + y2) / 2 - x23 := (x2 + x3) / 2 - y23 := (y2 + y3) / 2 - x34 := (x3 + x4) / 2 - y34 := (y3 + y4) / 2 - x123 := (x12 + x23) / 2 - y123 := (y12 + y23) / 2 - x234 := (x23 + x34) / 2 - y234 := (y23 + y34) / 2 - x1234 := (x123 + x234) / 2 - y1234 := (y123 + y234) / 2 - - // Try to approximate the full cubic curve by a single straight line - //------------------ - dx := x4 - x1 - dy := y4 - y1 - - d2 := math.Abs(((x2-x4)*dy - (y2-y4)*dx)) - d3 := math.Abs(((x3-x4)*dy - (y3-y4)*dx)) - - switch { - case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // All collinear OR p1==p4 - //---------------------- - k := dx*dx + dy*dy - if k == 0 { - d2 = squareDistance(x1, y1, x2, y2) - d3 = squareDistance(x4, y4, x3, y3) - } else { - k = 1 / k - da1 := x2 - x1 - da2 := y2 - y1 - d2 = k * (da1*dx + da2*dy) - da1 = x3 - x1 - da2 = y3 - y1 - d3 = k * (da1*dx + da2*dy) - if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 { - // Simple collinear case, 1---2---3---4 - // We can leave just two endpoints - return - } - if d2 <= 0 { - d2 = squareDistance(x2, y2, x1, y1) - } else if d2 >= 1 { - d2 = squareDistance(x2, y2, x4, y4) - } else { - d2 = squareDistance(x2, y2, x1+d2*dx, y1+d2*dy) - } - - if d3 <= 0 { - d3 = squareDistance(x3, y3, x1, y1) - } else if d3 >= 1 { - d3 = squareDistance(x3, y3, x4, y4) - } else { - d3 = squareDistance(x3, y3, x1+d3*dx, y1+d3*dy) - } - } - if d2 > d3 { - if d2 < distanceToleranceSquare { - v.LineTo(x2, y2) - return - } - } else { - if d3 < distanceToleranceSquare { - v.LineTo(x3, y3) - return - } - } - break - - case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // p1,p2,p4 are collinear, p3 is significant - //---------------------- - if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - v.LineTo(x23, y23) - return - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(y4-y3, x4-x3) - math.Atan2(y3-y2, x3-x2)) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - v.LineTo(x2, y2) - v.LineTo(x3, y3) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - v.LineTo(x3, y3) - return - } - } - } - break - - case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // p1,p3,p4 are collinear, p2 is significant - //---------------------- - if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - v.LineTo(x23, y23) - return - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1)) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - v.LineTo(x2, y2) - v.LineTo(x3, y3) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - v.LineTo(x2, y2) - return - } - } - } - break - - case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // Regular case - //----------------- - if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) { - // If the curvature doesn't exceed the distanceTolerance value - // we tend to finish subdivisions. - //---------------------- - if angleTolerance < CurveAngleToleranceEpsilon { - v.LineTo(x23, y23) - return - } - - // Angle & Cusp Condition - //---------------------- - k := math.Atan2(y3-y2, x3-x2) - da1 := math.Abs(k - math.Atan2(y2-y1, x2-x1)) - da2 := math.Abs(math.Atan2(y4-y3, x4-x3) - k) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - if da2 >= math.Pi { - da2 = 2*math.Pi - da2 - } - - if da1+da2 < angleTolerance { - // Finally we can stop the recursion - //---------------------- - v.LineTo(x23, y23) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - v.LineTo(x2, y2) - return - } - - if da2 > cuspLimit { - v.LineTo(x3, y3) - return - } - } - } - break - } - - // Continue subdivision - //---------------------- - recursiveCubicBezier(v, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, distanceToleranceSquare, angleTolerance, cuspLimit) - recursiveCubicBezier(v, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, distanceToleranceSquare, angleTolerance, cuspLimit) - -} diff --git a/image.go b/ftgc.go similarity index 85% rename from image.go rename to ftgc.go index 6b83f2b..86647cf 100644 --- a/image.go +++ b/ftgc.go @@ -275,56 +275,64 @@ func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color. gc.Current.Path.Clear() } -/**** second method ****/ func (gc *ImageGraphicContext) Stroke(paths ...*path.Path) { paths = append(paths, gc.Current.Path) gc.strokeRasterizer.UseNonZeroWinding = true - stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer))) + stroker := path.NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.strokeRasterizer))) stroker.HalfLineWidth = gc.Current.LineWidth / 2 - var pathConverter *PathConverter + + var liner path.LineBuilder if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { - dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) - pathConverter = NewPathConverter(dasher) + liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) } else { - pathConverter = NewPathConverter(stroker) + liner = stroker + } + for _, p := range paths { + p.Flatten(liner, gc.Current.Tr.GetScale()) } - pathConverter.ApproximationScale = gc.Current.Tr.GetScale() - pathConverter.Convert(paths...) gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } -/**** second method ****/ func (gc *ImageGraphicContext) Fill(paths ...*path.Path) { paths = append(paths, gc.Current.Path) gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() /**** first method ****/ - pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))) - pathConverter.ApproximationScale = gc.Current.Tr.GetScale() - pathConverter.Convert(paths...) + flattener := NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer)) + for _, p := range paths { + p.Flatten(flattener, gc.Current.Tr.GetScale()) + } gc.paint(gc.fillRasterizer, gc.Current.FillColor) } -/* second method */ func (gc *ImageGraphicContext) FillStroke(paths ...*path.Path) { + paths = append(paths, gc.Current.Path) gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() gc.strokeRasterizer.UseNonZeroWinding = true - filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)) + flattener := NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer)) - stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer))) + stroker := path.NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.strokeRasterizer))) stroker.HalfLineWidth = gc.Current.LineWidth / 2 - demux := NewLineBuilders(filler, stroker) - paths = append(paths, gc.Current.Path) - pathConverter := NewPathConverter(demux) - pathConverter.ApproximationScale = gc.Current.Tr.GetScale() - pathConverter.Convert(paths...) + var liner path.LineBuilder + if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { + liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) + } else { + liner = stroker + } + demux := path.NewLineBuilders(flattener, liner) + for _, p := range paths { + p.Flatten(demux, gc.Current.Tr.GetScale()) + } + + // Fill gc.paint(gc.fillRasterizer, gc.Current.FillColor) + // Stroke gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } @@ -337,25 +345,3 @@ func (f FillRule) UseNonZeroWinding() bool { } return false } - -func (c Cap) Convert() raster.Capper { - switch c { - case RoundCap: - return raster.RoundCapper - case ButtCap: - return raster.ButtCapper - case SquareCap: - return raster.SquareCapper - } - return raster.RoundCapper -} - -func (j Join) Convert() raster.Joiner { - switch j { - case RoundJoin: - return raster.RoundJoiner - case BevelJoin: - return raster.BevelJoiner - } - return raster.RoundJoiner -} diff --git a/gc.go b/gc.go index f510a8c..6b91f9a 100644 --- a/gc.go +++ b/gc.go @@ -30,8 +30,8 @@ type GraphicContext interface { SetFillColor(c color.Color) SetFillRule(f FillRule) SetLineWidth(lineWidth float64) - SetLineCap(cap Cap) - SetLineJoin(join Join) + SetLineCap(cap path.Cap) + SetLineJoin(join path.Join) SetLineDash(dash []float64, dashOffset float64) SetFontSize(fontSize float64) GetFontSize() float64 diff --git a/math.go b/math.go index c4bb761..91b5482 100644 --- a/math.go +++ b/math.go @@ -3,20 +3,6 @@ package draw2d -import ( - "math" -) - -func distance(x1, y1, x2, y2 float64) float64 { - dx := x2 - x1 - dy := y2 - y1 - return float64(math.Sqrt(dx*dx + dy*dy)) -} - -func vectorDistance(dx, dy float64) float64 { - return float64(math.Sqrt(dx*dx + dy*dy)) -} - func squareDistance(x1, y1, x2, y2 float64) float64 { dx := x2 - x1 dy := y2 - y1 diff --git a/dasher.go b/path/dasher.go similarity index 99% rename from dasher.go rename to path/dasher.go index a9a1ce2..8e5c115 100644 --- a/dasher.go +++ b/path/dasher.go @@ -1,7 +1,7 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 13/12/2010 by Laurent Le Goff -package draw2d +package path type DashVertexConverter struct { next LineBuilder diff --git a/line.go b/path/flattening.go similarity index 95% rename from line.go rename to path/flattening.go index 5838aad..9f18061 100644 --- a/line.go +++ b/path/flattening.go @@ -1,7 +1,7 @@ // Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff +// created: 06/12/2010 by Laurent Le Goff -package draw2d +package path // LineBuilder defines drawing line methods type LineBuilder interface { diff --git a/path_adder.go b/path/ftpath.go similarity index 60% rename from path_adder.go rename to path/ftpath.go index 0dd8c3f..d2b40d5 100644 --- a/path_adder.go +++ b/path/ftpath.go @@ -1,36 +1,36 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 13/12/2010 by Laurent Le Goff -package draw2d +package path import ( "code.google.com/p/freetype-go/freetype/raster" - "github.com/llgcode/draw2d/path" + "github.com/llgcode/draw2d/curve" ) -type VertexAdder struct { +type FtLineBuilder struct { adder raster.Adder } -func NewVertexAdder(adder raster.Adder) *VertexAdder { - return &VertexAdder{adder} +func NewFtLineBuilder(adder raster.Adder) *FtLineBuilder { + return &FtLineBuilder{adder} } -func (vertexAdder *VertexAdder) MoveTo(x, y float64) { - vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) +func (FtLineBuilder *FtLineBuilder) MoveTo(x, y float64) { + FtLineBuilder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) } -func (vertexAdder *VertexAdder) LineTo(x, y float64) { - vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) +func (FtLineBuilder *FtLineBuilder) LineTo(x, y float64) { + FtLineBuilder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) } -func (vertexAdder *VertexAdder) LineJoin() { +func (FtLineBuilder *FtLineBuilder) LineJoin() { } -func (vertexAdder *VertexAdder) Close() { +func (FtLineBuilder *FtLineBuilder) Close() { } -func (vertexAdder *VertexAdder) End() { +func (FtLineBuilder *FtLineBuilder) End() { } type PathAdder struct { @@ -43,29 +43,29 @@ func NewPathAdder(adder raster.Adder) *PathAdder { return &PathAdder{adder, raster.Point{0, 0}, 1} } -func (pathAdder *PathAdder) Convert(paths ...*path.Path) { +func (pathAdder *PathAdder) Convert(paths ...*Path) { for _, apath := range paths { j := 0 for _, cmd := range apath.Components { switch cmd { - case path.MoveToCmp: + 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 path.LineToCmp: + case LineToCmp: pathAdder.adder.Add1(raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)}) j += 2 - case path.QuadCurveToCmp: + 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 path.CubicCurveToCmp: + 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 path.ArcToCmp: - lastPoint := arcAdder(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) + 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 path.CloseCmp: + case CloseCmp: pathAdder.adder.Add1(pathAdder.firstPoint) } } diff --git a/path/interpret.go b/path/interpret.go deleted file mode 100644 index 86de6e8..0000000 --- a/path/interpret.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 06/12/2010 by Laurent Le Goff - -package path - -import ( - "github.com/llgcode/draw2d/curve" -) - -type PathConverter struct { - converter LineBuilder - ApproximationScale float64 -} - -func NewPathConverter(converter LineBuilder) *PathConverter { - return &PathConverter{converter, 1, 0, 0, 0, 0} -} - -// may not been in path instead put it in a troke package thing -func (c *PathConverter) Interpret(liner LineBuilder, scale float64, paths ...*Path) { - // First Point - var startX, startY float64 = 0, 0 - // Current Point - var x, y float64 = 0, 0 - for _, path := range paths { - 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 { - liner.End() - } - liner.MoveTo(x, y) - i += 2 - case LineToCmp: - x, y = path.Points[i], path.Points[i+1] - liner.LineTo(x, y) - liner.LineJoin() - i += 2 - case QuadCurveToCmp: - curve.TraceQuad(liner, path.Points[i-2:], 0.5) - x, y = path.Points[i+2], path.Points[i+3] - liner.LineTo(x, y) - i += 4 - case CubicCurveToCmp: - curve.TraceCubic(liner, path.Points[i-2:], 0.5) - x, y = path.Points[i+4], path.Points[i+5] - liner.LineTo(x, y) - i += 6 - case ArcToCmp: - x, y = arc(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) - i += 6 - case CloseCmp: - liner.LineTo(startX, startY) - liner.Close() - } - } - liner.End() - } -} diff --git a/path/path.go b/path/path.go index b902466..203837a 100644 --- a/path/path.go +++ b/path/path.go @@ -6,6 +6,7 @@ package path import ( "fmt" + "github.com/llgcode/draw2d/curve" "math" ) @@ -174,3 +175,47 @@ func (p *Path) String() string { } return s } + +// Flatten convert curves in straight segments keeping join segements +func (path *Path) Flatten(liner LineBuilder, 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 { + liner.End() + } + liner.MoveTo(x, y) + i += 2 + case LineToCmp: + x, y = path.Points[i], path.Points[i+1] + liner.LineTo(x, y) + liner.LineJoin() + i += 2 + case QuadCurveToCmp: + curve.TraceQuad(liner, path.Points[i-2:], 0.5) + x, y = path.Points[i+2], path.Points[i+3] + liner.LineTo(x, y) + i += 4 + case CubicCurveToCmp: + curve.TraceCubic(liner, path.Points[i-2:], 0.5) + x, y = path.Points[i+4], path.Points[i+5] + liner.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) + i += 6 + case CloseCmp: + liner.LineTo(startX, startY) + liner.Close() + } + } + liner.End() +} diff --git a/stroker.go b/path/stroker.go similarity index 81% rename from stroker.go rename to path/stroker.go index e140254..60dfac4 100644 --- a/stroker.go +++ b/path/stroker.go @@ -1,7 +1,11 @@ // Copyright 2010 The draw2d Authors. All rights reserved. // created: 13/12/2010 by Laurent Le Goff -package draw2d +package path + +import ( + "code.google.com/p/freetype-go/freetype/raster" +) type Cap int @@ -11,6 +15,18 @@ const ( SquareCap ) +func (c Cap) Convert() raster.Capper { + switch c { + case RoundCap: + return raster.RoundCapper + case ButtCap: + return raster.ButtCapper + case SquareCap: + return raster.SquareCapper + } + return raster.RoundCapper +} + type Join int const ( @@ -19,6 +35,16 @@ const ( MiterJoin ) +func (j Join) Convert() raster.Joiner { + switch j { + case RoundJoin: + return raster.RoundJoiner + case BevelJoin: + return raster.BevelJoiner + } + return raster.RoundJoiner +} + type LineStroker struct { Next LineBuilder HalfLineWidth float64 diff --git a/path/utils.go b/path/utils.go new file mode 100644 index 0000000..68dbc35 --- /dev/null +++ b/path/utils.go @@ -0,0 +1,13 @@ +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/stack_gc.go b/stack_gc.go index 3d83154..ea34e79 100644 --- a/stack_gc.go +++ b/stack_gc.go @@ -24,8 +24,8 @@ type ContextStack struct { StrokeColor color.Color FillColor color.Color FillRule FillRule - Cap Cap - Join Join + Cap path.Cap + Join path.Join FontSize float64 FontData FontData @@ -44,13 +44,13 @@ func NewStackGraphicContext() *StackGraphicContext { gc := &StackGraphicContext{} gc.Current = new(ContextStack) gc.Current.Tr = NewIdentityMatrix() - gc.Current.Path = NewPathStorage() + gc.Current.Path = new(path.Path) gc.Current.LineWidth = 1.0 gc.Current.StrokeColor = image.Black gc.Current.FillColor = image.White - gc.Current.Cap = RoundCap + gc.Current.Cap = path.RoundCap gc.Current.FillRule = FillRuleEvenOdd - gc.Current.Join = RoundJoin + gc.Current.Join = path.RoundJoin gc.Current.FontSize = 10 gc.Current.FontData = defaultFontData return gc @@ -96,12 +96,12 @@ func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) { gc.Current.LineWidth = LineWidth } -func (gc *StackGraphicContext) SetLineCap(Cap Cap) { - gc.Current.Cap = Cap +func (gc *StackGraphicContext) SetLineCap(cap path.Cap) { + gc.Current.Cap = cap } -func (gc *StackGraphicContext) SetLineJoin(Join Join) { - gc.Current.Join = Join +func (gc *StackGraphicContext) SetLineJoin(join path.Join) { + gc.Current.Join = join } func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) { @@ -141,42 +141,22 @@ func (gc *StackGraphicContext) MoveTo(x, y float64) { gc.Current.Path.MoveTo(x, y) } -func (gc *StackGraphicContext) RMoveTo(dx, dy float64) { - gc.Current.Path.RMoveTo(dx, dy) -} - func (gc *StackGraphicContext) LineTo(x, y float64) { gc.Current.Path.LineTo(x, y) } -func (gc *StackGraphicContext) RLineTo(dx, dy float64) { - gc.Current.Path.RLineTo(dx, dy) -} - func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) { gc.Current.Path.QuadCurveTo(cx, cy, x, y) } -func (gc *StackGraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float64) { - gc.Current.Path.RQuadCurveTo(dcx, dcy, dx, dy) -} - func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) { gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y) } -func (gc *StackGraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) { - gc.Current.Path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy) -} - func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) { gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle) } -func (gc *StackGraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) { - gc.Current.Path.RArcTo(dcx, dcy, rx, ry, startAngle, angle) -} - func (gc *StackGraphicContext) Close() { gc.Current.Path.Close() } diff --git a/transform.go b/transform.go index f2c2927..00d177f 100644 --- a/transform.go +++ b/transform.go @@ -7,6 +7,7 @@ import ( "math" "code.google.com/p/freetype-go/freetype/raster" + "github.com/llgcode/draw2d/path" ) type MatrixTransform [6]float64 @@ -254,10 +255,10 @@ func fequals(float1, float2 float64) bool { // this VertexConverter apply the Matrix transformation tr type VertexMatrixTransform struct { tr MatrixTransform - Next LineBuilder + Next path.LineBuilder } -func NewVertexMatrixTransform(tr MatrixTransform, converter LineBuilder) *VertexMatrixTransform { +func NewVertexMatrixTransform(tr MatrixTransform, converter path.LineBuilder) *VertexMatrixTransform { return &VertexMatrixTransform{tr, converter} }