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

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()
}
/**** 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
}

4
gc.go
View File

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

14
math.go
View File

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

View File

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

View File

@ -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 {

View File

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

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

View File

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

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

View File

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