110 lines
2.4 KiB
Go
110 lines
2.4 KiB
Go
package geom
|
|
|
|
import "log"
|
|
|
|
// PointsF is a set of points.
|
|
type PointsF []PointF
|
|
|
|
// PolF creates a polygon of points q.
|
|
func PolF(q ...PointF) PolygonF { return PolygonF{Points: q} }
|
|
|
|
// PolygonF is defined by a set of points (floating point).
|
|
type PolygonF struct {
|
|
Points PointsF
|
|
}
|
|
|
|
// Add adds q as a vector to all points in p.
|
|
func (p PolygonF) Add(q PointF) PolygonF {
|
|
var r = p.copy()
|
|
for i, pt := range r.Points {
|
|
r.Points[i] = pt.Add(q)
|
|
}
|
|
return r
|
|
}
|
|
|
|
func (p PolygonF) copy() PolygonF {
|
|
var q = PolygonF{make(PointsF, len(p.Points))}
|
|
copy(q.Points, p.Points)
|
|
return q
|
|
}
|
|
|
|
// Extend creates a new polygon based on p with one or more extra points q.
|
|
func (p PolygonF) Extend(q ...PointF) PolygonF {
|
|
var t = PolygonF{make(PointsF, len(p.Points)+len(q))}
|
|
copy(t.Points, p.Points)
|
|
copy(t.Points[len(p.Points):], q)
|
|
return t
|
|
}
|
|
|
|
// Mul multiplies the points of polygon p with t and returns the result.
|
|
func (p PolygonF) Mul(t float64) PolygonF {
|
|
var q = p.copy()
|
|
for i, pt := range q.Points {
|
|
q.Points[i] = pt.Mul(t)
|
|
}
|
|
return q
|
|
}
|
|
|
|
// Triangulate triangulates the polygon p.
|
|
func (p PolygonF) Triangulate() []TriangleF {
|
|
var triangles []TriangleF
|
|
points := p.copy().Points[:]
|
|
n := len(points)
|
|
|
|
if n > 0 && points[0] == points[n-1] { // remove first point if polygon is closed
|
|
points = points[1:]
|
|
n--
|
|
}
|
|
|
|
triangle := func(i int) TriangleF {
|
|
return TrF(points[(i+n-1)%n], points[i], points[(i+1)%n])
|
|
}
|
|
ear := func(i int) (TriangleF, bool) {
|
|
t := triangle(i)
|
|
if t.Winding() == WindingCounterClockwise {
|
|
return TriangleF{}, false
|
|
}
|
|
for j := 0; j < n-3; j++ {
|
|
p := points[(i+2+j)%n]
|
|
if t.Contains(p) {
|
|
return TriangleF{}, false
|
|
}
|
|
}
|
|
return t, true
|
|
}
|
|
|
|
for n >= 3 {
|
|
leastSharp := -1
|
|
var leastSharpAngle float64
|
|
for i := range points {
|
|
if t, ok := ear(i); ok {
|
|
sharpAngle := t.SmallestAngle()
|
|
if leastSharp < 0 || sharpAngle > leastSharpAngle {
|
|
leastSharp = i
|
|
leastSharpAngle = sharpAngle
|
|
}
|
|
}
|
|
}
|
|
if leastSharp < 0 {
|
|
if n >= 3 {
|
|
log.Println("not fully triangulated")
|
|
}
|
|
break
|
|
}
|
|
triangles = append(triangles, triangle(leastSharp))
|
|
points = append(points[:leastSharp], points[leastSharp+1:]...)
|
|
n = len(points)
|
|
}
|
|
return triangles
|
|
}
|
|
|
|
// Reverse reverses the order of the points of polygon p.
|
|
func (p PolygonF) Reverse() PolygonF {
|
|
n := len(p.Points)
|
|
var q = PolygonF{make(PointsF, n)}
|
|
for i := 0; i < n/2; i++ {
|
|
q.Points[i], q.Points[n-i-1] = p.Points[n-i-1], p.Points[i]
|
|
}
|
|
return q
|
|
}
|