82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
package geom
|
|
|
|
import "image"
|
|
|
|
// PolygonContains reports if a polygon contains a point. The polygon must be in
|
|
// clockwise order if +Y is pointing upwards, or anticlockwise if +Y is pointing
|
|
// downwards.
|
|
func PolygonContains(polygon []image.Point, p image.Point) bool {
|
|
for i, q := range polygon {
|
|
r := polygon[(i+1)%len(polygon)]
|
|
// ∆(p q r) should have positive signed area
|
|
q, r = q.Sub(p), r.Sub(p)
|
|
if q.X*r.Y > r.X*q.Y {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// PolygonRectOverlap reports if a polygon overlaps a rectangle.
|
|
func PolygonRectOverlap(polygon []image.Point, rect image.Rectangle) bool {
|
|
if polygon[0].In(rect) {
|
|
return true
|
|
}
|
|
|
|
// Check if any vertex of the rect is inside the polygon.
|
|
if PolygonContains(polygon, rect.Min) {
|
|
return true
|
|
}
|
|
|
|
// Reduced Max (to the inclusive bound).
|
|
rmax := rect.Max.Sub(image.Pt(1, 1))
|
|
|
|
// Only remaining cases involve line intersection between the rect and poly
|
|
// having eliminated the possibility that one is entirely within another.
|
|
|
|
// Since rect is an axis-aligned rectangle, we only need vertical and
|
|
// horizontal line intersection tests.
|
|
// Walk each edge of polygon.
|
|
for i, p := range polygon {
|
|
q := polygon[(i+1)%len(polygon)]
|
|
// Pretend the edge is a rectangle. Exclude those that don't overlap.
|
|
if !rect.Overlaps(image.Rectangle{p, q}.Canon()) {
|
|
continue
|
|
}
|
|
|
|
d := q.Sub(p)
|
|
// If the polygon edge is not vertical, test left and right sides
|
|
if d.X != 0 {
|
|
if d.X < 0 {
|
|
d = d.Mul(-1)
|
|
}
|
|
min := (rect.Min.Y - p.Y) * d.X
|
|
max := (rect.Max.Y - p.Y) * d.X
|
|
// Test left side of rect
|
|
if t := (rect.Min.X - p.X) * d.Y; min <= t && t < max {
|
|
return true
|
|
}
|
|
// Test right side of rect
|
|
if t := (rmax.X - p.X) * d.Y; min <= t && t < max {
|
|
return true
|
|
}
|
|
}
|
|
// If the polygon edge is not horizontal, test the top and bottom sides
|
|
if d.Y != 0 {
|
|
if d.Y < 0 {
|
|
d = d.Mul(-1)
|
|
}
|
|
min := (rect.Min.X - p.X) * d.Y
|
|
max := (rect.Max.X - p.X) * d.Y
|
|
// Test top side of rect
|
|
if t := (rect.Min.Y - p.Y) * d.X; min <= t && t < max {
|
|
return true
|
|
}
|
|
// Test bottom side of rect
|
|
if t := (rmax.Y - p.Y) * d.X; min <= t && t < max {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|