package svg import ( "fmt" "strings" ) type ( D []PathCommand PathCommand interface { PrintPathCommand() (string, string, bool) PrintPathCommandParameters() string } // PathCommand implementations // PathRelative makes any PathCommand and converts it into a relative command (makes lowercase) PathRelative[C PathCommand] struct { Command C } PathMoveTo struct { X, Y float64 } PathLineTo struct { X, Y float64 } PathHorizontalLine float64 PathVerticalLine float64 PathCubicBezier []PathCubicBezierParameterTuple PathCubicBezierParameterTuple struct { X1, Y1 float64 X2, Y2 float64 X, Y float64 } PathSmoothCubicBezier []PathSmoothCubicBezierParameterTuple PathSmoothCubicBezierParameterTuple struct { X2, Y2 float64 X, Y float64 } PathQuadraticBezier []PathQuadraticBezierParameterTuple PathQuadraticBezierParameterTuple struct { X1, Y1 float64 X, Y float64 } PathSmoothQuadraticBezier []PathSmoothQuadraticBezierParameterTuple PathSmoothQuadraticBezierParameterTuple struct { X, Y float64 } PathElliptical []PathEllipticalParameterTuple PathEllipticalParameterTuple struct { RX, RY float64 Angle float64 LargeArc bool Clockwise bool X, Y float64 } PathClose struct{} ) func PrintPathCommand(c PathCommand) (string, bool) { cmd, params, ok := c.PrintPathCommand() if !ok { return "", false } return fmt.Sprintf("%s %s", cmd, params), true } func (c PathRelative[C]) PrintPathCommand() (string, string, bool) { cmd, params, ok := c.Command.PrintPathCommand() return strings.ToLower(cmd), params, ok } func (c PathMoveTo) PrintPathCommand() (string, string, bool) { return "M", fmt.Sprintf("%v,%v", c.X, c.Y), true } func (c PathLineTo) PrintPathCommand() (string, string, bool) { return "L", fmt.Sprintf("%v,%v", c.X, c.Y), true } func (c PathHorizontalLine) PrintPathCommand() (string, string, bool) { return "H", fmt.Sprintf("%v", float64(c)), true } func (c PathVerticalLine) PrintPathCommand() (string, string, bool) { return "V", fmt.Sprintf("%v", float64(c)), false } func (c PathCubicBezier) PrintPathCommand() (string, string, bool) { if len(c) == 0 { return "", "", false } return "C", printTuples(c), true } func (c PathCubicBezier) Append(x1, y1, x2, y2, x, y float64) PathCubicBezier { return append(c, PathCubicBezierParameterTuple{ X1: x1, Y1: y1, X2: x2, Y2: y2, X: x, Y: y, }) } func (t PathCubicBezierParameterTuple) String() string { return fmt.Sprintf("%v,%v %v,%v %v,%v", t.X1, t.Y1, t.X2, t.Y2, t.X, t.Y) } func (c PathSmoothCubicBezier) PrintPathCommand() (string, string, bool) { if len(c) == 0 { return "", "", false } return "S", printTuples(c), true } func (c PathSmoothCubicBezier) Append(x2, y2, x, y float64) PathSmoothCubicBezier { return append(c, PathSmoothCubicBezierParameterTuple{ X2: x2, Y2: y2, X: x, Y: y, }) } func (t PathSmoothCubicBezierParameterTuple) String() string { return fmt.Sprintf("%v,%v %v,%v", t.X2, t.Y2, t.X, t.Y) } func (c PathQuadraticBezier) PrintPathCommand() (string, string, bool) { if len(c) == 0 { return "", "", false } return "Q", printTuples(c), true } func (c PathQuadraticBezier) Append(x1, y1, x, y float64) PathQuadraticBezier { return append(c, PathQuadraticBezierParameterTuple{ X1: x1, Y1: y1, X: x, Y: y, }) } func (t PathQuadraticBezierParameterTuple) String() string { return fmt.Sprintf("%v,%v %v,%v", t.X1, t.Y1, t.X, t.Y) } func (c PathSmoothQuadraticBezier) PrintPathCommand() (string, string, bool) { if len(c) == 0 { return "", "", false } return "T", printTuples(c), true } func (c PathSmoothQuadraticBezier) Append(x, y float64) PathSmoothQuadraticBezier { return append(c, PathSmoothQuadraticBezierParameterTuple{ X: x, Y: y, }) } func (t PathSmoothQuadraticBezierParameterTuple) String() string { return fmt.Sprintf("%v,%v", t.X, t.Y) } func (c PathElliptical) PrintPathCommand() (string, string, bool) { if len(c) == 0 { return "", "", false } return "A", printTuples(c), true } func (c PathElliptical) Append(rx, ry, angle float64, largeArc, clockwise bool, x, y float64) PathElliptical { return append(c, PathEllipticalParameterTuple{ RX: rx, RY: ry, Angle: angle, LargeArc: largeArc, Clockwise: clockwise, X: x, Y: y, }) } func (t PathEllipticalParameterTuple) String() string { var ( largeArcFlag int sweepFlag int ) if t.LargeArc { largeArcFlag = 1 } if t.Clockwise { sweepFlag = 1 } return fmt.Sprintf("%v,%v,%v,%d,%d,%v,%v", t.RX, t.RY, t.Angle, largeArcFlag, sweepFlag, t.X, t.Y) } func printTuples[S fmt.Stringer](vs []S) string { ss := make([]string, len(vs)) for i, v := range vs { ss[i] = fmt.Sprint(v) } return strings.Join(ss, " ") }