package pncdsl import "math" type Point struct { X, Y float64 } func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} } func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} } func (p Point) Dist(q Point) float64 { dx, dy := p.X-q.X, p.Y-q.Y return math.Sqrt(dx*dx + dy*dy) } // Shape is a hotspot area. Rectangle and Polygon both satisfy it. type Shape interface { Contains(p Point) bool Bounds() Rectangle } type Rectangle struct { X, Y, W, H float64 } func Rect(x, y, w, h float64) Rectangle { return Rectangle{X: x, Y: y, W: w, H: h} } func (r Rectangle) Contains(p Point) bool { return p.X >= r.X && p.X < r.X+r.W && p.Y >= r.Y && p.Y < r.Y+r.H } func (r Rectangle) Bounds() Rectangle { return r } func (r Rectangle) Center() Point { return Point{X: r.X + r.W/2, Y: r.Y + r.H/2} } type Polygon struct { Points []Point } func Poly(pts ...Point) Polygon { return Polygon{Points: pts} } func (g Polygon) Contains(pt Point) bool { n := len(g.Points) if n < 3 { return false } inside := false for i, j := 0, n-1; i < n; j, i = i, i+1 { pi, pj := g.Points[i], g.Points[j] if (pi.Y > pt.Y) != (pj.Y > pt.Y) && pt.X < (pj.X-pi.X)*(pt.Y-pi.Y)/(pj.Y-pi.Y)+pi.X { inside = !inside } } return inside } func (g Polygon) Bounds() Rectangle { if len(g.Points) == 0 { return Rectangle{} } minX, maxX := g.Points[0].X, g.Points[0].X minY, maxY := g.Points[0].Y, g.Points[0].Y for _, p := range g.Points[1:] { if p.X < minX { minX = p.X } if p.X > maxX { maxX = p.X } if p.Y < minY { minY = p.Y } if p.Y > maxY { maxY = p.Y } } return Rectangle{X: minX, Y: minY, W: maxX - minX, H: maxY - minY} }