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
|
||||
|
||||
- globalCompositeOperation
|
||||
- imageSmoothingEnabled
|
||||
- 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)
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
} 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 {
|
||||
|
|
8
paths.go
8
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
|
||||
})
|
||||
|
|
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue