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
|
- 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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
58
path2d.go
58
path2d.go
|
@ -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
|
||||||
|
}
|
||||||
|
|
29
paths.go
29
paths.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue