fixed a problem with closed paths and self intersecting polygons
added the readme example as a test
This commit is contained in:
parent
aa059cfcf3
commit
a4826a3e12
6 changed files with 59 additions and 9 deletions
|
@ -126,4 +126,5 @@ These features *should* work just like their HTML5 counterparts, but there are l
|
||||||
# Missing features
|
# Missing features
|
||||||
|
|
||||||
- globalCompositeOperation
|
- globalCompositeOperation
|
||||||
|
- imageSmoothingEnabled
|
||||||
- textBaseline hanging and ideographic (currently work just like top and bottom)
|
- textBaseline hanging and ideographic (currently work just like top and bottom)
|
||||||
|
|
|
@ -584,3 +584,26 @@ func TestShadow(t *testing.T) {
|
||||||
cv.FillRect(50, 15, 30, 60)
|
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
path2d.go
29
path2d.go
|
@ -82,18 +82,20 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
||||||
newp.flags |= pathIsConvex
|
newp.flags |= pathIsConvex
|
||||||
} else if prev.flags&pathIsConvex > 0 {
|
} else if prev.flags&pathIsConvex > 0 {
|
||||||
cuts := false
|
cuts := false
|
||||||
|
var cutPoint vec
|
||||||
if checkSelfIntersection && !Performance.IgnoreSelfIntersections {
|
if checkSelfIntersection && !Performance.IgnoreSelfIntersections {
|
||||||
b0, b1 := prev.pos, vec{x, y}
|
b0, b1 := prev.pos, vec{x, y}
|
||||||
for i := 1; i < count; i++ {
|
for i := 1; i < count; i++ {
|
||||||
a0, a1 := p.p[i-1].pos, p.p[i].pos
|
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 {
|
if r1 > 0 && r1 < 1 && r2 > 0 && r2 < 1 {
|
||||||
cuts = true
|
cuts = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cuts {
|
if cuts && !isSamePoint(cutPoint, vec{x, y}, samePointTolerance) {
|
||||||
newp.flags |= pathSelfIntersects
|
newp.flags |= pathSelfIntersects
|
||||||
} else {
|
} else {
|
||||||
prev2 := &p.p[len(p.p)-3]
|
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 (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
|
start := 0
|
||||||
for i, p := range path {
|
for i, p := range path {
|
||||||
if p.flags&pathMove == 0 {
|
if p.flags&pathMove == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i >= start+3 {
|
if i >= start+3 {
|
||||||
if fn(path[start:i]) {
|
end := i
|
||||||
|
if runSubPath(path[start:end], close, fn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
start = i
|
start = i
|
||||||
}
|
}
|
||||||
if len(path) >= start+3 {
|
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
|
type pathRule uint8
|
||||||
|
|
||||||
// Path rule constants. See https://en.wikipedia.org/wiki/Nonzero-rule
|
// Path rule constants. See https://en.wikipedia.org/wiki/Nonzero-rule
|
||||||
|
@ -319,7 +336,7 @@ const (
|
||||||
// to the given rule
|
// to the given rule
|
||||||
func (p *Path2D) IsPointInPath(x, y float64, rule pathRule) bool {
|
func (p *Path2D) IsPointInPath(x, y float64, rule pathRule) bool {
|
||||||
inside := false
|
inside := false
|
||||||
runSubPaths(p.p, func(sp []pathPoint) bool {
|
runSubPaths(p.p, false, func(sp []pathPoint) bool {
|
||||||
num := 0
|
num := 0
|
||||||
prev := sp[len(sp)-1].pos
|
prev := sp[len(sp)-1].pos
|
||||||
for _, pt := range p.p {
|
for _, pt := range p.p {
|
||||||
|
|
8
paths.go
8
paths.go
|
@ -362,7 +362,8 @@ func (cv *Canvas) fillPath(path *Path2D, tf mat) {
|
||||||
|
|
||||||
var triBuf [500][2]float64
|
var triBuf [500][2]float64
|
||||||
tris := triBuf[:0]
|
tris := triBuf[:0]
|
||||||
runSubPaths(path.p, func(sp []pathPoint) bool {
|
|
||||||
|
runSubPaths(path.p, true, func(sp []pathPoint) bool {
|
||||||
tris = appendSubPathTriangles(tris, tf, sp)
|
tris = appendSubPathTriangles(tris, tf, sp)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
@ -381,6 +382,9 @@ func appendSubPathTriangles(tris [][2]float64, mat mat, path []pathPoint) [][2]f
|
||||||
if last.flags&pathIsConvex != 0 {
|
if last.flags&pathIsConvex != 0 {
|
||||||
p0, p1 := path[0].pos.mulMat(mat), path[1].pos.mulMat(mat)
|
p0, p1 := path[0].pos.mulMat(mat), path[1].pos.mulMat(mat)
|
||||||
last := len(path)
|
last := len(path)
|
||||||
|
if path[0].pos == path[last-1].pos {
|
||||||
|
last--
|
||||||
|
}
|
||||||
for i := 2; i < last; i++ {
|
for i := 2; i < last; i++ {
|
||||||
p2 := path[i].pos.mulMat(mat)
|
p2 := path[i].pos.mulMat(mat)
|
||||||
tris = append(tris, p0, p1, p2)
|
tris = append(tris, p0, p1, p2)
|
||||||
|
@ -410,7 +414,7 @@ func (cv *Canvas) clip(path *Path2D, tf mat) {
|
||||||
|
|
||||||
var triBuf [500][2]float64
|
var triBuf [500][2]float64
|
||||||
tris := triBuf[:0]
|
tris := triBuf[:0]
|
||||||
runSubPaths(path.p, func(sp []pathPoint) bool {
|
runSubPaths(path.p, true, func(sp []pathPoint) bool {
|
||||||
tris = appendSubPathTriangles(tris, tf, sp)
|
tris = appendSubPathTriangles(tris, tf, sp)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
BIN
testdata/Readme.png
vendored
Executable file
BIN
testdata/Readme.png
vendored
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -85,6 +85,10 @@ func polygonContainsPoint(polygon []vec, p vec) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func triangulatePath(path []pathPoint, mat mat, target [][2]float64) [][2]float64 {
|
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
|
var buf [500]vec
|
||||||
polygon := buf[:0]
|
polygon := buf[:0]
|
||||||
for _, p := range path {
|
for _, p := range path {
|
||||||
|
@ -345,9 +349,10 @@ func setPathLeftRightInside(net *tessNet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool) {
|
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)
|
net := cutIntersections(sp1)
|
||||||
if net.verts == nil {
|
if net.verts == nil {
|
||||||
|
partFn(sp1)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue