added a function to check if a point is in a path

This commit is contained in:
Thomas Friedel 2019-04-10 11:48:22 +02:00
parent e40654181e
commit 7c4521b227
5 changed files with 85 additions and 28 deletions

View file

@ -119,11 +119,11 @@ These features *should* work just like their HTML5 counterparts, but there are l
- shadowColor - shadowColor
- shadowOffset(X/Y) - shadowOffset(X/Y)
- shadowBlur - shadowBlur
- isPointInPath
# Missing features # Missing features
- globalCompositeOperation - globalCompositeOperation
- isPointInPath
- isPointInStroke - isPointInStroke
- textBaseline hanging and ideographic (currently work just like top and bottom) - textBaseline hanging and ideographic (currently work just like top and bottom)
- full self intersecting polygon support - full self intersecting polygon support

View file

@ -454,3 +454,9 @@ func (cv *Canvas) SetShadowOffset(x, y float64) {
func (cv *Canvas) SetShadowBlur(r float64) { func (cv *Canvas) SetShadowBlur(r float64) {
cv.state.shadowBlur = r cv.state.shadowBlur = r
} }
// IsPointInPath returns true if the point is in the current
// path according to the given rule
func (cv *Canvas) IsPointInPath(x, y float64, rule pathRule) bool {
return cv.path.IsPointInPath(x, y, rule)
}

View file

@ -286,3 +286,61 @@ func (p *Path2D) Rect(x, y, w, h float64) {
// func (p *Path2D) Ellipse(...) { // func (p *Path2D) Ellipse(...) {
// } // }
func runSubPaths(path *Path2D, fn func(subPath []pathPoint) bool) {
start := 0
for i, p := range path.p {
if p.flags&pathMove == 0 {
continue
}
if i >= start+3 {
if fn(path.p[start:i]) {
return
}
}
start = i
}
if len(path.p) >= start+3 {
fn(path.p[start:])
}
}
type pathRule uint8
// Path rule constants. See https://en.wikipedia.org/wiki/Nonzero-rule
// and https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule
const (
NonZero pathRule = iota
EvenOdd
)
// IsPointInPath returns true if the point is in the path according
// to the given rule
func (p *Path2D) IsPointInPath(x, y float64, rule pathRule) bool {
inside := false
runSubPaths(p, func(sp []pathPoint) bool {
num := 0
prev := sp[len(sp)-1].pos
for _, pt := range p.p {
r, dir := pointIsRightOfLine(prev, pt.pos, vec{x, y})
prev = pt.pos
if !r {
continue
}
if dir {
num++
} else {
num--
}
}
if rule == NonZero {
inside = num != 0
} else {
inside = num%2 == 0
}
return inside
})
return inside
}

View file

@ -338,7 +338,11 @@ func (cv *Canvas) FillPath(path *Path2D) {
} }
var triBuf [500][2]float64 var triBuf [500][2]float64
tris := buildFillTriangles(path, triBuf[:0]) tris := triBuf[:0]
runSubPaths(path, func(sp []pathPoint) bool {
tris = appendSubPathTriangles(tris, sp)
return false
})
if len(tris) == 0 { if len(tris) == 0 {
return return
} }
@ -349,23 +353,6 @@ func (cv *Canvas) FillPath(path *Path2D) {
cv.b.Fill(&stl, tris) cv.b.Fill(&stl, tris)
} }
func buildFillTriangles(path *Path2D, tris [][2]float64) [][2]float64 {
start := 0
for i, p := range path.p {
if p.flags&pathMove == 0 {
continue
}
if i >= start+3 {
tris = appendSubPathTriangles(tris, path.p[start:i])
}
start = i
}
if len(path.p) >= start+3 {
tris = appendSubPathTriangles(tris, path.p[start:])
}
return tris
}
func appendSubPathTriangles(tris [][2]float64, path []pathPoint) [][2]float64 { func appendSubPathTriangles(tris [][2]float64, path []pathPoint) [][2]float64 {
last := path[len(path)-1] last := path[len(path)-1]
if last.flags&pathIsConvex != 0 { if last.flags&pathIsConvex != 0 {
@ -397,7 +384,11 @@ func (cv *Canvas) clip(path *Path2D) {
} }
var triBuf [500][2]float64 var triBuf [500][2]float64
tris := buildFillTriangles(path, triBuf[:0]) tris := triBuf[:0]
runSubPaths(path, func(sp []pathPoint) bool {
tris = appendSubPathTriangles(tris, sp)
return false
})
if len(tris) == 0 { if len(tris) == 0 {
return return
} }

View file

@ -5,20 +5,22 @@ import (
"sort" "sort"
) )
func pointIsRightOfLine(a, b, p vec) bool { func pointIsRightOfLine(a, b, p vec) (bool, bool) {
if a[1] == b[1] { if a[1] == b[1] {
return false return false, false
} }
dir := false
if a[1] > b[1] { if a[1] > b[1] {
a, b = b, a a, b = b, a
dir = !dir
} }
if p[1] < a[1] || p[1] > b[1] { if p[1] < a[1] || p[1] > b[1] {
return false return false, false
} }
v := b.sub(a) v := b.sub(a)
r := (p[1] - a[1]) / v[1] r := (p[1] - a[1]) / v[1]
x := a[0] + r*v[0] x := a[0] + r*v[0]
return p[0] > x return p[0] > x, dir
} }
func triangleContainsPoint(a, b, c, p vec) bool { func triangleContainsPoint(a, b, c, p vec) bool {
@ -38,13 +40,13 @@ func triangleContainsPoint(a, b, c, p vec) bool {
// check whether the point is to the right of each triangle line. // check whether the point is to the right of each triangle line.
// if the total is 1, it is inside the triangle // if the total is 1, it is inside the triangle
count := 0 count := 0
if pointIsRightOfLine(a, b, p) { if r, _ := pointIsRightOfLine(a, b, p); r {
count++ count++
} }
if pointIsRightOfLine(b, c, p) { if r, _ := pointIsRightOfLine(b, c, p); r {
count++ count++
} }
if pointIsRightOfLine(c, a, p) { if r, _ := pointIsRightOfLine(c, a, p); r {
count++ count++
} }
return count == 1 return count == 1
@ -54,7 +56,7 @@ func polygonContainsPoint(polygon []vec, p vec) bool {
a := polygon[len(polygon)-1] a := polygon[len(polygon)-1]
count := 0 count := 0
for _, b := range polygon { for _, b := range polygon {
if pointIsRightOfLine(a, b, p) { if r, _ := pointIsRightOfLine(a, b, p); r {
count++ count++
} }
a = b a = b