diff --git a/README.md b/README.md index 1e70dde..03af53f 100644 --- a/README.md +++ b/README.md @@ -126,4 +126,5 @@ These features *should* work just like their HTML5 counterparts, but there are l # Missing features - globalCompositeOperation +- imageSmoothingEnabled - textBaseline hanging and ideographic (currently work just like top and bottom) diff --git a/canvas_test.go b/canvas_test.go index 92ff535..f0e3c5f 100644 --- a/canvas_test.go +++ b/canvas_test.go @@ -584,3 +584,26 @@ func TestShadow(t *testing.T) { cv.FillRect(50, 15, 30, 60) }) } + +func TestReadme(t *testing.T) { + run(t, func(cv *canvas.Canvas) { + w, h := 100.0, 100.0 + cv.SetFillStyle("#000") + cv.FillRect(0, 0, w, h) + + for r := 0.0; r < math.Pi*2; r += math.Pi * 0.1 { + cv.SetFillStyle(int(r*10), int(r*20), int(r*40)) + cv.BeginPath() + cv.MoveTo(w*0.5, h*0.5) + cv.Arc(w*0.5, h*0.5, math.Min(w, h)*0.4, r, r+0.1*math.Pi, false) + cv.ClosePath() + cv.Fill() + } + + cv.SetStrokeStyle("#FFF") + cv.SetLineWidth(10) + cv.BeginPath() + cv.Arc(w*0.5, h*0.5, math.Min(w, h)*0.4, 0, math.Pi*2, false) + cv.Stroke() + }) +} diff --git a/path2d.go b/path2d.go index 49e4668..f62c66d 100644 --- a/path2d.go +++ b/path2d.go @@ -82,18 +82,20 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) { newp.flags |= pathIsConvex } else if prev.flags&pathIsConvex > 0 { cuts := false + var cutPoint vec if checkSelfIntersection && !Performance.IgnoreSelfIntersections { b0, b1 := prev.pos, vec{x, y} for i := 1; i < count; i++ { a0, a1 := p.p[i-1].pos, p.p[i].pos - _, r1, r2 := lineIntersection(a0, a1, b0, b1) + var r1, r2 float64 + cutPoint, r1, r2 = lineIntersection(a0, a1, b0, b1) if r1 > 0 && r1 < 1 && r2 > 0 && r2 < 1 { cuts = true break } } } - if cuts { + if cuts && !isSamePoint(cutPoint, vec{x, y}, samePointTolerance) { newp.flags |= pathSelfIntersects } else { prev2 := &p.p[len(p.p)-3] @@ -288,24 +290,39 @@ func (p *Path2D) Rect(x, y, w, h float64) { // func (p *Path2D) Ellipse(...) { // } -func runSubPaths(path []pathPoint, fn func(subPath []pathPoint) bool) { +func runSubPaths(path []pathPoint, close bool, fn func(subPath []pathPoint) bool) { start := 0 for i, p := range path { if p.flags&pathMove == 0 { continue } if i >= start+3 { - if fn(path[start:i]) { + end := i + if runSubPath(path[start:end], close, fn) { return } } start = i } if len(path) >= start+3 { - fn(path[start:]) + runSubPath(path[start:], close, fn) } } +func runSubPath(path []pathPoint, close bool, fn func(subPath []pathPoint) bool) bool { + if !close || path[0].pos == path[len(path)-1].pos { + return fn(path) + } + + var buf [64]pathPoint + path2 := Path2D{ + p: append(buf[:0], path...), + move: path[0].pos, + } + path2.lineTo(path[0].pos[0], path[0].pos[1], true) + return fn(path2.p) +} + type pathRule uint8 // Path rule constants. See https://en.wikipedia.org/wiki/Nonzero-rule @@ -319,7 +336,7 @@ const ( // to the given rule func (p *Path2D) IsPointInPath(x, y float64, rule pathRule) bool { inside := false - runSubPaths(p.p, func(sp []pathPoint) bool { + runSubPaths(p.p, false, func(sp []pathPoint) bool { num := 0 prev := sp[len(sp)-1].pos for _, pt := range p.p { diff --git a/paths.go b/paths.go index 615bcca..18d4b13 100644 --- a/paths.go +++ b/paths.go @@ -362,7 +362,8 @@ func (cv *Canvas) fillPath(path *Path2D, tf mat) { var triBuf [500][2]float64 tris := triBuf[:0] - runSubPaths(path.p, func(sp []pathPoint) bool { + + runSubPaths(path.p, true, func(sp []pathPoint) bool { tris = appendSubPathTriangles(tris, tf, sp) return false }) @@ -381,6 +382,9 @@ func appendSubPathTriangles(tris [][2]float64, mat mat, path []pathPoint) [][2]f if last.flags&pathIsConvex != 0 { p0, p1 := path[0].pos.mulMat(mat), path[1].pos.mulMat(mat) last := len(path) + if path[0].pos == path[last-1].pos { + last-- + } for i := 2; i < last; i++ { p2 := path[i].pos.mulMat(mat) tris = append(tris, p0, p1, p2) @@ -410,7 +414,7 @@ func (cv *Canvas) clip(path *Path2D, tf mat) { var triBuf [500][2]float64 tris := triBuf[:0] - runSubPaths(path.p, func(sp []pathPoint) bool { + runSubPaths(path.p, true, func(sp []pathPoint) bool { tris = appendSubPathTriangles(tris, tf, sp) return false }) diff --git a/testdata/Readme.png b/testdata/Readme.png new file mode 100755 index 0000000..fb6dde0 Binary files /dev/null and b/testdata/Readme.png differ diff --git a/triangulation.go b/triangulation.go index 7c1763b..b5eeadf 100644 --- a/triangulation.go +++ b/triangulation.go @@ -85,6 +85,10 @@ func polygonContainsPoint(polygon []vec, p vec) bool { } func triangulatePath(path []pathPoint, mat mat, target [][2]float64) [][2]float64 { + if path[0].pos == path[len(path)-1].pos { + path = path[:len(path)-1] + } + var buf [500]vec polygon := buf[:0] for _, p := range path { @@ -345,9 +349,10 @@ func setPathLeftRightInside(net *tessNet) { } func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool) { - runSubPaths(p, func(sp1 []pathPoint) bool { + runSubPaths(p, false, func(sp1 []pathPoint) bool { net := cutIntersections(sp1) if net.verts == nil { + partFn(sp1) return false }