diff --git a/path2d.go b/path2d.go index f62c66d..c8be2b4 100644 --- a/path2d.go +++ b/path2d.go @@ -253,6 +253,71 @@ func (p *Path2D) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) { p.LineTo(x3, y3) } +// Ellipse (see equivalent function on canvas type) +func (p *Path2D) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, anticlockwise bool) { + checkSelfIntersection := len(p.p) > 0 + + rs, rc := math.Sincos(rotation) + + lastWasMove := len(p.p) == 0 || p.p[len(p.p)-1].flags&pathMove != 0 + + if endAngle == startAngle { + s, c := math.Sincos(endAngle) + rx, ry := radiusX*c, radiusY*s + rx, ry = rx*rc-ry*rs, rx*rs+ry*rc + p.lineTo(x+rx, y+ry, checkSelfIntersection) + + if lastWasMove { + p.p[len(p.p)-1].flags |= pathIsConvex + } + + return + } + + if (!anticlockwise && endAngle < startAngle) || (anticlockwise && endAngle > startAngle) { + startAngle, endAngle = endAngle, startAngle + } + + if !anticlockwise { + diff := endAngle - startAngle + if diff >= math.Pi*4 { + diff = math.Mod(diff, math.Pi*2) + math.Pi*2 + endAngle = startAngle + diff + } + } else { + diff := startAngle - endAngle + if diff >= math.Pi*4 { + diff = math.Mod(diff, math.Pi*2) + endAngle = startAngle - diff + } + } + + const step = math.Pi * 2 / 90 + if !anticlockwise { + for a := startAngle; a < endAngle; a += step { + s, c := math.Sincos(a) + rx, ry := radiusX*c, radiusY*s + rx, ry = rx*rc-ry*rs, rx*rs+ry*rc + p.lineTo(x+rx, y+ry, checkSelfIntersection) + } + } else { + for a := startAngle; a > endAngle; a -= step { + s, c := math.Sincos(a) + rx, ry := radiusX*c, radiusY*s + rx, ry = rx*rc-ry*rs, rx*rs+ry*rc + p.lineTo(x+rx, y+ry, checkSelfIntersection) + } + } + s, c := math.Sincos(endAngle) + rx, ry := radiusX*c, radiusY*s + rx, ry = rx*rc-ry*rs, rx*rs+ry*rc + p.lineTo(x+rx, y+ry, checkSelfIntersection) + + if lastWasMove { + p.p[len(p.p)-1].flags |= pathIsConvex + } +} + // ClosePath (see equivalent function on canvas type) func (p *Path2D) ClosePath() { if len(p.p) < 2 { @@ -287,9 +352,6 @@ func (p *Path2D) Rect(x, y, w, h float64) { } } -// func (p *Path2D) Ellipse(...) { -// } - func runSubPaths(path []pathPoint, close bool, fn func(subPath []pathPoint) bool) { start := 0 for i, p := range path { diff --git a/paths.go b/paths.go index d123f3d..9cc8aa7 100644 --- a/paths.go +++ b/paths.go @@ -57,6 +57,19 @@ func (cv *Canvas) QuadraticCurveTo(x1, y1, x2, y2 float64) { cv.path.QuadraticCurveTo(tf1[0], tf1[1], tf2[0], tf2[1]) } +// Ellipse adds an ellipse segment to the end of the path. x/y is the center, +// radiusX is the major axis radius, radiusY is the minor axis radius, +// rotation is the rotation of the ellipse in radians, startAngle and endAngle +// are angles in radians, and anticlockwise means that the line is added +// anticlockwise +func (cv *Canvas) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, anticlockwise bool) { + tf := cv.tf(vec{x, y}) + ax, ay := math.Sincos(startAngle) + startAngle2 := vec{ay, ax}.mulMat2(cv.state.transform.mat2()).atan2() + endAngle2 := startAngle2 + (endAngle - startAngle) + cv.path.Ellipse(tf[0], tf[1], radiusX, radiusY, rotation, startAngle2, endAngle2, anticlockwise) +} + // BezierCurveTo adds a bezier curve to the path. It uses the current end point // of the path, x1/y1 and x2/y2 define the curve, and x3/y3 is the end point func (cv *Canvas) BezierCurveTo(x1, y1, x2, y2, x3, y3 float64) {