diff --git a/README.md b/README.md index 5417c8d..3aedcd0 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ func ExampleNew() { svg.Title(svg.CharData("Example triangle01- simple example of a 'path'")), svg.Desc(svg.CharData("A path that draws a triangle")), svg.Rect().XYWidthHeight(1, 1, 398, 398, svg.Number).Fill("none").Stroke("blue"), - svg.Path().D(svgpath.New( - svgpath.MoveToAbs([]float64{100, 100}), - svgpath.LineToAbs([]float64{300, 100}), - svgpath.LineToAbs([]float64{200, 300}), - svgpath.ClosePath(), + svg.Path().D(svgpath.New(). + MoveToAbs([]float64{100, 100}). + LineToAbs([]float64{300, 100}). + LineToAbs([]float64{200, 300}). + ClosePath(), )).Fill("red").Stroke("blue").StrokeWidth(svg.Number(3)), ) if _, err := svgElement.WriteToIndent(os.Stdout, "", " "); err != nil { diff --git a/example_test.go b/example_test.go index 7f51c5e..9b8cae9 100644 --- a/example_test.go +++ b/example_test.go @@ -12,12 +12,12 @@ func ExampleNew() { svg.Title(svg.CharData("Example triangle01- simple example of a 'path'")), svg.Desc(svg.CharData("A path that draws a triangle")), svg.Rect().XYWidthHeight(1, 1, 398, 398, svg.Number).Fill("none").Stroke("blue"), - svg.Path().D(svgpath.New( - svgpath.MoveToAbs([]float64{100, 100}), - svgpath.LineToAbs([]float64{300, 100}), - svgpath.LineToAbs([]float64{200, 300}), - svgpath.ClosePath(), - )).Fill("red").Stroke("blue").StrokeWidth(svg.Number(3)), + svg.Path().D(svgpath.New(). + MoveToAbs([]float64{100, 100}). + LineToAbs([]float64{300, 100}). + LineToAbs([]float64{200, 300}). + ClosePath(), + ).Fill("red").Stroke("blue").StrokeWidth(svg.Number(3)), ) if _, err := svgElement.WriteToIndent(os.Stdout, "", " "); err != nil { panic(err) diff --git a/svg_test.go b/svg_test.go index a3bfc66..e6fd33f 100644 --- a/svg_test.go +++ b/svg_test.go @@ -88,12 +88,12 @@ func TestSimple(t *testing.T) { svg.Title(svg.CharData("Example triangle01- simple example of a 'path'")), svg.Desc(svg.CharData("A path that draws a triangle")), svg.Rect().XYWidthHeight(1, 1, 398, 398, svg.Number).Fill("none").Stroke("blue"), - svg.Path().D(svgpath.New( - svgpath.MoveToAbs([]float64{100, 100}), - svgpath.LineToAbs([]float64{300, 100}), - svgpath.LineToAbs([]float64{200, 300}), - svgpath.ClosePath(), - )).Fill("red").Stroke("blue").StrokeWidth(svg.Number(3)), + svg.Path().D(svgpath.New(). + MoveToAbs([]float64{100, 100}). + LineToAbs([]float64{300, 100}). + LineToAbs([]float64{200, 300}). + ClosePath(), + ).Fill("red").Stroke("blue").StrokeWidth(svg.Number(3)), ), }, { @@ -133,11 +133,11 @@ func TestSimple(t *testing.T) { {400, 300}, {400, 200}, }), - svg.Path().Class("SamplePath").D(svgpath.New( - svgpath.MoveToAbs([]float64{100, 200}), - svgpath.CurveToAbs([][]float64{{100, 100}, {250, 100}, {250, 200}}...), - svgpath.SmoothCurveToAbs([][]float64{{400, 300}, {400, 200}}...), - )), + svg.Path().Class("SamplePath").D(svgpath.New(). + MoveToAbs([]float64{100, 200}). + CurveToAbs([][]float64{{100, 100}, {250, 100}, {250, 200}}...). + SCurveToAbs([][]float64{{400, 300}, {400, 200}}...), + ), svg.Circle().Class("EndPoint").CXCYR(100, 200, 10, svg.Number), svg.Circle().Class("EndPoint").CXCYR(250, 200, 10, svg.Number), svg.Circle().Class("EndPoint").CXCYR(400, 200, 10, svg.Number), @@ -235,12 +235,12 @@ func TestSimple(t *testing.T) { name: "toap01", // 11.8.1 svg: svg.New().WidthHeight(12, 3.6, svg.CM).ViewBox(0, 0, 1000, 300).Children( svg.Defs( - svg.Path().ID("MyPath").D(svgpath.New( - svgpath.MoveToAbs([]float64{100, 200}), - svgpath.CurveToAbs([][]float64{{200, 100}, {300, 0}, {400, 100}}...), - svgpath.CurveToAbs([][]float64{{500, 200}, {600, 300}, {700, 200}}...), - svgpath.CurveToAbs([][]float64{{800, 100}, {900, 100}, {900, 100}}...), - )), + svg.Path().ID("MyPath").D(svgpath.New(). + MoveToAbs([]float64{100, 200}). + CurveToAbs([][]float64{{200, 100}, {300, 0}, {400, 100}}...). + CurveToAbs([][]float64{{500, 200}, {600, 300}, {700, 200}}...). + CurveToAbs([][]float64{{800, 100}, {900, 100}, {900, 100}}...), + ), ), svg.Desc( svg.CharData("Example toap01 - simple text on a path"), @@ -287,20 +287,20 @@ func TestSimple(t *testing.T) { svg.Circle().CXCYR(5, 5, 5, svg.Number).Fill("maroon").Opacity(0.85), ), ), - svg.Path().D(svgpath.New( - svgpath.MoveToAbs([]float64{10, 10}), - svgpath.HLineToRel(10), - svgpath.VLineToRel(10), - svgpath.ClosePath(), - svgpath.MoveToRel([]float64{20, 0}), - svgpath.HLineToRel(10), - svgpath.VLineToRel(10), - svgpath.ClosePath(), - svgpath.MoveToRel([]float64{20, 0}), - svgpath.HLineToRel(10), - svgpath.VLineToRel(10), - svgpath.ClosePath(), - )).Fill("none").Stroke("black").MarkerStart("url(#m1)").MarkerMid("url(#m2)").MarkerEnd("url(#m3)"), + svg.Path().D(svgpath.New(). + MoveToAbs([]float64{10, 10}). + HLineToRel(10). + VLineToRel(10). + ClosePath(). + MoveToRel([]float64{20, 0}). + HLineToRel(10). + VLineToRel(10). + ClosePath(). + MoveToRel([]float64{20, 0}). + HLineToRel(10). + VLineToRel(10). + ClosePath(), + ).Fill("none").Stroke("black").MarkerStart("url(#m1)").MarkerMid("url(#m2)").MarkerEnd("url(#m3)"), ), }, } { diff --git a/svgpath/svgpath.go b/svgpath/svgpath.go index 9278beb..1493843 100644 --- a/svgpath/svgpath.go +++ b/svgpath/svgpath.go @@ -21,117 +21,114 @@ const ( commandVerticalLineToRel = "v" ) -type Command interface { - String() string +// A Path is an SVG path. +type Path struct { + commands []string } -type Path []Command - -func New(commands ...Command) Path { - return Path(commands) +// New returns a new Path. +func New() *Path { + return &Path{} } -func (p Path) String() string { - ss := make([]string, 0, len(p)) - for _, s := range p { - ss = append(ss, s.String()) +func (p *Path) String() string { + if p == nil { + return "" } - return strings.Join(ss, " ") + return strings.Join(p.commands, " ") } -type curveToAbsCommand struct { - coords [][]float64 +// CurveToAbs appends an absolute curveto command to p. +func (p *Path) CurveToAbs(coords ...[]float64) *Path { + command := commandCurveToAbs + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func CurveToAbs(coords ...[]float64) Command { return curveToAbsCommand{coords: coords} } -func (c curveToAbsCommand) String() string { return commandCurveToAbs + formatCoords(c.coords) } - -type curveToRelCommand struct { - coords [][]float64 +// CurveToAbs appends a relative curveto command to p. +func (p *Path) CurveToRel(coords ...[]float64) *Path { + command := commandCurveToRel + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func CurveToRel(coords ...[]float64) Command { return curveToRelCommand{coords: coords} } -func (c curveToRelCommand) String() string { return commandCurveToRel + formatCoords(c.coords) } - -type closePathCommand struct{} - -func ClosePath() Command { return closePathCommand{} } -func (c closePathCommand) String() string { return string(commandClosePath) } - -type hLineToAbsCommand struct { - x float64 +// ClosePath appends a closepath command to p. +func (p *Path) ClosePath() *Path { + command := commandClosePath + p.commands = append(p.commands, command) + return p } -func HLineToAbs(x float64) Command { return hLineToAbsCommand{x: x} } -func (c hLineToAbsCommand) String() string { return commandHorizontalLineToAbs + formatFloat(c.x) } - -type hLineToRelCommand struct { - x float64 +// HLineToAbs appends an absolute horizontal lineto command to p. +func (p *Path) HLineToAbs(x float64) *Path { + command := commandHorizontalLineToAbs + formatFloat(x) + p.commands = append(p.commands, command) + return p } -func HLineToRel(x float64) Command { return hLineToRelCommand{x: x} } -func (c hLineToRelCommand) String() string { return commandHorizontalLineToRel + formatFloat(c.x) } - -type lineToAbsCommand struct { - coords [][]float64 +// HLineToAbs appends a relative horizontal lineto command to p. +func (p *Path) HLineToRel(x float64) *Path { + command := commandHorizontalLineToRel + formatFloat(x) + p.commands = append(p.commands, command) + return p } -func LineToAbs(coords ...[]float64) Command { return lineToAbsCommand{coords: coords} } -func (c lineToAbsCommand) String() string { return commandLineToAbs + formatCoords(c.coords) } - -type lineToRelCommand struct { - coords [][]float64 +// LineToAbs appends an absolute lineto command to p. +func (p *Path) LineToAbs(coords ...[]float64) *Path { + command := commandLineToAbs + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func LineToRel(coords ...[]float64) Command { return lineToRelCommand{coords: coords} } -func (c lineToRelCommand) String() string { return commandLineToRel + formatCoords(c.coords) } - -type moveToAbsCommand struct { - coords [][]float64 +// LineToAbs appends a relative lineto command to p. +func (p *Path) LineToRel(coords ...[]float64) *Path { + command := commandLineToRel + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func MoveToAbs(coords ...[]float64) Command { return moveToAbsCommand{coords: coords} } -func (c moveToAbsCommand) String() string { return commandMoveToAbs + formatCoords(c.coords) } - -type moveToRelCommand struct { - coords [][]float64 +// MoveToAbs appends an absolute moveto command to p. +func (p *Path) MoveToAbs(coords ...[]float64) *Path { + command := commandMoveToAbs + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func MoveToRel(coords ...[]float64) Command { return moveToRelCommand{coords: coords} } -func (c moveToRelCommand) String() string { return commandMoveToRel + formatCoords(c.coords) } - -type smoothCurveToAbsCommand struct { - coords [][]float64 +// MoveToAbs appends a relative moveto command to p. +func (p *Path) MoveToRel(coords ...[]float64) *Path { + command := commandMoveToRel + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func SmoothCurveToAbs(coords ...[]float64) Command { return smoothCurveToAbsCommand{coords: coords} } -func (c smoothCurveToAbsCommand) String() string { - return commandSmoothCurveToAbs + formatCoords(c.coords) +// SCurveToAbs appends an absolute shorthand/smooth curveto command to p. +func (p *Path) SCurveToAbs(coords ...[]float64) *Path { + command := commandSmoothCurveToAbs + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -type smoothCurveToRelCommand struct { - coords [][]float64 +// SCurveToRel appends a relative shorthand/smooth curveto command to p. +func (p *Path) SCurveToRel(coords ...[]float64) *Path { + command := commandSmoothCurveToRel + formatCoords(coords) + p.commands = append(p.commands, command) + return p } -func SmoothCurveToRel(coords ...[]float64) Command { return smoothCurveToRelCommand{coords: coords} } -func (c smoothCurveToRelCommand) String() string { - return commandSmoothCurveToRel + formatCoords(c.coords) +// VLineToAbs appends an absolute vertical lineto command to p. +func (p *Path) VLineToAbs(x float64) *Path { + command := commandVerticalLineToAbs + formatFloat(x) + p.commands = append(p.commands, command) + return p } -type vLineToAbsCommand struct { - x float64 +// VLineToAbs appends a relative vertical lineto command to p. +func (p *Path) VLineToRel(x float64) *Path { + command := commandVerticalLineToRel + formatFloat(x) + p.commands = append(p.commands, command) + return p } -func VLineToAbs(x float64) Command { return vLineToAbsCommand{x: x} } -func (c vLineToAbsCommand) String() string { return commandVerticalLineToAbs + formatFloat(c.x) } - -type vLineToRelCommand struct { - y float64 -} - -func VLineToRel(y float64) Command { return vLineToRelCommand{y: y} } -func (c vLineToRelCommand) String() string { return commandVerticalLineToRel + formatFloat(c.y) } - func formatCoord(c []float64) string { return formatFloat(c[0]) + "," + formatFloat(c[1]) } diff --git a/svgpath/svgpath_test.go b/svgpath/svgpath_test.go index 6b5f3eb..3bf7899 100644 --- a/svgpath/svgpath_test.go +++ b/svgpath/svgpath_test.go @@ -11,7 +11,7 @@ import ( func TestString(t *testing.T) { for _, tc := range []struct { name string - path svgpath.Path + path *svgpath.Path expected string }{ { @@ -19,11 +19,10 @@ func TestString(t *testing.T) { }, { name: "simple", - path: svgpath.Path{ - svgpath.MoveToAbs([]float64{200, 300}), - svgpath.LineToAbs([]float64{400, 50}), - svgpath.ClosePath(), - }, + path: svgpath.New(). + MoveToAbs([]float64{200, 300}). + LineToAbs([]float64{400, 50}). + ClosePath(), expected: "M200,300 L400,50 z", }, } {