Skip to content

Commit

Permalink
fixed a problem with closed paths and self intersecting polygons
Browse files Browse the repository at this point in the history
added the readme example as a test
  • Loading branch information
tfriedel6 committed May 12, 2019
1 parent aa059cf commit a4826a3
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
23 changes: 23 additions & 0 deletions canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
}
29 changes: 23 additions & 6 deletions path2d.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
8 changes: 6 additions & 2 deletions paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand All @@ -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)
Expand Down Expand Up @@ -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
})
Expand Down
Binary file added testdata/Readme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion triangulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down

0 comments on commit a4826a3

Please sign in to comment.