add advanced_path function

- Circle
 - Rect
 - RoundRect
 - Ellipse
This commit is contained in:
legoff.laurent 2010-12-06 12:11:57 +00:00
parent bdf86580b2
commit 184115e3e1
12 changed files with 302 additions and 324 deletions

View File

@ -11,8 +11,8 @@ import (
"image"
"image/png"
//"draw2d"
"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
"draw2d"
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
)

View File

@ -6,8 +6,8 @@ import (
"exp/draw/x11"
"image"
"math"
//"draw2d"
"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
"draw2d"
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
)
func main() {

View File

@ -11,8 +11,8 @@ import (
"image"
"image/png"
//"draw2d"
"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
"draw2d"
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
)
const (
@ -75,52 +75,52 @@ func gordon(gc *draw2d.GraphicContext, x, y, w, h float) {
gc.Close()
gc.SetFillColor(brb)
gc.Fill()
gc.RoundRect(x, y+h, x+ w, y+h+h, 10, 10)
draw2d.RoundRect(gc, x, y+h, x+ w, y+h+h, 10, 10)
gc.Fill()
gc.Circle(x, y+h, w/12) // left ear
draw2d.Circle(gc,x, y+h, w/12) // left ear
gc.SetFillColor(brf)
gc.Fill()
gc.Circle(x, y+h, w/12-10)
draw2d.Circle(gc, x, y+h, w/12-10)
gc.SetFillColor(nf)
gc.Fill()
gc.Circle(x+w, y+h, w/12) // right ear
draw2d.Circle(gc, x+w, y+h, w/12) // right ear
gc.SetFillColor(brf)
gc.Fill()
gc.Circle(x+w, y+h, w/12-10)
draw2d.Circle(gc, x+w, y+h, w/12-10)
gc.SetFillColor(nf)
gc.Fill()
gc.Circle(x+w/3, y+h23, w/9) // left eye
draw2d.Circle(gc, x+w/3, y+h23, w/9) // left eye
gc.SetFillColor(wf)
gc.Fill()
gc.Circle(x+w/3+10, y+h23, w / 10 - 10)
draw2d.Circle(gc, x+w/3+10, y+h23, w / 10 - 10)
gc.SetFillColor(blf)
gc.Fill()
gc.Circle(x+w/3+15, y+h23, 5)
draw2d.Circle(gc, x+w/3+15, y+h23, 5)
gc.SetFillColor(wf)
gc.Fill()
gc.Circle(x+w-w/3, y+h23, w/9) // right eye
draw2d.Circle(gc, x+w-w/3, y+h23, w/9) // right eye
gc.Fill()
gc.Circle(x+w-w/3+10, y+h23, w / 10 - 10)
draw2d.Circle(gc, x+w-w/3+10, y+h23, w / 10 - 10)
gc.SetFillColor(blf)
gc.Fill()
gc.Circle(x+w-(w/3)+15, y+h23, 5)
draw2d.Circle(gc, x+w-(w/3)+15, y+h23, 5)
gc.SetFillColor(wf)
gc.Fill()
gc.SetFillColor(wf)
gc.RoundRect(x+w/2-w/8, y+h+30, x+w/2-w/8 + w/8, y+h+30 + w/6, 5, 5) // left tooth
draw2d.RoundRect(gc, x+w/2-w/8, y+h+30, x+w/2-w/8 + w/8, y+h+30 + w/6, 5, 5) // left tooth
gc.Fill()
gc.RoundRect(x+w/2, y+h+30, x+w/2+w/8, y+h+30+w/6, 5, 5) // right tooth
draw2d.RoundRect(gc, x+w/2, y+h+30, x+w/2+w/8, y+h+30+w/6, 5, 5) // right tooth
gc.Fill()
gc.Ellipse(x+(w/2), y+h+30, w/6, w/12) // snout
draw2d.Ellipse(gc, x+(w/2), y+h+30, w/6, w/12) // snout
gc.SetFillColor(nf)
gc.Fill()
gc.Ellipse(x+(w/2), y+h+10, w/10, w/12) // nose
draw2d.Ellipse(gc, x+(w/2), y+h+10, w/10, w/12) // nose
gc.SetFillColor(blf)
gc.Fill()

View File

@ -11,8 +11,8 @@ import (
"math"
"image"
"image/png"
//"draw2d"
"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
"draw2d"
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
)
const (
@ -71,21 +71,21 @@ func android(gc *draw2d.GraphicContext, x, y float) {
gc.MoveTo(x+100, y+25)
gc.LineTo( x+110, y+10)
gc.Stroke()
gc.Circle(x+60, y+45, 5) // left eye
draw2d.Circle(gc, x+60, y+45, 5) // left eye
gc.FillStroke()
gc.Circle(x+100, y+45, 5) // right eye
draw2d.Circle(gc, x+100, y+45, 5) // right eye
gc.FillStroke()
gc.RoundRect(x+30, y+75, x+30+100, y+75+90, 10, 10) // body
draw2d.RoundRect(gc, x+30, y+75, x+30+100, y+75+90, 10, 10) // body
gc.FillStroke()
gc.Rect(x+30, y+75, x+30+100, y+75+80)
draw2d.Rect(gc, x+30, y+75, x+30+100, y+75+80)
gc.FillStroke()
gc.RoundRect(x+5, y+80, x+5+20, y+80+70, 10, 10) // left arm
draw2d.RoundRect(gc, x+5, y+80, x+5+20, y+80+70, 10, 10) // left arm
gc.FillStroke()
gc.RoundRect(x+135, y+80, x+135+20, y+80+70, 10, 10) // right arm
draw2d.RoundRect(gc, x+135, y+80, x+135+20, y+80+70, 10, 10) // right arm
gc.FillStroke()
gc.RoundRect(x+50, y+150, x+50+20, y+150+50, 10, 10) // left leg
draw2d.RoundRect(gc, x+50, y+150, x+50+20, y+150+50, 10, 10) // left leg
gc.FillStroke()
gc.RoundRect(x+90, y+150, x+90+20, y+150+50, 10, 10) // right leg
draw2d.RoundRect(gc, x+90, y+150, x+90+20, y+150+50, 10, 10) // right leg
gc.FillStroke()
}

View File

@ -13,8 +13,8 @@ import (
"math"
"image"
"image/png"
//"draw2d"
"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
"draw2d"
//"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
)
const (
@ -283,11 +283,11 @@ func TestFillStyle() {
i, gc := initGc(w, h)
gc.SetLineWidth(6)
gc.Rect(12, 12, 244, 70)
draw2d.Rect(gc, 12, 12, 244, 70)
wheel1 := new(draw2d.Path)
wheel1 := new(draw2d.PathStorage)
wheel1.ArcTo(64, 64, 40, 40, 0, 2*math.Pi)
wheel2 := new(draw2d.Path)
wheel2 := new(draw2d.PathStorage)
wheel2.ArcTo(192, 64, 40, 40, 0, 2*math.Pi)
gc.SetFillRule(draw2d.FillRuleEvenOdd)
@ -296,10 +296,10 @@ func TestFillStyle() {
gc.SetStrokeColor(image.Black)
gc.FillStroke(wheel1, wheel2)
gc.Rect(12, 140, 244, 198)
wheel1 = new(draw2d.Path)
draw2d.Rect(gc, 12, 140, 244, 198)
wheel1 = new(draw2d.PathStorage)
wheel1.ArcTo(64, 192, 40, 40, 0, 2*math.Pi)
wheel2 = new(draw2d.Path)
wheel2 = new(draw2d.PathStorage)
wheel2.ArcTo(192, 192, 40, 40, 0, -2*math.Pi)
gc.SetFillRule(draw2d.FillRuleWinding)

View File

@ -1,21 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package main
import (
//"draw2d"
"draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d"
"fmt"
)
func main() {
path := new(draw2d.Path)
path.MoveTo(2.0, 3.0)
path.LineTo(2.0, 3.0)
path.QuadCurveTo(2.0, 3.0, 10, 20)
path.CubicCurveTo(2.0, 3.0, 10, 20, 13, 23)
path.Rect(2.0, 3.0, 100, 200)
path.ArcTo(2.0, 3.0, 100, 200, 200, 300)
fmt.Printf("%v\n", path)
}

View File

@ -0,0 +1,39 @@
package draw2d
import(
"math"
)
//high level path creation
func Rect(path Path, x1, y1, x2, y2 float) {
path.MoveTo(x1, y1)
path.LineTo(x2, y1)
path.LineTo(x2, y2)
path.LineTo(x1, y2)
path.Close()
}
func RoundRect(path Path, x1, y1, x2, y2, arcWidth, arcHeight float) {
arcWidth = arcWidth/2;
arcHeight = arcHeight/2;
path.MoveTo(x1, y1+ arcHeight);
path.QuadCurveTo(x1, y1, x1 + arcWidth, y1);
path.LineTo(x2-arcWidth, y1);
path.QuadCurveTo(x2, y1, x2, y1 + arcHeight);
path.LineTo(x2, y2-arcHeight);
path.QuadCurveTo(x2, y2, x2 - arcWidth, y2);
path.LineTo(x1 + arcWidth, y2);
path.QuadCurveTo(x1, y2, x1, y2 - arcHeight);
path.Close()
}
func Ellipse(path Path, cx, cy, rx, ry float) {
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi * 2)
path.Close()
}
func Circle(path Path, cx, cy, radius float) {
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi * 2)
path.Close()
}

View File

@ -12,7 +12,7 @@ type DashAdder struct {
dashOffset float
}
func traceDashPath(dash []float, dashOffset float, approximationScale float, adder raster.Adder, paths ...*Path) {
func traceDashPath(dash []float, dashOffset float, approximationScale float, adder raster.Adder, paths ...*PathStorage) {
var dasher DashAdder
if dash != nil && len(dash) > 0 {
dasher.dash = dash

View File

@ -6,7 +6,6 @@ package draw2d
import (
"exp/draw"
"image"
"math"
"freetype-go.googlecode.com/hg/freetype/raster"
)
@ -41,7 +40,7 @@ type GraphicContext struct {
type contextStack struct {
tr MatrixTransform
path *Path
path *PathStorage
lineWidth float
dash []float
dashOffset float
@ -65,7 +64,7 @@ func NewGraphicContext(pi *image.RGBA) *GraphicContext {
gc.current = new(contextStack)
gc.current.tr = NewIdentityMatrix()
gc.current.path = new(Path)
gc.current.path = new(PathStorage)
gc.current.lineWidth = 1.0
gc.current.strokeColor = image.Black
gc.current.fillColor = image.White
@ -159,7 +158,7 @@ func (gc *GraphicContext) Restore() {
}
func (gc *GraphicContext) BeginPath() {
gc.current.path = new(Path)
gc.current.path = new(PathStorage)
}
func (gc *GraphicContext) MoveTo(x, y float) {
@ -202,43 +201,6 @@ func (gc *GraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) {
gc.current.path.RArcTo(dcx, dcy, rx, ry, startAngle, angle)
}
//high level path creation
func (gc *GraphicContext) Rect(x1, y1, x2, y2 float) {
if gc.current.path.isEmpty() {
gc.current.path.MoveTo(x1, y1)
} else {
gc.current.path.LineTo(x1, y1)
}
gc.current.path.LineTo(x2, y1)
gc.current.path.LineTo(x2, y2)
gc.current.path.LineTo(x1, y2)
gc.current.path.Close()
}
func (gc *GraphicContext) RoundRect(x1, y1, x2, y2, arcWidth, arcHeight float) {
arcWidth = arcWidth/2;
arcHeight = arcHeight/2;
gc.MoveTo(x1, y1+ arcHeight);
gc.QuadCurveTo(x1, y1, x1 + arcWidth, y1);
gc.LineTo(x2-arcWidth, y1);
gc.QuadCurveTo(x2, y1, x2, y1 + arcHeight);
gc.LineTo(x2, y2-arcHeight);
gc.QuadCurveTo(x2, y2, x2 - arcWidth, y2);
gc.LineTo(x1 + arcWidth, y2);
gc.QuadCurveTo(x1, y2, x1, y2 - arcHeight);
gc.Close()
}
func (gc *GraphicContext) Ellipse(cx, cy, rx, ry float) {
gc.current.path.ArcTo(cx, cy, rx, ry, 0, -math.Pi * 2)
gc.current.path.Close()
}
func (gc *GraphicContext) Circle(cx, cy, radius float) {
gc.current.path.ArcTo(cx, cy, radius, radius, 0, -math.Pi * 2)
gc.current.path.Close()
}
func (gc *GraphicContext) Close() {
gc.current.path.Close()
}
@ -248,10 +210,10 @@ func (gc *GraphicContext) paint(color image.Color) {
painter.SetColor(color)
gc.rasterizer.Rasterize(painter)
gc.rasterizer.Clear()
gc.current.path = new(Path)
gc.current.path = new(PathStorage)
}
func (gc *GraphicContext) Stroke(paths ...*Path) {
func (gc *GraphicContext) Stroke(paths ...*PathStorage) {
paths = append(paths, gc.current.path)
gc.rasterizer.UseNonZeroWinding = true
rasterPath := new(raster.Path)
@ -265,7 +227,7 @@ func (gc *GraphicContext) Stroke(paths ...*Path) {
gc.paint(gc.current.strokeColor)
}
func (gc *GraphicContext) Fill(paths ...*Path) {
func (gc *GraphicContext) Fill(paths ...*PathStorage) {
paths = append(paths, gc.current.path)
gc.rasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule()
mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer)
@ -273,7 +235,7 @@ func (gc *GraphicContext) Fill(paths ...*Path) {
gc.paint(gc.current.fillColor)
}
func (gc *GraphicContext) FillStroke(paths ...*Path) {
func (gc *GraphicContext) FillStroke(paths ...*PathStorage) {
paths = append(paths, gc.current.path)
mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer)
tracePath(gc.current.tr.GetMaxAbsScaling(), mta, paths...)

View File

@ -3,26 +3,18 @@
package draw2d
import (
"fmt"
"math"
)
type PathCmd int
const (
MoveTo PathCmd = iota
LineTo
QuadCurveTo
CubicCurveTo
ArcTo
Close
)
type Path struct {
commands []PathCmd
vertices []float
x, y float
type Path interface {
MoveTo(x, y float)
RMoveTo(dx, dy float)
LineTo(x, y float)
RLineTo(dx, dy float)
QuadCurveTo(cx, cy, x, y float)
RQuadCurveTo(dcx, dcy, dx, dy float)
CubicCurveTo(cx1, cy1, cx2, cy2, x, y float)
RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float)
ArcTo(cx, cy, rx, ry, startAngle, angle float)
RArcTo(dcx, dcy, rx, ry, startAngle, angle float)
Close()
}
type LineTracer interface {
@ -30,200 +22,3 @@ type LineTracer interface {
LineTo(x, y float)
}
func (p *Path) appendToPath(cmd PathCmd, vertices ...float) {
p.commands = append(p.commands, cmd)
p.vertices = append(p.vertices, vertices...)
}
func (src *Path) Copy() (dest *Path) {
dest = new(Path)
dest.commands = make([]PathCmd, len(src.commands))
copy(dest.commands, src.commands)
dest.vertices = make([]float, len(src.vertices))
copy(dest.vertices, src.vertices)
return dest
}
func (p *Path) LastPoint() (x, y float) {
return p.x, p.y
}
func (p *Path) isEmpty() bool {
return len(p.commands) == 0
}
func (p *Path) Close() *Path {
p.appendToPath(Close)
return p
}
func (p *Path) MoveTo(x, y float) *Path {
p.appendToPath(MoveTo, x, y)
p.x = x
p.y = y
return p
}
func (p *Path) RMoveTo(dx, dy float) *Path {
x, y := p.LastPoint()
p.MoveTo(x+dx, y+dy)
return p
}
func (p *Path) LineTo(x, y float) *Path {
p.appendToPath(LineTo, x, y)
p.x = x
p.y = y
return p
}
func (p *Path) RLineTo(dx, dy float) *Path {
x, y := p.LastPoint()
p.LineTo(x+dx, y+dy)
return p
}
func (p *Path) Rect(x1, y1, x2, y2 float) *Path {
w, h := x2-x1, y2-y1
if len(p.commands) > 0 {
p.LineTo(x1, y1)
} else {
p.MoveTo(x1, y1)
}
p.LineTo(x1+w, y1)
p.LineTo(x1+w, y1+h)
p.LineTo(x1, y1+h)
p.LineTo(x1, y1)
return p
}
func (p *Path) RRect(dx1, dy1, dx2, dy2 float) *Path {
x, y := p.LastPoint()
p.Rect(x+dx1, y+dy1, x+dx2, y+dy2)
return p
}
func (p *Path) QuadCurveTo(cx, cy, x, y float) *Path {
p.appendToPath(QuadCurveTo, cx, cy, x, y)
p.x = x
p.y = y
return p
}
func (p *Path) RQuadCurveTo(dcx, dcy, dx, dy float) *Path {
x, y := p.LastPoint()
p.RQuadCurveTo(x+dcx, y+dcy, x+dx, y+dy)
return p
}
func (p *Path) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) *Path {
p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y)
p.x = x
p.y = y
return p
}
func (p *Path) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) *Path {
x, y := p.LastPoint()
p.RCubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy)
return p
}
func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, angle float) *Path {
endAngle := startAngle + angle
clockWise := true
if angle < 0 {
clockWise = false
}
// normalize
if clockWise {
for endAngle < startAngle {
endAngle += math.Pi * 2.0
}
} else {
for startAngle < endAngle {
startAngle += math.Pi * 2.0
}
}
startX := cx + cos(startAngle)*rx
startY := cy + sin(startAngle)*ry
if len(p.commands) > 0 {
p.LineTo(startX, startY)
} else {
p.MoveTo(startX, startY)
}
p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle)
p.x = cx + cos(endAngle)*rx
p.y = cy + sin(endAngle)*ry
return p
}
func (p *Path) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) *Path {
x, y := p.LastPoint()
p.RArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle)
return p
}
func (p *Path) TraceLine(tracer LineTracer, approximationScale float) {
j := 0
x, y := 0.0, 0.0
firstX, firstY := x, y
if len(p.commands) > 0 {
if p.commands[0] == MoveTo {
firstX, firstY = p.vertices[0], p.vertices[1]
}
}
for _, cmd := range p.commands {
switch cmd {
case MoveTo:
tracer.MoveTo(p.vertices[j], p.vertices[j+1])
x, y = p.vertices[j], p.vertices[j+1]
firstX, firstY = x, y
j = j + 2
case LineTo:
tracer.LineTo(p.vertices[j], p.vertices[j+1])
x, y = p.vertices[j], p.vertices[j+1]
j = j + 2
case QuadCurveTo:
quadraticBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], approximationScale, 0)
x, y = p.vertices[j+2], p.vertices[j+3]
j = j + 4
case CubicCurveTo:
cubicBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale, 0, 0)
x, y = p.vertices[j+4], p.vertices[j+5]
j = j + 6
case ArcTo:
arc(tracer, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale)
j = j + 6
case Close:
tracer.LineTo(firstX, firstY)
x, y = firstX, firstY
}
}
}
func (p *Path) String() string {
s := ""
j := 0
for _, cmd := range p.commands {
switch cmd {
case MoveTo:
s += fmt.Sprintf("MoveTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
j = j + 2
case LineTo:
s += fmt.Sprintf("LineTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
j = j + 2
case QuadCurveTo:
s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3])
j = j + 4
case CubicCurveTo:
s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
j = j + 6
case ArcTo:
s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
j = j + 6
case Close:
s += "Close\n"
}
}
return s
}

View File

@ -15,7 +15,7 @@ func floatToPoint(x, y float) raster.Point {
return raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}
}
func tracePath(approximationScale float, adder raster.Adder, paths ...*Path) {
func tracePath(approximationScale float, adder raster.Adder, paths ...*PathStorage) {
pathAdder := &PathAdder{adder}
for _, path := range paths {
path.TraceLine(pathAdder, approximationScale)

View File

@ -0,0 +1,203 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"fmt"
"math"
)
type PathCmd int
const (
MoveTo PathCmd = iota
LineTo
QuadCurveTo
CubicCurveTo
ArcTo
Close
)
type PathStorage struct {
commands []PathCmd
vertices []float
x, y float
}
func (p *PathStorage) appendToPath(cmd PathCmd, vertices ...float) {
p.commands = append(p.commands, cmd)
p.vertices = append(p.vertices, vertices...)
}
func (src *PathStorage) Copy() (dest *PathStorage) {
dest = new(PathStorage)
dest.commands = make([]PathCmd, len(src.commands))
copy(dest.commands, src.commands)
dest.vertices = make([]float, len(src.vertices))
copy(dest.vertices, src.vertices)
return dest
}
func (p *PathStorage) LastPoint() (x, y float) {
return p.x, p.y
}
func (p *PathStorage) isEmpty() bool {
return len(p.commands) == 0
}
func (p *PathStorage) Close() *PathStorage {
p.appendToPath(Close)
return p
}
func (p *PathStorage) MoveTo(x, y float) *PathStorage {
p.appendToPath(MoveTo, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RMoveTo(dx, dy float) *PathStorage {
x, y := p.LastPoint()
p.MoveTo(x+dx, y+dy)
return p
}
func (p *PathStorage) LineTo(x, y float) *PathStorage {
p.appendToPath(LineTo, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RLineTo(dx, dy float) *PathStorage {
x, y := p.LastPoint()
p.LineTo(x+dx, y+dy)
return p
}
func (p *PathStorage) QuadCurveTo(cx, cy, x, y float) *PathStorage {
p.appendToPath(QuadCurveTo, cx, cy, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RQuadCurveTo(dcx, dcy, dx, dy float) *PathStorage {
x, y := p.LastPoint()
p.RQuadCurveTo(x+dcx, y+dcy, x+dx, y+dy)
return p
}
func (p *PathStorage) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) *PathStorage {
p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) *PathStorage {
x, y := p.LastPoint()
p.RCubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy)
return p
}
func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float) *PathStorage {
endAngle := startAngle + angle
clockWise := true
if angle < 0 {
clockWise = false
}
// normalize
if clockWise {
for endAngle < startAngle {
endAngle += math.Pi * 2.0
}
} else {
for startAngle < endAngle {
startAngle += math.Pi * 2.0
}
}
startX := cx + cos(startAngle)*rx
startY := cy + sin(startAngle)*ry
if len(p.commands) > 0 {
p.LineTo(startX, startY)
} else {
p.MoveTo(startX, startY)
}
p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle)
p.x = cx + cos(endAngle)*rx
p.y = cy + sin(endAngle)*ry
return p
}
func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) *PathStorage {
x, y := p.LastPoint()
p.RArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle)
return p
}
func (p *PathStorage) TraceLine(tracer LineTracer, approximationScale float) {
j := 0
x, y := 0.0, 0.0
firstX, firstY := x, y
if len(p.commands) > 0 {
if p.commands[0] == MoveTo {
firstX, firstY = p.vertices[0], p.vertices[1]
}
}
for _, cmd := range p.commands {
switch cmd {
case MoveTo:
tracer.MoveTo(p.vertices[j], p.vertices[j+1])
x, y = p.vertices[j], p.vertices[j+1]
firstX, firstY = x, y
j = j + 2
case LineTo:
tracer.LineTo(p.vertices[j], p.vertices[j+1])
x, y = p.vertices[j], p.vertices[j+1]
j = j + 2
case QuadCurveTo:
quadraticBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], approximationScale, 0)
x, y = p.vertices[j+2], p.vertices[j+3]
j = j + 4
case CubicCurveTo:
cubicBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale, 0, 0)
x, y = p.vertices[j+4], p.vertices[j+5]
j = j + 6
case ArcTo:
arc(tracer, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale)
j = j + 6
case Close:
tracer.LineTo(firstX, firstY)
x, y = firstX, firstY
}
}
}
func (p *PathStorage) String() string {
s := ""
j := 0
for _, cmd := range p.commands {
switch cmd {
case MoveTo:
s += fmt.Sprintf("MoveTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
j = j + 2
case LineTo:
s += fmt.Sprintf("LineTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
j = j + 2
case QuadCurveTo:
s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3])
j = j + 4
case CubicCurveTo:
s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
j = j + 6
case ArcTo:
s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
j = j + 6
case Close:
s += "Close\n"
}
}
return s
}