diff --git a/canvas.go b/canvas.go index 829a143..20929d0 100644 --- a/canvas.go +++ b/canvas.go @@ -141,7 +141,7 @@ func New(backend backendbase.Backend) *Canvas { cv.state.globalAlpha = 1 cv.state.fill.color = color.RGBA{A: 255} cv.state.stroke.color = color.RGBA{A: 255} - cv.state.transform = matIdentity() + cv.state.transform = matIdentity cv.path.cv = cv return cv } @@ -395,7 +395,7 @@ func (cv *Canvas) Restore() { cv.b.ClearClip() for _, st := range cv.stateStack { if len(st.clip.p) > 0 { - cv.clip(&st.clip, matIdentity()) + cv.clip(&st.clip, matIdentity) } } cv.state = cv.stateStack[l-1] diff --git a/math.go b/math.go index 118eafe..d4fc183 100644 --- a/math.go +++ b/math.go @@ -79,12 +79,10 @@ func (m *mat) String() string { return fmt.Sprintf("[%f,%f,0,\n %f,%f,0,\n %f,%f,1,]", m[0], m[2], m[4], m[1], m[3], m[5]) } -func matIdentity() mat { - return mat{ - 1, 0, - 0, 1, - 0, 0} -} +var matIdentity = mat{ + 1, 0, + 0, 1, + 0, 0} func matTranslate(v vec) mat { return mat{ diff --git a/path2d.go b/path2d.go index 16db526..75bdc0e 100644 --- a/path2d.go +++ b/path2d.go @@ -114,13 +114,21 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) { // Arc (see equivalent function on canvas type) func (p *Path2D) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool) { + p.arc(x, y, radius, startAngle, endAngle, anticlockwise, matIdentity, true) +} + +func (p *Path2D) arc(x, y, radius, startAngle, endAngle float64, anticlockwise bool, m mat, ident bool) { checkSelfIntersection := len(p.p) > 0 lastWasMove := len(p.p) == 0 || p.p[len(p.p)-1].flags&pathMove != 0 if endAngle == startAngle { s, c := math.Sincos(endAngle) - p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) + pt := vec{x + radius*c, y + radius*s} + if !ident { + pt = pt.mulMat(m) + } + p.lineTo(pt[0], pt[1], checkSelfIntersection) if lastWasMove { p.p[len(p.p)-1].flags |= pathIsConvex @@ -153,16 +161,28 @@ func (p *Path2D) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise b if !anticlockwise { for a := startAngle; a < endAngle; a += step { s, c := math.Sincos(a) - p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) + pt := vec{x + radius*c, y + radius*s} + if !ident { + pt = pt.mulMat(m) + } + p.lineTo(pt[0], pt[1], checkSelfIntersection) } } else { for a := startAngle; a > endAngle; a -= step { s, c := math.Sincos(a) - p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) + pt := vec{x + radius*c, y + radius*s} + if !ident { + pt = pt.mulMat(m) + } + p.lineTo(pt[0], pt[1], checkSelfIntersection) } } s, c := math.Sincos(endAngle) - p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) + pt := vec{x + radius*c, y + radius*s} + if !ident { + pt = pt.mulMat(m) + } + p.lineTo(pt[0], pt[1], checkSelfIntersection) if lastWasMove { p.p[len(p.p)-1].flags |= pathIsConvex @@ -171,10 +191,17 @@ func (p *Path2D) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise b // ArcTo (see equivalent function on canvas type) func (p *Path2D) ArcTo(x1, y1, x2, y2, radius float64) { + p.arcTo(x1, y1, x2, y2, radius, matIdentity, true) +} + +func (p *Path2D) arcTo(x1, y1, x2, y2, radius float64, m mat, ident bool) { if len(p.p) == 0 { return } p0, p1, p2 := p.p[len(p.p)-1].pos, vec{x1, y1}, vec{x2, y2} + if !ident { + p0 = p0.mulMat(m.invert()) + } v0, v1 := p0.sub(p1).norm(), p2.sub(p1).norm() angle := math.Acos(v0.dot(v1)) // should be in the range [0-pi]. if parallel, use a straight line @@ -201,7 +228,7 @@ func (p *Path2D) ArcTo(x1, y1, x2, y2, radius float64) { a1 += math.Pi * 2 } } - p.Arc(center[0], center[1], radius, a0, a1, x > 0) + p.arc(center[0], center[1], radius, a0, a1, x > 0, m, ident) } // QuadraticCurveTo (see equivalent function on canvas type) diff --git a/paths.go b/paths.go index fc567b5..80216c5 100644 --- a/paths.go +++ b/paths.go @@ -32,11 +32,10 @@ func (cv *Canvas) LineTo(x, y float64) { // is the radius, startAngle and endAngle are angles in radians, anticlockwise // means that the line is added anticlockwise func (cv *Canvas) Arc(x, y, radius, 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.Arc(tf[0], tf[1], radius, startAngle2, endAngle2, anticlockwise) + cv.path.arc(x, y, radius, startAngle2, endAngle2, anticlockwise, cv.state.transform, false) } // ArcTo adds to the current path by drawing a line toward x1/y1 and a circle @@ -44,9 +43,7 @@ func (cv *Canvas) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise // lines from the end of the path to x1/y1, and from x1/y1 to x2/y2. The line // will only go to where the circle segment would touch the latter line func (cv *Canvas) ArcTo(x1, y1, x2, y2, radius float64) { - tf1 := cv.tf(vec{x1, y1}) - tf2 := cv.tf(vec{x2, y2}) - cv.path.ArcTo(tf1[0], tf1[1], tf2[0], tf2[1], radius) + cv.path.arcTo(x1, y1, x2, y2, radius, cv.state.transform, false) } // QuadraticCurveTo adds a quadratic curve to the path. It uses the current end @@ -368,7 +365,7 @@ func linePointDistSqr(a, b, p vec) float64 { // Fill fills the current path with the current FillStyle func (cv *Canvas) Fill() { - cv.fillPath(&cv.path, matIdentity()) + cv.fillPath(&cv.path, matIdentity) } // FillPath fills the given path with the current FillStyle @@ -426,7 +423,7 @@ func appendSubPathTriangles(tris [][2]float64, mat mat, path []pathPoint) [][2]f // Clip uses the current path to clip any further drawing. Use Save/Restore to // remove the clipping again func (cv *Canvas) Clip() { - cv.clip(&cv.path, matIdentity()) + cv.clip(&cv.path, matIdentity) } func (cv *Canvas) clip(path *Path2D, tf mat) {