added a function to check if a point is in a path
This commit is contained in:
parent
e40654181e
commit
7c4521b227
5 changed files with 85 additions and 28 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
58
path2d.go
58
path2d.go
|
@ -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
|
||||
}
|
||||
|
|
29
paths.go
29
paths.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue