85 lines
2.6 KiB
Go
85 lines
2.6 KiB
Go
package area
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
)
|
|
|
|
var ErrInvalidGeometry = errors.New("区域几何不符合要求")
|
|
|
|
func validateGeometry(kind, direction string, points []Point) error {
|
|
if kind != KindPolygon && kind != KindDirectionLine {
|
|
return ErrInvalidGeometry
|
|
}
|
|
if (kind == KindPolygon && (len(points) < 3 || len(points) > 64)) || (kind == KindDirectionLine && len(points) != 2) {
|
|
return ErrInvalidGeometry
|
|
}
|
|
if kind == KindDirectionLine && direction != DirectionForward && direction != DirectionReverse {
|
|
return ErrInvalidGeometry
|
|
}
|
|
if kind == KindPolygon && direction != "" {
|
|
return ErrInvalidGeometry
|
|
}
|
|
for i, point := range points {
|
|
if math.IsNaN(point.X) || math.IsNaN(point.Y) || math.IsInf(point.X, 0) || math.IsInf(point.Y, 0) || point.X < 0 || point.X > 1 || point.Y < 0 || point.Y > 1 {
|
|
return ErrInvalidGeometry
|
|
}
|
|
if i > 0 && samePoint(point, points[i-1]) {
|
|
return ErrInvalidGeometry
|
|
}
|
|
}
|
|
if kind == KindDirectionLine {
|
|
if samePoint(points[0], points[1]) {
|
|
return ErrInvalidGeometry
|
|
}
|
|
return nil
|
|
}
|
|
if samePoint(points[0], points[len(points)-1]) || math.Abs(polygonArea(points)) < 0.000001 || polygonSelfIntersects(points) {
|
|
return ErrInvalidGeometry
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func samePoint(a, b Point) bool {
|
|
return math.Abs(a.X-b.X) < 0.0000001 && math.Abs(a.Y-b.Y) < 0.0000001
|
|
}
|
|
|
|
func polygonArea(points []Point) float64 {
|
|
area := 0.0
|
|
for i := range points {
|
|
next := points[(i+1)%len(points)]
|
|
area += points[i].X*next.Y - next.X*points[i].Y
|
|
}
|
|
return area / 2
|
|
}
|
|
|
|
func polygonSelfIntersects(points []Point) bool {
|
|
for i := range points {
|
|
a1, a2 := points[i], points[(i+1)%len(points)]
|
|
for j := i + 1; j < len(points); j++ {
|
|
if j == i || j == (i+1)%len(points) || i == (j+1)%len(points) {
|
|
continue
|
|
}
|
|
b1, b2 := points[j], points[(j+1)%len(points)]
|
|
if segmentsIntersect(a1, a2, b1, b2) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func segmentsIntersect(a, b, c, d Point) bool {
|
|
orientation := func(p, q, r Point) float64 {
|
|
return (q.Y-p.Y)*(r.X-q.X) - (q.X-p.X)*(r.Y-q.Y)
|
|
}
|
|
o1, o2, o3, o4 := orientation(a, b, c), orientation(a, b, d), orientation(c, d, a), orientation(c, d, b)
|
|
if ((o1 > 0 && o2 < 0) || (o1 < 0 && o2 > 0)) && ((o3 > 0 && o4 < 0) || (o3 < 0 && o4 > 0)) {
|
|
return true
|
|
}
|
|
onSegment := func(p, q, r Point) bool {
|
|
return q.X <= math.Max(p.X, r.X)+0.0000001 && q.X >= math.Min(p.X, r.X)-0.0000001 && q.Y <= math.Max(p.Y, r.Y)+0.0000001 && q.Y >= math.Min(p.Y, r.Y)-0.0000001
|
|
}
|
|
return (math.Abs(o1) < 0.0000001 && onSegment(a, c, b)) || (math.Abs(o2) < 0.0000001 && onSegment(a, d, b)) || (math.Abs(o3) < 0.0000001 && onSegment(c, a, d)) || (math.Abs(o4) < 0.0000001 && onSegment(c, b, d))
|
|
}
|