move flattening code in draw2dbase

This commit is contained in:
Laurent Le Goff 2015-04-29 17:16:15 +02:00
parent ee83fedb10
commit 82ef300f1d
13 changed files with 472 additions and 179 deletions

161
draw2dbase/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 draw2dbase
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
}

136
draw2dbase/curve_test.go Normal file
View File

@ -0,0 +1,136 @@
package draw2dbase
import (
"bufio"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"log"
"os"
"testing"
"github.com/llgcode/draw2d/raster"
)
var (
flattening_threshold float64 = 0.5
testsCubicFloat64 = []float64{
100, 100, 200, 100, 100, 200, 200, 200,
100, 100, 300, 200, 200, 200, 300, 100,
100, 100, 0, 300, 200, 0, 300, 300,
150, 290, 10, 10, 290, 10, 150, 290,
10, 290, 10, 10, 290, 10, 290, 290,
100, 290, 290, 10, 10, 10, 200, 290,
}
testsQuadFloat64 = []float64{
100, 100, 200, 100, 200, 200,
100, 100, 290, 200, 290, 100,
100, 100, 0, 290, 200, 290,
150, 290, 10, 10, 290, 290,
10, 290, 10, 10, 290, 290,
100, 290, 290, 10, 120, 290,
}
)
func init() {
os.Mkdir("test_results", 0666)
f, err := os.Create("test_results/_test.html")
if err != nil {
log.Println(err)
os.Exit(1)
}
defer f.Close()
log.Printf("Create html viewer")
f.Write([]byte("<html><body>"))
for i := 0; i < len(testsCubicFloat64)/8; i++ {
f.Write([]byte(fmt.Sprintf("<div><img src='_test%d.png'/></div>\n", i)))
}
for i := 0; i < len(testsQuadFloat64); i++ {
f.Write([]byte(fmt.Sprintf("<div><img src='_testQuad%d.png'/>\n</div>\n", i)))
}
f.Write([]byte("</body></html>"))
}
func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image {
for i := 0; i < len(s); i += 2 {
x, y := int(s[i]+0.5), int(s[i+1]+0.5)
img.Set(x, y, c)
img.Set(x, y+1, c)
img.Set(x, y-1, c)
img.Set(x+1, y, c)
img.Set(x+1, y+1, c)
img.Set(x+1, y-1, c)
img.Set(x-1, y, c)
img.Set(x-1, y+1, c)
img.Set(x-1, y-1, c)
}
return img
}
func TestCubicCurve(t *testing.T) {
for i := 0; i < len(testsCubicFloat64); i += 8 {
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...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
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))
}
fmt.Println()
}
func TestQuadCurve(t *testing.T) {
for i := 0; i < len(testsQuadFloat64); i += 6 {
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...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
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))
}
fmt.Println()
}
func BenchmarkCubicCurve(b *testing.B) {
for i := 0; i < b.N; i++ {
for i := 0; i < len(testsCubicFloat64); i += 8 {
var p SegmentedPath
p.MoveTo(testsCubicFloat64[i], testsCubicFloat64[i+1])
TraceCubic(&p, testsCubicFloat64[i:], flattening_threshold)
}
}
}
// SaveToPngFile create and save an image to a file using PNG format
func SaveToPngFile(filePath string, m image.Image) error {
// Create the file
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
// Create Writer from file
b := bufio.NewWriter(f)
// Write the image into the buffer
err = png.Encode(b, m)
if err != nil {
return err
}
err = b.Flush()
if err != nil {
return err
}
return nil
}

View File

@ -3,24 +3,20 @@
package draw2dbase
import (
"github.com/llgcode/draw2d"
)
type DashVertexConverter struct {
next draw2d.Flattener
next Flattener
x, y, distance float64
dash []float64
currentDash int
dashOffset float64
}
func NewDashConverter(dash []float64, dashOffset float64, converter draw2d.Flattener) *DashVertexConverter {
func NewDashConverter(dash []float64, dashOffset float64, flattener Flattener) *DashVertexConverter {
var dasher DashVertexConverter
dasher.dash = dash
dasher.currentDash = 0
dasher.dashOffset = dashOffset
dasher.next = converter
dasher.next = flattener
return &dasher
}

View File

@ -1,11 +1,7 @@
package draw2dbase
import (
"github.com/llgcode/draw2d"
)
type DemuxFlattener struct {
Flatteners []draw2d.Flattener
Flatteners []Flattener
}
func (dc DemuxFlattener) MoveTo(x, y float64) {

121
draw2dbase/flattener.go Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 06/12/2010 by Laurent Le Goff
package draw2dbase
import (
"github.com/llgcode/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()
}
// Flatten convert curves into straight segments keeping join segments info
func Flatten(path *draw2d.Path, flattener Flattener, scale float64) {
// First Point
var startX, startY float64 = 0, 0
// Current Point
var x, y float64 = 0, 0
i := 0
for _, cmp := range path.Components {
switch cmp {
case draw2d.MoveToCmp:
x, y = path.Points[i], path.Points[i+1]
startX, startY = x, y
if i != 0 {
flattener.End()
}
flattener.MoveTo(x, y)
i += 2
case draw2d.LineToCmp:
x, y = path.Points[i], path.Points[i+1]
flattener.LineTo(x, y)
flattener.LineJoin()
i += 2
case draw2d.QuadCurveToCmp:
TraceQuad(flattener, path.Points[i-2:], 0.5)
x, y = path.Points[i+2], path.Points[i+3]
flattener.LineTo(x, y)
i += 4
case draw2d.CubicCurveToCmp:
TraceCubic(flattener, path.Points[i-2:], 0.5)
x, y = path.Points[i+4], path.Points[i+5]
flattener.LineTo(x, y)
i += 6
case draw2d.ArcToCmp:
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 draw2d.CloseCmp:
flattener.LineTo(startX, startY)
flattener.Close()
}
}
flattener.End()
}
// Transformer apply the Matrix transformation tr
type Transformer struct {
Tr draw2d.MatrixTransform
Flattener Flattener
}
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 (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 (t Transformer) LineJoin() {
t.Flattener.LineJoin()
}
func (t Transformer) Close() {
t.Flattener.Close()
}
func (t Transformer) End() {
t.Flattener.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
}

View File

@ -32,7 +32,7 @@ func toFtJoin(j draw2d.LineJoin) raster.Joiner {
}
type LineStroker struct {
Next draw2d.Flattener
Flattener Flattener
HalfLineWidth float64
Cap draw2d.LineCap
Join draw2d.LineJoin
@ -41,9 +41,9 @@ type LineStroker struct {
x, y, nx, ny float64
}
func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener draw2d.Flattener) *LineStroker {
func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener Flattener) *LineStroker {
l := new(LineStroker)
l.Next = flattener
l.Flattener = flattener
l.HalfLineWidth = 0.5
l.Cap = c
l.Join = j
@ -82,18 +82,18 @@ func (l *LineStroker) Close() {
func (l *LineStroker) End() {
if len(l.vertices) > 1 {
l.Next.MoveTo(l.vertices[0], l.vertices[1])
l.Flattener.MoveTo(l.vertices[0], l.vertices[1])
for i, j := 2, 3; j < len(l.vertices); i, j = i+2, j+2 {
l.Next.LineTo(l.vertices[i], l.vertices[j])
l.Flattener.LineTo(l.vertices[i], l.vertices[j])
}
}
for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 {
l.Next.LineTo(l.rewind[i], l.rewind[j])
l.Flattener.LineTo(l.rewind[i], l.rewind[j])
}
if len(l.vertices) > 1 {
l.Next.LineTo(l.vertices[0], l.vertices[1])
l.Flattener.LineTo(l.vertices[0], l.vertices[1])
}
l.Next.End()
l.Flattener.End()
// reinit vertices
l.vertices = l.vertices[0:0]
l.rewind = l.rewind[0:0]

View File

@ -188,17 +188,17 @@ func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
var liner draw2d.Flattener
var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
for _, p := range paths {
p.Flatten(liner, gc.Current.Tr.GetScale())
draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
}
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
@ -209,9 +209,9 @@ func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
/**** first method ****/
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
for _, p := range paths {
p.Flatten(flattener, gc.Current.Tr.GetScale())
draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
}
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
@ -222,21 +222,21 @@ func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
gc.strokeRasterizer.UseNonZeroWinding = true
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
var liner draw2d.Flattener
var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}}
demux := draw2dbase.DemuxFlattener{[]draw2dbase.Flattener{flattener, liner}}
for _, p := range paths {
p.Flatten(demux, gc.Current.Tr.GetScale())
draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
}
// Fill

View File

@ -276,17 +276,17 @@ func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
var liner draw2d.Flattener
var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
for _, p := range paths {
p.Flatten(liner, gc.Current.Tr.GetScale())
draw2dbase.Flatten(p, liner, gc.Current.Tr.GetScale())
}
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
@ -297,9 +297,9 @@ func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
/**** first method ****/
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
for _, p := range paths {
p.Flatten(flattener, gc.Current.Tr.GetScale())
draw2dbase.Flatten(p, flattener, gc.Current.Tr.GetScale())
}
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
@ -310,21 +310,21 @@ func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
gc.fillRasterizer.UseNonZeroWinding = useNonZeroWinding(gc.Current.FillRule)
gc.strokeRasterizer.UseNonZeroWinding = true
flattener := draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
flattener := draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.fillRasterizer}}
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker := draw2dbase.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2dbase.Transformer{gc.Current.Tr, draw2dbase.FtLineBuilder{gc.strokeRasterizer}})
stroker.HalfLineWidth = gc.Current.LineWidth / 2
var liner draw2d.Flattener
var liner draw2dbase.Flattener
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
liner = draw2dbase.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
} else {
liner = stroker
}
demux := draw2dbase.DemuxFlattener{[]draw2d.Flattener{flattener, liner}}
demux := draw2dbase.DemuxFlattener{[]draw2dbase.Flattener{flattener, liner}}
for _, p := range paths {
p.Flatten(demux, gc.Current.Tr.GetScale())
draw2dbase.Flatten(p, demux, gc.Current.Tr.GetScale())
}
// Fill

View File

@ -7,7 +7,7 @@ import (
"math"
)
// Rectangle draw a rectangle using a PathBuilder
// Rectangle draws a rectangle using a PathBuilder
func Rectangle(path PathBuilder, x1, y1, x2, y2 float64) {
path.MoveTo(x1, y1)
path.LineTo(x2, y1)
@ -16,7 +16,7 @@ func Rectangle(path PathBuilder, x1, y1, x2, y2 float64) {
path.Close()
}
// RoundedRectangle draw a rounded rectangle using a PathBuilder
// RoundedRectangle draws a rounded rectangle using a PathBuilder
func RoundedRectangle(path PathBuilder, x1, y1, x2, y2, arcWidth, arcHeight float64) {
arcWidth = arcWidth / 2
arcHeight = arcHeight / 2
@ -31,13 +31,13 @@ func RoundedRectangle(path PathBuilder, x1, y1, x2, y2, arcWidth, arcHeight floa
path.Close()
}
// Ellipse draw an ellipse using a PathBuilder
// Ellipse draws an ellipse using a PathBuilder
func Ellipse(path PathBuilder, cx, cy, rx, ry float64) {
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2)
path.Close()
}
// Circle draw an circle using a PathBuilder
// Circle draws a circle using a PathBuilder
func Circle(path PathBuilder, cx, cy, radius float64) {
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2)
path.Close()

View File

@ -1,59 +0,0 @@
// 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
)

16
gc.go
View File

@ -15,6 +15,22 @@ const (
FillRuleWinding
)
type LineCap int
const (
RoundCap LineCap = iota
ButtCap
SquareCap
)
type LineJoin int
const (
BevelJoin LineJoin = iota
RoundJoin
MiterJoin
)
type GraphicContext interface {
PathBuilder
// Create a new path

48
path.go
View File

@ -9,7 +9,7 @@ import (
"math"
)
// PathBuilder define method that create path
// PathBuilder defines methods that creates path
type PathBuilder interface {
// LastPoint returns the current point of the current path
LastPoint() (x, y float64)
@ -33,7 +33,7 @@ type PathBuilder interface {
Close()
}
// PathCmp represent components of a path
// PathCmp represents component of a path
type PathCmp int
const (
@ -174,47 +174,3 @@ func (p *Path) String() string {
}
return s
}
// Flatten convert curves into straight segments keeping join segments info
func (path *Path) Flatten(flattener Flattener, 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 {
flattener.End()
}
flattener.MoveTo(x, y)
i += 2
case LineToCmp:
x, y = path.Points[i], path.Points[i+1]
flattener.LineTo(x, y)
flattener.LineJoin()
i += 2
case QuadCurveToCmp:
TraceQuad(flattener, path.Points[i-2:], 0.5)
x, y = path.Points[i+2], path.Points[i+3]
flattener.LineTo(x, y)
i += 4
case CubicCurveToCmp:
TraceCubic(flattener, path.Points[i-2:], 0.5)
x, y = path.Points[i+4], path.Points[i+5]
flattener.LineTo(x, y)
i += 6
case ArcToCmp:
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:
flattener.LineTo(startX, startY)
flattener.Close()
}
}
flattener.End()
}

View File

@ -246,33 +246,3 @@ func (tr MatrixTransform) IsTranslation() bool {
func fequals(float1, float2 float64) bool {
return math.Abs(float1-float2) <= epsilon
}
// Transformer apply the Matrix transformation tr
type Transformer struct {
Tr MatrixTransform
Flattener Flattener
}
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 (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 (t Transformer) LineJoin() {
t.Flattener.LineJoin()
}
func (t Transformer) Close() {
t.Flattener.Close()
}
func (t Transformer) End() {
t.Flattener.End()
}