// SPDX-License-Identifier: MIT
// Derived from Rough.js 4.6.6 and path-data-parser 0.1.0.
// Copyright (c) 2019, 2020 Preet Shihn.

package rough

import (
	
	
	
)

type ellipseParams struct {
	rx        float64
	ry        float64
	increment float64
}

type ellipseResult struct {
	opset           OpSet
	estimatedPoints []Point
}

var rendererHelper = renderHelper{
	randOffset:          randOffset,
	randOffsetWithRange: randOffsetWithRange,
	ellipse:             ellipse,
	doubleLineOps:       doubleLineOps,
}

func line(, , ,  float64,  *ResolvedOptions) OpSet {
	return OpSet{Type: OpSetPath, Ops: doubleLine(, , , , )}
}

func linearPath( []Point,  bool,  *ResolvedOptions) OpSet {
	 := len()
	if  > 2 {
		 := make([]Op, 0)
		for  := 0;  < -1; ++ {
			 = append(, doubleLine(
				[][0], [][1],
				[+1][0], [+1][1],
				,
			)...)
		}
		if  {
			 = append(, doubleLine(
				[-1][0], [-1][1],
				[0][0], [0][1],
				,
			)...)
		}
		return OpSet{Type: OpSetPath, Ops: }
	}
	if  == 2 {
		return line([0][0], [0][1], [1][0], [1][1], )
	}
	return OpSet{Type: OpSetPath, Ops: []Op{}}
}

func polygon( []Point,  *ResolvedOptions) OpSet {
	return linearPath(, true, )
}

func rectangle(, , ,  float64,  *ResolvedOptions) OpSet {
	return polygon([]Point{
		{, },
		{ + , },
		{ + ,  + },
		{,  + },
	}, )
}

func curve( []Point,  *ResolvedOptions) OpSet {
	 := curveWithOffset(, 1*(1+.Roughness*0.2), )
	if .DisableMultiStroke {
		return OpSet{Type: OpSetPath, Ops: }
	}
	 := cloneOptionsAlterSeed()
	 := curveWithOffset(, 1.5*(1+.Roughness*0.22), )
	return OpSet{Type: OpSetPath, Ops: append(, ...)}
}

func ellipse(, , ,  float64,  *ResolvedOptions) OpSet {
	 := generateEllipseParams(, , )
	return ellipseWithParams(, , , ).opset
}

func generateEllipseParams(,  float64,  *ResolvedOptions) ellipseParams {
	 := math.Sqrt(math.Pi * 2 * math.Sqrt((math.Pow(/2, 2)+math.Pow(/2, 2))/2))
	 := math.Ceil(math.Max(.CurveStepCount, (.CurveStepCount/math.Sqrt(200))*))
	 := (math.Pi * 2) / 
	 := math.Abs( / 2)
	 := math.Abs( / 2)
	 := 1 - .CurveFitting
	 += offsetOpt(*, )
	 += offsetOpt(*, )
	return ellipseParams{increment: , rx: , ry: }
}

func ellipseWithParams(,  float64,  *ResolvedOptions,  ellipseParams) ellipseResult {
	,  := computeEllipsePoints(
		.increment, , , .rx, .ry, 1,
		.increment*offset(0.1, offset(0.4, 1, ), ),
		,
	)
	 := roughCurve(, nil, )
	if !.DisableMultiStroke && .Roughness != 0 {
		,  := computeEllipsePoints(.increment, , , .rx, .ry, 1.5, 0, )
		 = append(, roughCurve(, nil, )...)
	}
	return ellipseResult{
		estimatedPoints: ,
		opset: OpSet{
			Type: OpSetPath,
			Ops:  ,
		},
	}
}

func arc(, , , , ,  float64, ,  bool,  *ResolvedOptions) OpSet {
	,  := , 
	 := math.Abs( / 2)
	 := math.Abs( / 2)
	 += offsetOpt(*0.01, )
	 += offsetOpt(*0.01, )
	,  := , 
	for  < 0 {
		 += math.Pi * 2
		 += math.Pi * 2
	}
	if - > math.Pi*2 {
		 = 0
		 = math.Pi * 2
	}
	 := (math.Pi * 2) / .CurveStepCount
	 := math.Min(/2, (-)/2)
	 := roughArc(, , , , , , , 1, )
	 := 
	if !.DisableMultiStroke {
		 = append(, roughArc(, , , , , , , 1.5, )...)
	}
	if  {
		if  {
			 = append(, doubleLine(
				, ,
				+*math.Cos(), +*math.Sin(),
				,
			)...)
			 = append(, doubleLine(
				, ,
				+*math.Cos(), +*math.Sin(),
				,
			)...)
		} else {
			 = append(,
				Op{Op: OpLineTo, Data: []float64{, }},
				Op{Op: OpLineTo, Data: []float64{ + *math.Cos(),  + *math.Sin()}},
			)
		}
	}
	return OpSet{Type: OpSetPath, Ops: }
}

var (
	pathNewlineRE = regexp.MustCompile(`\n`)
	// ECMAScript's \s includes Unicode whitespace beyond RE2's Perl \s.
	pathMinusSpaceRE = regexp.MustCompile(`-[\t\n\v\f\r \x{00a0}\x{1680}\x{2000}-\x{200a}\x{2028}\x{2029}\x{202f}\x{205f}\x{3000}\x{feff}]`)

	pathLiteralSpaceBug = "/(ss)/g"
)

func svgPath( string,  *ResolvedOptions) OpSet {
	 := newParsedPath()
	if jsTruthyNumber(.Simplification) {
		 := newRoughPath()
		 := newPathFitter(.linearPoints(), .closed())
		 = newParsedPath(.fit(.Simplification))
	}
	 := make([]Op, 0)
	,  := Point{}, Point{}
	 := false
	var ,  *Point
	 := ""
	for ,  := range .segments {
		 := .key
		 :=  >= "a" &&  <= "z"
		 := strings.ToUpper()
		 := append([]float64(nil), .data...)
		 := func(,  float64) Point {
			if  {
				return Point{ + [0],  + [1]}
			}
			return Point{, }
		}
		switch  {
		case "M":
			 = ([0], [1])
			,  = , true
		case "L":
			 := ([0], [1])
			 = append(, doubleLine([0], [1], [0], [1], )...)
			 = 
		case "H":
			 := [0]
			if  {
				 += [0]
			}
			 := Point{, [1]}
			 = append(, doubleLine([0], [1], [0], [1], )...)
			 = 
		case "V":
			 := [0]
			if  {
				 += [1]
			}
			 := Point{[0], }
			 = append(, doubleLine([0], [1], [0], [1], )...)
			 = 
		case "C":
			 := ([0], [1])
			 := ([2], [3])
			 := ([4], [5])
			 = append(, bezierToCurrent(, , , , )...)
			 = 
			 := 
			 = &
		case "S":
			 := 
			if ( == "C" ||  == "S") &&  != nil {
				 = Point{2*[0] - (*)[0], 2*[1] - (*)[1]}
			}
			 := ([0], [1])
			 := ([2], [3])
			 = append(, bezierToCurrent(, , , , )...)
			 = 
			 := 
			 = &
		case "Q":
			 := ([0], [1])
			 := ([2], [3])
			 := Point{[0] + ([0]-[0])*2/3, [1] + ([1]-[1])*2/3}
			 := Point{[0] + ([0]-[0])*2/3, [1] + ([1]-[1])*2/3}
			 = append(, bezierToCurrent(, , , , )...)
			 = 
			 := 
			 = &
		case "T":
			 := 
			if ( == "Q" ||  == "T") &&  != nil {
				 = Point{2*[0] - (*)[0], 2*[1] - (*)[1]}
			}
			 := ([0], [1])
			 := Point{[0] + ([0]-[0])*2/3, [1] + ([1]-[1])*2/3}
			 := Point{[0] + ([0]-[0])*2/3, [1] + ([1]-[1])*2/3}
			 = append(, bezierToCurrent(, , , , )...)
			 = 
			 := 
			 = &
		case "A":
			 := ([5], [6])
			if  !=  {
				if [0] == 0 || [1] == 0 {
					 = append(, bezierToCurrent(, , , , )...)
				} else {
					 := 
					for ,  := range arcToCubicCurves([0], [1], [0], [1], math.Abs([0]), math.Abs([1]), [2], [3], [4], nil) {
						 = append(, bezierToCurrent(Point{[0], [1]}, Point{[2], [3]}, Point{[4], [5]}, , )...)
						 = Point{[4], [5]}
					}
				}
			}
			 = 
		case "Z":
			if  {
				 = append(, doubleLine([0], [1], [0], [1], )...)
				 = 
			}
		}
		if  != "C" &&  != "S" {
			 = nil
		}
		if  != "Q" &&  != "T" {
			 = nil
		}
		 = 
	}
	return OpSet{Type: OpSetPath, Ops: }
}

func arcToCubicCurves(, , , , , , , ,  float64,  []float64) [][6]float64 {
	 := math.Pi *  / 180
	 := make([]Point, 0)
	var , , ,  float64
	if  != nil {
		, , ,  = [0], [1], [2], [3]
	} else {
		,  = rotatePathPoint(, , -)
		,  = rotatePathPoint(, , -)
		 := ( - ) / 2
		 := ( - ) / 2
		 := */(*) + */(*)
		if  > 1 {
			 = math.Sqrt()
			 *= 
			 *= 
		}
		 := 1.0
		if  ==  {
			 = -1
		}
		,  := *, *
		 := * - ** - **
		 := ** + **
		 :=  * math.Sqrt(math.Abs(/))
		 = **/ + (+)/2
		 = *-*/ + (+)/2
		 = math.Asin(jsToFixedNumber((-)/, 9))
		 = math.Asin(jsToFixedNumber((-)/, 9))
		if  <  {
			 = math.Pi - 
		}
		if  <  {
			 = math.Pi - 
		}
		if  < 0 {
			 = math.Pi*2 + 
		}
		if  < 0 {
			 = math.Pi*2 + 
		}
		if  != 0 &&  >  {
			 -= math.Pi * 2
		}
		if  == 0 &&  >  {
			 -= math.Pi * 2
		}
	}
	 :=  - 
	if math.Abs() > math.Pi*120/180 {
		, ,  := , , 
		if  != 0 &&  >  {
			 =  + math.Pi*120/180
		} else {
			 =  - math.Pi*120/180
		}
		 =  + *math.Cos()
		 =  + *math.Sin()
		for ,  := range (, , , , , , , 0, , []float64{, , , }) {
			 = append(, Point{[0], [1]}, Point{[2], [3]}, Point{[4], [5]})
		}
	}
	 =  - 
	, , ,  := math.Cos(), math.Sin(), math.Cos(), math.Sin()
	 := math.Tan( / 4)
	,  := 4.0/3**, 4.0/3**
	 := Point{, }
	 := Point{ + *,  - *}
	 := Point{ + *,  - *}
	 := Point{, }
	 = Point{2*[0] - [0], 2*[1] - [1]}
	 = append([]Point{, , }, ...)
	if  != nil {
		 := make([][6]float64, 0, len()/3)
		for  := 0; +2 < len();  += 3 {
			 = append(, [6]float64{[][0], [][1], [+1][0], [+1][1], [+2][0], [+2][1]})
		}
		return 
	}
	 := make([][6]float64, 0, len()/3)
	for  := 0; +2 < len();  += 3 {
		,  := rotatePathPoint([][0], [][1], )
		,  := rotatePathPoint([+1][0], [+1][1], )
		,  := rotatePathPoint([+2][0], [+2][1], )
		 = append(, [6]float64{, , , , , })
	}
	return 
}

func rotatePathPoint(, ,  float64) (float64, float64) {
	return *math.Cos() - *math.Sin(), *math.Sin() + *math.Cos()
}

func bezierToCurrent(, , ,  Point,  *ResolvedOptions) []Op {
	 := jsOr(.MaxRandomnessOffset, 1)
	 := [2]float64{,  + 0.3}
	 := 2
	if .DisableMultiStroke {
		 = 1
	}
	 := make([]Op, 0, *2)
	for  := 0;  < ; ++ {
		 := 
		if  != 0 && !.PreserveVertices {
			 = Point{[0] + offsetOpt([0], ), [1] + offsetOpt([0], )}
		}
		 := 
		if !.PreserveVertices {
			 = Point{[0] + offsetOpt([], ), [1] + offsetOpt([], )}
		}
		 = append(,
			Op{Op: OpMove, Data: []float64{[0], [1]}},
			Op{Op: OpBCurveTo, Data: []float64{
				[0] + offsetOpt([], ), [1] + offsetOpt([], ),
				[0] + offsetOpt([], ), [1] + offsetOpt([], ),
				[0], [1],
			}},
		)
	}
	return 
}

func solidFillPolygon( []Point,  *ResolvedOptions) OpSet {
	return solidFillPolygons([][]Point{}, )
}

func solidFillPolygons( [][]Point,  *ResolvedOptions) OpSet {
	 := make([]Op, 0)
	for ,  := range  {
		 := jsOr(.MaxRandomnessOffset, 0)
		if len() > 2 {
			 = append(, Op{Op: OpMove, Data: []float64{
				[0][0] + offsetOpt(, ),
				[0][1] + offsetOpt(, ),
			}})
			for  := 1;  < len(); ++ {
				 = append(, Op{Op: OpLineTo, Data: []float64{
					[][0] + offsetOpt(, ),
					[][1] + offsetOpt(, ),
				}})
			}
		}
	}
	return OpSet{Type: OpSetFillPath, Ops: }
}

func patternFillPolygon( []Point,  *ResolvedOptions) OpSet {
	return getFiller(, rendererHelper).fillPolygon(, )
}

func patternFillArc(, , , , ,  float64,  *ResolvedOptions) OpSet {
	,  := , 
	 := math.Abs( / 2)
	 := math.Abs( / 2)
	 += offsetOpt(*0.01, )
	 += offsetOpt(*0.01, )
	,  := , 
	for  < 0 {
		 += math.Pi * 2
		 += math.Pi * 2
	}
	if - > math.Pi*2 {
		 = 0
		 = math.Pi * 2
	}
	 := ( - ) / .CurveStepCount
	 := make([]Point, 0)
	for  := ;  <= ;  +=  {
		 = append(, Point{ + *math.Cos(),  + *math.Sin()})
	}
	 = append(,
		Point{ + *math.Cos(),  + *math.Sin()},
		Point{, },
	)
	return patternFillPolygon(, )
}

func randOffset( float64,  *ResolvedOptions) float64 {
	return offsetOpt(, )
}

func randOffsetWithRange(,  float64,  *ResolvedOptions) float64 {
	return offset(, , )
}

func doubleLineOps(, , ,  float64,  *ResolvedOptions) []Op {
	return doubleLineFill(, , , , )
}

func offset(,  float64,  *ResolvedOptions,  ...float64) float64 {
	 := 1.0
	if len() != 0 {
		 = [0]
	}
	return .Roughness *  * (random()*(-) + )
}

func offsetOpt( float64,  *ResolvedOptions,  ...float64) float64 {
	return offset(-, , , ...)
}

func doubleLine(, , ,  float64,  *ResolvedOptions) []Op {
	return doubleLineMode(, , , , , false)
}

func doubleLineFill(, , ,  float64,  *ResolvedOptions) []Op {
	return doubleLineMode(, , , , , true)
}

func doubleLineMode(, , ,  float64,  *ResolvedOptions,  bool) []Op {
	 := roughLine(, , , , , true, false)
	if (! && .DisableMultiStroke) || ( && .DisableMultiStrokeFill) {
		return 
	}
	 := roughLine(, , , , , true, true)
	return append(, ...)
}

func roughLine(, , ,  float64,  *ResolvedOptions, ,  bool) []Op {
	 := math.Pow(-, 2) + math.Pow(-, 2)
	 := math.Sqrt()
	 := 1.0
	if  < 200 {
		 = 1
	} else if  > 500 {
		 = 0.4
	} else {
		// JavaScript rounds the multiplication before the addition. The
		// jsNumberStep prevents an architecture-dependent fused multiply-add
		// at the observable 200/500 length boundaries.
		 := jsNumberStep(-0.0016668 * )
		 =  + 1.233334
	}

	 := jsOr(.MaxRandomnessOffset, 0)
	if **100 >  {
		 =  / 10
	}
	 :=  / 2
	 := 0.2 + random()*0.2
	 := .Bowing * .MaxRandomnessOffset * ( - ) / 200
	 := .Bowing * .MaxRandomnessOffset * ( - ) / 200
	 = offsetOpt(, , )
	 = offsetOpt(, , )
	 := func() float64 { return offsetOpt(, , ) }
	 := func() float64 { return offsetOpt(, , ) }
	 := .PreserveVertices
	 := make([]Op, 0, 2)
	if  {
		if  {
			 = append(, Op{Op: OpMove, Data: []float64{
				 + jsConditionalFloat(, 0, ),
				 + jsConditionalFloat(, 0, ),
			}})
		} else {
			 = append(, Op{Op: OpMove, Data: []float64{
				 + jsConditionalFloat(, 0, ),
				 + jsConditionalFloat(, 0, ),
			}})
		}
	}
	if  {
		 = append(, Op{Op: OpBCurveTo, Data: []float64{
			 +  + (-)* + (),
			 +  + (-)* + (),
			 +  + 2*(-)* + (),
			 +  + 2*(-)* + (),
			 + jsConditionalFloat(, 0, ),
			 + jsConditionalFloat(, 0, ),
		}})
	} else {
		 = append(, Op{Op: OpBCurveTo, Data: []float64{
			 +  + (-)* + (),
			 +  + (-)* + (),
			 +  + 2*(-)* + (),
			 +  + 2*(-)* + (),
			 + jsConditionalFloat(, 0, ),
			 + jsConditionalFloat(, 0, ),
		}})
	}
	return 
}

func curveWithOffset( []Point,  float64,  *ResolvedOptions) []Op {
	if len() == 0 {
		return []Op{}
	}
	 := make([]Point, 0, len()+2)
	 = append(,
		Point{[0][0] + offsetOpt(, ), [0][1] + offsetOpt(, )},
		Point{[0][0] + offsetOpt(, ), [0][1] + offsetOpt(, )},
	)
	for  := 1;  < len(); ++ {
		 = append(, Point{
			[][0] + offsetOpt(, ),
			[][1] + offsetOpt(, ),
		})
		if  == len()-1 {
			 = append(, Point{
				[][0] + offsetOpt(, ),
				[][1] + offsetOpt(, ),
			})
		}
	}
	return roughCurve(, nil, )
}

func roughCurve( []Point,  *Point,  *ResolvedOptions) []Op {
	 := len()
	 := make([]Op, 0)
	if  > 3 {
		 := 1 - .CurveTightness
		 = append(, Op{Op: OpMove, Data: []float64{[1][0], [1][1]}})
		for  := 1; +2 < ; ++ {
			 := []
			 := Point{
				[0] + (*[+1][0]-*[-1][0])/6,
				[1] + (*[+1][1]-*[-1][1])/6,
			}
			 := Point{
				[+1][0] + (*[][0]-*[+2][0])/6,
				[+1][1] + (*[][1]-*[+2][1])/6,
			}
			 := [+1]
			 = append(, Op{Op: OpBCurveTo, Data: []float64{
				[0], [1], [0], [1], [0], [1],
			}})
		}
		if  != nil {
			 := .MaxRandomnessOffset
			 = append(, Op{Op: OpLineTo, Data: []float64{
				(*)[0] + offsetOpt(, ),
				(*)[1] + offsetOpt(, ),
			}})
		}
	} else if  == 3 {
		 = append(,
			Op{Op: OpMove, Data: []float64{[1][0], [1][1]}},
			Op{Op: OpBCurveTo, Data: []float64{
				[1][0], [1][1],
				[2][0], [2][1],
				[2][0], [2][1],
			}},
		)
	} else if  == 2 {
		 = append(, doubleLine(
			[0][0], [0][1],
			[1][0], [1][1],
			,
		)...)
	}
	return 
}

func computeEllipsePoints(, , , , , ,  float64,  *ResolvedOptions) ([]Point, []Point) {
	 := make([]Point, 0)
	 := make([]Point, 0)
	if .Roughness == 0 {
		 /= 4
		 = append(, Point{ + *math.Cos(-),  + *math.Sin(-)})
		for  := 0.0;  <= math.Pi*2;  +=  {
			 := Point{ + *math.Cos(),  + *math.Sin()}
			 = append(, )
			 = append(, )
		}
		 = append(,
			Point{ + , },
			Point{ + *math.Cos(),  + *math.Sin()},
		)
		return , 
	}
	 := offsetOpt(0.5, ) - math.Pi/2

	 = append(, Point{
		offsetOpt(, ) +  + 0.9**math.Cos(-),
		offsetOpt(, ) +  + 0.9**math.Sin(-),
	})
	for  := ;  < math.Pi*2+-0.01;  +=  {
		 := Point{
			offsetOpt(, ) +  + *math.Cos(),
			offsetOpt(, ) +  + *math.Sin(),
		}
		 = append(, )
		 = append(, )
	}
	 = append(,
		Point{
			offsetOpt(, ) +  + *math.Cos(+math.Pi*2+*0.5),
			offsetOpt(, ) +  + *math.Sin(+math.Pi*2+*0.5),
		},
		Point{
			offsetOpt(, ) +  + 0.98**math.Cos(+),
			offsetOpt(, ) +  + 0.98**math.Sin(+),
		},
		Point{
			offsetOpt(, ) +  + 0.9**math.Cos(+*0.5),
			offsetOpt(, ) +  + 0.9**math.Sin(+*0.5),
		},
	)
	return , 
}

func roughArc(, , , , , , ,  float64,  *ResolvedOptions) []Op {
	 :=  + offsetOpt(0.1, )
	 := make([]Point, 0)
	 = append(, Point{
		offsetOpt(, ) +  + 0.9**math.Cos(-),
		offsetOpt(, ) +  + 0.9**math.Sin(-),
	})
	for  := ;  <= ;  +=  {
		 = append(, Point{
			offsetOpt(, ) +  + *math.Cos(),
			offsetOpt(, ) +  + *math.Sin(),
		})
	}
	 := Point{ + *math.Cos(),  + *math.Sin()}
	 = append(, , )
	return roughCurve(, nil, )
}

func bezierTo(, , , , ,  float64,  *roughPath,  *ResolvedOptions) []Op {
	 := jsOr(.MaxRandomnessOffset, 1)
	 := [2]float64{,  + 0.3}
	 := make([]Op, 0, 4)
	 := Point{0, 0}
	 := 2
	if .DisableMultiStroke {
		 = 1
	}
	for  := 0;  < ; ++ {
		if  == 0 {
			 = append(, Op{Op: OpMove, Data: []float64{.x(), .y()}})
		} else {
			 = append(, Op{Op: OpMove, Data: []float64{
				.x() + jsConditionalFloat(.PreserveVertices, 0, func() float64 { return offsetOpt([0], ) }),
				.y() + jsConditionalFloat(.PreserveVertices, 0, func() float64 { return offsetOpt([0], ) }),
			}})
		}
		if .PreserveVertices {
			 = Point{, }
		} else {
			 = Point{ + offsetOpt([], ),  + offsetOpt([], )}
		}
		 = append(, Op{Op: OpBCurveTo, Data: []float64{
			 + offsetOpt([], ),  + offsetOpt([], ),
			 + offsetOpt([], ),  + offsetOpt([], ),
			[0], [1],
		}})
	}
	.setPosition([0], [1])
	return 
}

func cloneOptionsAlterSeed( *ResolvedOptions) *ResolvedOptions {
	 := cloneResolvedOptions(*)
	.randomizer = nil
	if .Seed != 0 {
		.Seed = .Seed + 1
	}
	return &
}

func jsConditionalFloat( bool,  float64,  func() float64) float64 {
	if  {
		return 
	}
	return ()
}

func processSegment( *roughPath, ,  *pathSegment,  *ResolvedOptions) []Op {
	 := make([]Op, 0)
	switch .key {
	case "M", "m":
		 := .key == "m"
		if len(.data) >= 2 {
			,  := .data[0], .data[1]
			if  {
				 += .x()
				 += .y()
			}
			 := jsOr(.MaxRandomnessOffset, 0)
			 += offsetOpt(, )
			 += offsetOpt(, )
			.setPosition(, )
			 = append(, Op{Op: OpMove, Data: []float64{, }})
		}
	case "L", "l":
		 := .key == "l"
		if len(.data) >= 2 {
			,  := .data[0], .data[1]
			if  {
				 += .x()
				 += .y()
			}
			 = append(, doubleLine(.x(), .y(), , , )...)
			.setPosition(, )
		}
	case "H", "h":
		 := .key == "h"
		if len(.data) > 0 {
			 := .data[0]
			if  {
				 += .x()
			}
			 = append(, doubleLine(.x(), .y(), , .y(), )...)
			.setPosition(, .y())
		}
	case "V", "v":
		 := .key == "v"
		if len(.data) > 0 {
			 := .data[0]
			if  {
				 += .y()
			}
			 = append(, doubleLine(.x(), .y(), .x(), , )...)
			.setPosition(.x(), )
		}
	case "Z", "z":
		if .first != nil {
			 = append(, doubleLine(.x(), .y(), (*.first)[0], (*.first)[1], )...)
			.setPosition((*.first)[0], (*.first)[1])
			.first = nil
		}
	case "C", "c":
		 := .key == "c"
		if len(.data) >= 6 {
			,  := .data[0], .data[1]
			,  := .data[2], .data[3]
			,  := .data[4], .data[5]
			if  {
				 += .x()
				 += .x()
				 += .x()
				 += .y()
				 += .y()
				 += .y()
			}
			 = append(, bezierTo(, , , , , , , )...)
			 := Point{ + ( - ),  + ( - )}
			.bezierReflectionPoint = &
		}
	case "S", "s":
		 := .key == "s"
		if len(.data) >= 4 {
			,  := .data[0], .data[1]
			,  := .data[2], .data[3]
			if  {
				 += .x()
				 += .x()
				 += .y()
				 += .y()
			}
			,  := , 
			 := ""
			if  != nil {
				 = .key
			}
			if  == "c" ||  == "C" ||  == "s" ||  == "S" {
				if .bezierReflectionPoint != nil {
					,  = (*.bezierReflectionPoint)[0], (*.bezierReflectionPoint)[1]
				}
			}
			 = append(, bezierTo(, , , , , , , )...)
			 := Point{ + ( - ),  + ( - )}
			.bezierReflectionPoint = &
		}
	case "Q", "q":
		 := .key == "q"
		if len(.data) >= 4 {
			,  := .data[0], .data[1]
			,  := .data[2], .data[3]
			if  {
				 += .x()
				 += .x()
				 += .y()
				 += .y()
			}
			 := 1 * (1 + .Roughness*0.2)
			 := 1.5 * (1 + .Roughness*0.22)
			 = append(, Op{Op: OpMove, Data: []float64{
				.x() + offsetOpt(, ), .y() + offsetOpt(, ),
			}})
			 := Point{ + offsetOpt(, ),  + offsetOpt(, )}
			 = append(, Op{Op: OpQCurveTo, Data: []float64{
				 + offsetOpt(, ),  + offsetOpt(, ), [0], [1],
			}})
			 = append(, Op{Op: OpMove, Data: []float64{
				.x() + offsetOpt(, ), .y() + offsetOpt(, ),
			}})
			 = Point{ + offsetOpt(, ),  + offsetOpt(, )}
			 = append(, Op{Op: OpQCurveTo, Data: []float64{
				 + offsetOpt(, ),  + offsetOpt(, ), [0], [1],
			}})
			.setPosition([0], [1])
			 := Point{ + ( - ),  + ( - )}
			.quadReflectionPoint = &
		}
	case "T", "t":
		 := .key == "t"
		if len(.data) >= 2 {
			,  := .data[0], .data[1]
			if  {
				 += .x()
				 += .y()
			}
			,  := , 
			 := ""
			if  != nil {
				 = .key
			}
			if  == "q" ||  == "Q" ||  == "t" ||  == "T" {
				if .quadReflectionPoint != nil {
					,  = (*.quadReflectionPoint)[0], (*.quadReflectionPoint)[1]
				}
			}
			 := 1 * (1 + .Roughness*0.2)
			 := 1.5 * (1 + .Roughness*0.22)
			 = append(, Op{Op: OpMove, Data: []float64{
				.x() + offsetOpt(, ), .y() + offsetOpt(, ),
			}})
			 := Point{ + offsetOpt(, ),  + offsetOpt(, )}
			 = append(, Op{Op: OpQCurveTo, Data: []float64{
				 + offsetOpt(, ),  + offsetOpt(, ), [0], [1],
			}})
			 = append(, Op{Op: OpMove, Data: []float64{
				.x() + offsetOpt(, ), .y() + offsetOpt(, ),
			}})
			 = Point{ + offsetOpt(, ),  + offsetOpt(, )}
			 = append(, Op{Op: OpQCurveTo, Data: []float64{
				 + offsetOpt(, ),  + offsetOpt(, ), [0], [1],
			}})
			.setPosition([0], [1])
			 := Point{ + ( - ),  + ( - )}
			.quadReflectionPoint = &
		}
	case "A", "a":
		 := .key == "a"
		if len(.data) >= 7 {
			,  := .data[0], .data[1]
			 := .data[2]
			 := .data[3]
			 := .data[4]
			,  := .data[5], .data[6]
			if  {
				 += .x()
				 += .y()
			}
			if  == .x() &&  == .y() {
				break
			}
			if  == 0 ||  == 0 {
				 = append(, doubleLine(.x(), .y(), , , )...)
				.setPosition(, )
			} else {
				 := newRoughArcConverter(
					Point{.x(), .y()}, Point{, }, Point{, }, ,
					jsTruthyNumber(), jsTruthyNumber(),
				)
				for  := .getNextSegment();  != nil;  = .getNextSegment() {
					 = append(, bezierTo(
						.cp1[0], .cp1[1],
						.cp2[0], .cp2[1],
						.to[0], .to[1],
						, ,
					)...)
				}
			}
		}
	}
	return 
}

func jsTruthyNumber( float64) bool {
	return  != 0 && !math.IsNaN()
}

func jsOr(,  float64) float64 {
	if jsTruthyNumber() {
		return 
	}
	return 
}