remove path package and create draw2dimg package

This commit is contained in:
Laurent Le Goff 2015-04-29 14:33:32 +02:00
parent 24d62b9aa7
commit 966a9b73f7
24 changed files with 506 additions and 598 deletions

161
curve.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

29
draw2dbase/ftpath.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package draw2d
package draw2dimg
import (
"bufio"

View File

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

View File

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

View File

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

59
flattener.go Normal file
View File

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

13
gc.go
View File

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

11
math.go
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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