From 65ebe6af69ab7f5aedceb88a6d0ff27be7127f27 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Thu, 21 Feb 2019 11:03:38 +0100 Subject: [PATCH] arc transformation fix --- math.go | 14 +++++++++++++ path2d.go | 59 ++++++++++++++++++++++++++++++++++++++++--------------- paths.go | 5 ++++- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/math.go b/math.go index 2b11098..6068449 100644 --- a/math.go +++ b/math.go @@ -34,6 +34,10 @@ func (v vec) mulMat(m mat) (vec, float64) { m[2]*v[0] + m[5]*v[1] + m[8] } +func (v vec) mulMat2(m mat2) vec { + return vec{m[0]*v[0] + m[2]*v[1], m[1]*v[0] + m[3]*v[1]} +} + func (v1 vec) div(v2 vec) vec { return vec{v1[0] / v2[0], v1[1] / v2[1]} } @@ -139,3 +143,13 @@ func (m mat) f32() [9]float32 { float32(m[3]), float32(m[4]), float32(m[5]), float32(m[6]), float32(m[7]), float32(m[8])} } + +func (m mat) mat2() mat2 { + return mat2{m[0], m[1], m[3], m[4]} +} + +type mat2 [4]float64 + +func (m *mat2) String() string { + return fmt.Sprintf("[%f,%f,\n %f,%f]", m[0], m[2], m[1], m[3]) +} diff --git a/path2d.go b/path2d.go index 225ea70..510b0af 100644 --- a/path2d.go +++ b/path2d.go @@ -1,6 +1,8 @@ package canvas -import "math" +import ( + "math" +) type Path2D struct { p []pathPoint @@ -113,27 +115,43 @@ func (p *Path2D) Arc(x, y, radius, startAngle, endAngle float64, anticlockwise b lastWasMove := len(p.p) == 0 || p.p[len(p.p)-1].flags&pathMove != 0 - startAngle = math.Mod(startAngle, math.Pi*2) - if startAngle < 0 { - startAngle += math.Pi * 2 + if endAngle == startAngle { + s, c := math.Sincos(endAngle) + p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) + + if lastWasMove { + p.p[len(p.p)-1].flags |= pathIsConvex + } + + return } - endAngle = math.Mod(endAngle, math.Pi*2) - if endAngle < 0 { - endAngle += math.Pi * 2 + + if (!anticlockwise && endAngle < startAngle) || (anticlockwise && endAngle > startAngle) { + endAngle, startAngle = startAngle, endAngle } - if !anticlockwise && endAngle <= startAngle { - endAngle += math.Pi * 2 - } else if anticlockwise && endAngle >= startAngle { - endAngle -= math.Pi * 2 + + 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 / 360 - if anticlockwise { - for a := startAngle; a > endAngle; a -= step { + + const step = math.Pi * 2 / 90 + if !anticlockwise { + for a := startAngle; a < endAngle; a += step { s, c := math.Sincos(a) p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) } } else { - for a := startAngle; a < endAngle; a += step { + for a := startAngle; a > endAngle; a -= step { s, c := math.Sincos(a) p.lineTo(x+radius*c, y+radius*s, checkSelfIntersection) } @@ -159,7 +177,7 @@ func (p *Path2D) ArcTo(x1, y1, x2, y2, radius float64) { p.LineTo(x2, y2) return } - // cv are the vectors orthogonal to the lines that point to the center of the circle + // cv0 and cv1 are vectors that point to the center of the circle cv0 := vec{-v0[1], v0[0]} cv1 := vec{v1[1], -v1[0]} x := cv1.sub(cv0).div(v0.sub(v1))[0] * radius @@ -169,6 +187,15 @@ func (p *Path2D) ArcTo(x1, y1, x2, y2, radius float64) { } center := p1.add(v0.mulf(math.Abs(x))).add(cv0.mulf(radius)) a0, a1 := cv0.mulf(-1).atan2(), cv1.mulf(-1).atan2() + if x > 0 { + if a1-a0 > 0 { + a0 += math.Pi * 2 + } + } else { + if a0-a1 > 0 { + a1 += math.Pi * 2 + } + } p.Arc(center[0], center[1], radius, a0, a1, x > 0) } diff --git a/paths.go b/paths.go index bf5c75c..ff5d856 100644 --- a/paths.go +++ b/paths.go @@ -34,7 +34,10 @@ func (cv *Canvas) LineTo(x, y float64) { // 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}) - cv.path.Arc(tf[0], tf[1], radius, startAngle, endAngle, anticlockwise) + 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) } // ArcTo adds to the current path by drawing a line toward x1/y1 and a circle