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
- shadowOffset(X/Y)
- shadowBlur
- isPointInPath
# Missing features
- globalCompositeOperation
- isPointInPath
- isPointInStroke
- textBaseline hanging and ideographic (currently work just like top and bottom)
- full self intersecting polygon support

View file

@ -454,3 +454,9 @@ func (cv *Canvas) SetShadowOffset(x, y float64) {
func (cv *Canvas) SetShadowBlur(r float64) {
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 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
tris := buildFillTriangles(path, triBuf[:0])
tris := triBuf[:0]
runSubPaths(path, func(sp []pathPoint) bool {
tris = appendSubPathTriangles(tris, sp)
return false
})
if len(tris) == 0 {
return
}
@ -349,23 +353,6 @@ func (cv *Canvas) FillPath(path *Path2D) {
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 {
last := path[len(path)-1]
if last.flags&pathIsConvex != 0 {
@ -397,7 +384,11 @@ func (cv *Canvas) clip(path *Path2D) {
}
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 {
return
}

View file

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