Skip to content

Commit

Permalink
feat: refactor path generation
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed May 8, 2024
1 parent 2cf71e1 commit d06127e
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 126 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 6 additions & 6 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
62 changes: 31 additions & 31 deletions svg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
),
},
{
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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)"),
),
},
} {
Expand Down
153 changes: 75 additions & 78 deletions svgpath/svgpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
Expand Down
11 changes: 5 additions & 6 deletions svgpath/svgpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@ import (
func TestString(t *testing.T) {
for _, tc := range []struct {
name string
path svgpath.Path
path *svgpath.Path
expected string
}{
{
name: "empty",
},
{
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",
},
} {
Expand Down

0 comments on commit d06127e

Please sign in to comment.