refactoring mv path things in path package

This commit is contained in:
Laurent Le Goff 2015-04-29 10:28:05 +02:00
parent 1d191b3eaf
commit 0b7a049f3e
15 changed files with 185 additions and 583 deletions

68
arc.go
View File

@ -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)}
}

View File

@ -5,10 +5,12 @@ package curve
import ( import (
"math" "math"
"code.google.com/p/freetype-go/freetype/raster"
) )
// TraceArc trace an arc using a LineBuilder // 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 end := start + angle
clockWise := true clockWise := true
if angle < 0 { 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 { if (angle < end-da/4) != clockWise {
curX = x + math.Cos(end)*rx curX = x + math.Cos(end)*rx
curY = y + math.Sin(end)*ry curY = y + math.Sin(end)*ry
break return curX, curY
} }
curX = x + math.Cos(angle)*rx curX = x + math.Cos(angle)*rx
curY = y + math.Sin(angle)*ry curY = y + math.Sin(angle)*ry
@ -34,5 +36,35 @@ func TraceArc(t LineBuilder, x, y, rx, ry, start, angle, scale float64) {
angle += da angle += da
t.LineTo(curX, curY) 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)}
} }

336
curves.go
View File

@ -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)
}

View File

@ -275,56 +275,64 @@ func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.
gc.Current.Path.Clear() gc.Current.Path.Clear()
} }
/**** second method ****/
func (gc *ImageGraphicContext) Stroke(paths ...*path.Path) { func (gc *ImageGraphicContext) Stroke(paths ...*path.Path) {
paths = append(paths, gc.Current.Path) paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true 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 stroker.HalfLineWidth = gc.Current.LineWidth / 2
var pathConverter *PathConverter
var liner path.LineBuilder
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
pathConverter = NewPathConverter(dasher)
} else { } 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) gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
} }
/**** second method ****/
func (gc *ImageGraphicContext) Fill(paths ...*path.Path) { func (gc *ImageGraphicContext) Fill(paths ...*path.Path) {
paths = append(paths, gc.Current.Path) paths = append(paths, gc.Current.Path)
gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
/**** first method ****/ /**** first method ****/
pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))) flattener := NewVertexMatrixTransform(gc.Current.Tr, path.NewFtLineBuilder(gc.fillRasterizer))
pathConverter.ApproximationScale = gc.Current.Tr.GetScale() for _, p := range paths {
pathConverter.Convert(paths...) p.Flatten(flattener, gc.Current.Tr.GetScale())
}
gc.paint(gc.fillRasterizer, gc.Current.FillColor) gc.paint(gc.fillRasterizer, gc.Current.FillColor)
} }
/* second method */
func (gc *ImageGraphicContext) FillStroke(paths ...*path.Path) { func (gc *ImageGraphicContext) FillStroke(paths ...*path.Path) {
paths = append(paths, gc.Current.Path)
gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
gc.strokeRasterizer.UseNonZeroWinding = true 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 stroker.HalfLineWidth = gc.Current.LineWidth / 2
demux := NewLineBuilders(filler, stroker) var liner path.LineBuilder
paths = append(paths, gc.Current.Path) if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
pathConverter := NewPathConverter(demux) liner = path.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
pathConverter.ApproximationScale = gc.Current.Tr.GetScale() } else {
pathConverter.Convert(paths...) 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) gc.paint(gc.fillRasterizer, gc.Current.FillColor)
// Stroke
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
} }
@ -337,25 +345,3 @@ func (f FillRule) UseNonZeroWinding() bool {
} }
return false 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
}

4
gc.go
View File

@ -30,8 +30,8 @@ type GraphicContext interface {
SetFillColor(c color.Color) SetFillColor(c color.Color)
SetFillRule(f FillRule) SetFillRule(f FillRule)
SetLineWidth(lineWidth float64) SetLineWidth(lineWidth float64)
SetLineCap(cap Cap) SetLineCap(cap path.Cap)
SetLineJoin(join Join) SetLineJoin(join path.Join)
SetLineDash(dash []float64, dashOffset float64) SetLineDash(dash []float64, dashOffset float64)
SetFontSize(fontSize float64) SetFontSize(fontSize float64)
GetFontSize() float64 GetFontSize() float64

14
math.go
View File

@ -3,20 +3,6 @@
package draw2d 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 { func squareDistance(x1, y1, x2, y2 float64) float64 {
dx := x2 - x1 dx := x2 - x1
dy := y2 - y1 dy := y2 - y1

View File

@ -1,7 +1,7 @@
// Copyright 2010 The draw2d Authors. All rights reserved. // Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff // created: 13/12/2010 by Laurent Le Goff
package draw2d package path
type DashVertexConverter struct { type DashVertexConverter struct {
next LineBuilder next LineBuilder

View File

@ -1,7 +1,7 @@
// Copyright 2010 The draw2d Authors. All rights reserved. // 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 // LineBuilder defines drawing line methods
type LineBuilder interface { type LineBuilder interface {

View File

@ -1,36 +1,36 @@
// Copyright 2010 The draw2d Authors. All rights reserved. // Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff // created: 13/12/2010 by Laurent Le Goff
package draw2d package path
import ( import (
"code.google.com/p/freetype-go/freetype/raster" "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 adder raster.Adder
} }
func NewVertexAdder(adder raster.Adder) *VertexAdder { func NewFtLineBuilder(adder raster.Adder) *FtLineBuilder {
return &VertexAdder{adder} return &FtLineBuilder{adder}
} }
func (vertexAdder *VertexAdder) MoveTo(x, y float64) { func (FtLineBuilder *FtLineBuilder) MoveTo(x, y float64) {
vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) FtLineBuilder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)})
} }
func (vertexAdder *VertexAdder) LineTo(x, y float64) { func (FtLineBuilder *FtLineBuilder) LineTo(x, y float64) {
vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) 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 { type PathAdder struct {
@ -43,29 +43,29 @@ func NewPathAdder(adder raster.Adder) *PathAdder {
return &PathAdder{adder, raster.Point{0, 0}, 1} 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 { for _, apath := range paths {
j := 0 j := 0
for _, cmd := range apath.Components { for _, cmd := range apath.Components {
switch cmd { 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.firstPoint = raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)}
pathAdder.adder.Start(pathAdder.firstPoint) pathAdder.adder.Start(pathAdder.firstPoint)
j += 2 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)}) pathAdder.adder.Add1(raster.Point{raster.Fix32(apath.Points[j] * 256), raster.Fix32(apath.Points[j+1] * 256)})
j += 2 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)}) 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 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)}) 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 j += 6
case path.ArcToCmp: case 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) 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) pathAdder.adder.Add1(lastPoint)
j += 6 j += 6
case path.CloseCmp: case CloseCmp:
pathAdder.adder.Add1(pathAdder.firstPoint) pathAdder.adder.Add1(pathAdder.firstPoint)
} }
} }

View File

@ -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()
}
}

View File

@ -6,6 +6,7 @@ package path
import ( import (
"fmt" "fmt"
"github.com/llgcode/draw2d/curve"
"math" "math"
) )
@ -174,3 +175,47 @@ func (p *Path) String() string {
} }
return s 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()
}

View File

@ -1,7 +1,11 @@
// Copyright 2010 The draw2d Authors. All rights reserved. // Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff // created: 13/12/2010 by Laurent Le Goff
package draw2d package path
import (
"code.google.com/p/freetype-go/freetype/raster"
)
type Cap int type Cap int
@ -11,6 +15,18 @@ const (
SquareCap 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 type Join int
const ( const (
@ -19,6 +35,16 @@ const (
MiterJoin 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 { type LineStroker struct {
Next LineBuilder Next LineBuilder
HalfLineWidth float64 HalfLineWidth float64

13
path/utils.go Normal file
View File

@ -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))
}

View File

@ -24,8 +24,8 @@ type ContextStack struct {
StrokeColor color.Color StrokeColor color.Color
FillColor color.Color FillColor color.Color
FillRule FillRule FillRule FillRule
Cap Cap Cap path.Cap
Join Join Join path.Join
FontSize float64 FontSize float64
FontData FontData FontData FontData
@ -44,13 +44,13 @@ func NewStackGraphicContext() *StackGraphicContext {
gc := &StackGraphicContext{} gc := &StackGraphicContext{}
gc.Current = new(ContextStack) gc.Current = new(ContextStack)
gc.Current.Tr = NewIdentityMatrix() gc.Current.Tr = NewIdentityMatrix()
gc.Current.Path = NewPathStorage() gc.Current.Path = new(path.Path)
gc.Current.LineWidth = 1.0 gc.Current.LineWidth = 1.0
gc.Current.StrokeColor = image.Black gc.Current.StrokeColor = image.Black
gc.Current.FillColor = image.White gc.Current.FillColor = image.White
gc.Current.Cap = RoundCap gc.Current.Cap = path.RoundCap
gc.Current.FillRule = FillRuleEvenOdd gc.Current.FillRule = FillRuleEvenOdd
gc.Current.Join = RoundJoin gc.Current.Join = path.RoundJoin
gc.Current.FontSize = 10 gc.Current.FontSize = 10
gc.Current.FontData = defaultFontData gc.Current.FontData = defaultFontData
return gc return gc
@ -96,12 +96,12 @@ func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) {
gc.Current.LineWidth = LineWidth gc.Current.LineWidth = LineWidth
} }
func (gc *StackGraphicContext) SetLineCap(Cap Cap) { func (gc *StackGraphicContext) SetLineCap(cap path.Cap) {
gc.Current.Cap = Cap gc.Current.Cap = cap
} }
func (gc *StackGraphicContext) SetLineJoin(Join Join) { func (gc *StackGraphicContext) SetLineJoin(join path.Join) {
gc.Current.Join = Join gc.Current.Join = join
} }
func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) { 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) 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) { func (gc *StackGraphicContext) LineTo(x, y float64) {
gc.Current.Path.LineTo(x, y) 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) { func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) {
gc.Current.Path.QuadCurveTo(cx, cy, x, y) 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) { func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y) 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) { func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle) 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() { func (gc *StackGraphicContext) Close() {
gc.Current.Path.Close() gc.Current.Path.Close()
} }

View File

@ -7,6 +7,7 @@ import (
"math" "math"
"code.google.com/p/freetype-go/freetype/raster" "code.google.com/p/freetype-go/freetype/raster"
"github.com/llgcode/draw2d/path"
) )
type MatrixTransform [6]float64 type MatrixTransform [6]float64
@ -254,10 +255,10 @@ func fequals(float1, float2 float64) bool {
// this VertexConverter apply the Matrix transformation tr // this VertexConverter apply the Matrix transformation tr
type VertexMatrixTransform struct { type VertexMatrixTransform struct {
tr MatrixTransform 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} return &VertexMatrixTransform{tr, converter}
} }