187 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package drawing
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| )
 | |
| 
 | |
| // PathBuilder describes the interface for path drawing.
 | |
| type PathBuilder interface {
 | |
| 	// LastPoint returns the current point of the current sub path
 | |
| 	LastPoint() (x, y float64)
 | |
| 	// MoveTo creates a new subpath that start at the specified point
 | |
| 	MoveTo(x, y float64)
 | |
| 	// LineTo adds a line to the current subpath
 | |
| 	LineTo(x, y float64)
 | |
| 	// QuadCurveTo adds a quadratic Bézier curve to the current subpath
 | |
| 	QuadCurveTo(cx, cy, x, y float64)
 | |
| 	// CubicCurveTo adds a cubic Bézier curve to the current subpath
 | |
| 	CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64)
 | |
| 	// ArcTo adds an arc to the current subpath
 | |
| 	ArcTo(cx, cy, rx, ry, startAngle, angle float64)
 | |
| 	// Close creates a line from the current point to the last MoveTo
 | |
| 	// point (if not the same) and mark the path as closed so the
 | |
| 	// first and last lines join nicely.
 | |
| 	Close()
 | |
| }
 | |
| 
 | |
| // PathComponent represents component of a path
 | |
| type PathComponent int
 | |
| 
 | |
| const (
 | |
| 	// MoveToComponent is a MoveTo component in a Path
 | |
| 	MoveToComponent PathComponent = iota
 | |
| 	// LineToComponent is a LineTo component in a Path
 | |
| 	LineToComponent
 | |
| 	// QuadCurveToComponent is a QuadCurveTo component in a Path
 | |
| 	QuadCurveToComponent
 | |
| 	// CubicCurveToComponent is a CubicCurveTo component in a Path
 | |
| 	CubicCurveToComponent
 | |
| 	// ArcToComponent is a ArcTo component in a Path
 | |
| 	ArcToComponent
 | |
| 	// CloseComponent is a ArcTo component in a Path
 | |
| 	CloseComponent
 | |
| )
 | |
| 
 | |
| // Path stores points
 | |
| type Path struct {
 | |
| 	// Components is a slice of PathComponent in a Path and mark the role of each points in the Path
 | |
| 	Components []PathComponent
 | |
| 	// Points are combined with Components to have a specific role in the path
 | |
| 	Points []float64
 | |
| 	// Last Point of the Path
 | |
| 	x, y float64
 | |
| }
 | |
| 
 | |
| func (p *Path) appendToPath(cmd PathComponent, points ...float64) {
 | |
| 	p.Components = append(p.Components, cmd)
 | |
| 	p.Points = append(p.Points, points...)
 | |
| }
 | |
| 
 | |
| // LastPoint returns the current point of the current path
 | |
| func (p *Path) LastPoint() (x, y float64) {
 | |
| 	return p.x, p.y
 | |
| }
 | |
| 
 | |
| // MoveTo starts a new path at (x, y) position
 | |
| func (p *Path) MoveTo(x, y float64) {
 | |
| 	p.appendToPath(MoveToComponent, x, y)
 | |
| 	p.x = x
 | |
| 	p.y = y
 | |
| }
 | |
| 
 | |
| // LineTo adds a line to the current path
 | |
| func (p *Path) LineTo(x, y float64) {
 | |
| 	if len(p.Components) == 0 { //special case when no move has been done
 | |
| 		p.MoveTo(0, 0)
 | |
| 	}
 | |
| 	p.appendToPath(LineToComponent, x, y)
 | |
| 	p.x = x
 | |
| 	p.y = y
 | |
| }
 | |
| 
 | |
| // QuadCurveTo adds a quadratic bezier curve to the current path
 | |
| func (p *Path) QuadCurveTo(cx, cy, x, y float64) {
 | |
| 	if len(p.Components) == 0 { //special case when no move has been done
 | |
| 		p.MoveTo(0, 0)
 | |
| 	}
 | |
| 	p.appendToPath(QuadCurveToComponent, cx, cy, x, y)
 | |
| 	p.x = x
 | |
| 	p.y = y
 | |
| }
 | |
| 
 | |
| // CubicCurveTo adds a cubic bezier curve to the current path
 | |
| func (p *Path) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
 | |
| 	if len(p.Components) == 0 { //special case when no move has been done
 | |
| 		p.MoveTo(0, 0)
 | |
| 	}
 | |
| 	p.appendToPath(CubicCurveToComponent, cx1, cy1, cx2, cy2, x, y)
 | |
| 	p.x = x
 | |
| 	p.y = y
 | |
| }
 | |
| 
 | |
| // ArcTo adds an arc to the path
 | |
| func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, delta float64) {
 | |
| 	endAngle := startAngle + delta
 | |
| 	clockWise := true
 | |
| 	if delta < 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 + math.Cos(startAngle)*rx
 | |
| 	startY := cy + math.Sin(startAngle)*ry
 | |
| 	if len(p.Components) > 0 {
 | |
| 		p.LineTo(startX, startY)
 | |
| 	} else {
 | |
| 		p.MoveTo(startX, startY)
 | |
| 	}
 | |
| 	p.appendToPath(ArcToComponent, cx, cy, rx, ry, startAngle, delta)
 | |
| 	p.x = cx + math.Cos(endAngle)*rx
 | |
| 	p.y = cy + math.Sin(endAngle)*ry
 | |
| }
 | |
| 
 | |
| // Close closes the current path
 | |
| func (p *Path) Close() {
 | |
| 	p.appendToPath(CloseComponent)
 | |
| }
 | |
| 
 | |
| // Copy make a clone of the current path and return it
 | |
| func (p *Path) Copy() (dest *Path) {
 | |
| 	dest = new(Path)
 | |
| 	dest.Components = make([]PathComponent, len(p.Components))
 | |
| 	copy(dest.Components, p.Components)
 | |
| 	dest.Points = make([]float64, len(p.Points))
 | |
| 	copy(dest.Points, p.Points)
 | |
| 	dest.x, dest.y = p.x, p.y
 | |
| 	return dest
 | |
| }
 | |
| 
 | |
| // Clear reset the path
 | |
| func (p *Path) Clear() {
 | |
| 	p.Components = p.Components[0:0]
 | |
| 	p.Points = p.Points[0:0]
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // IsEmpty returns true if the path is empty
 | |
| func (p *Path) IsEmpty() bool {
 | |
| 	return len(p.Components) == 0
 | |
| }
 | |
| 
 | |
| // String returns a debug text view of the path
 | |
| func (p *Path) String() string {
 | |
| 	s := ""
 | |
| 	j := 0
 | |
| 	for _, cmd := range p.Components {
 | |
| 		switch cmd {
 | |
| 		case MoveToComponent:
 | |
| 			s += fmt.Sprintf("MoveTo: %f, %f\n", p.Points[j], p.Points[j+1])
 | |
| 			j = j + 2
 | |
| 		case LineToComponent:
 | |
| 			s += fmt.Sprintf("LineTo: %f, %f\n", p.Points[j], p.Points[j+1])
 | |
| 			j = j + 2
 | |
| 		case QuadCurveToComponent:
 | |
| 			s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.Points[j], p.Points[j+1], p.Points[j+2], p.Points[j+3])
 | |
| 			j = j + 4
 | |
| 		case CubicCurveToComponent:
 | |
| 			s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.Points[j], p.Points[j+1], p.Points[j+2], p.Points[j+3], p.Points[j+4], p.Points[j+5])
 | |
| 			j = j + 6
 | |
| 		case ArcToComponent:
 | |
| 			s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.Points[j], p.Points[j+1], p.Points[j+2], p.Points[j+3], p.Points[j+4], p.Points[j+5])
 | |
| 			j = j + 6
 | |
| 		case CloseComponent:
 | |
| 			s += "Close\n"
 | |
| 		}
 | |
| 	}
 | |
| 	return s
 | |
| }
 |