78 lines
2.1 KiB
Go
78 lines
2.1 KiB
Go
package area
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
const epsilon = 1e-9
|
|
|
|
func ValidateGeometry(kind, direction string, points []Point) error {
|
|
for _, point := range points {
|
|
if point.X < 0 || point.X > 1 || point.Y < 0 || point.Y > 1 {
|
|
return fmt.Errorf("坐标必须位于画面范围内")
|
|
}
|
|
}
|
|
switch kind {
|
|
case KindPolygon:
|
|
if len(points) < 3 {
|
|
return fmt.Errorf("多边形至少需要 3 个点")
|
|
}
|
|
if math.Abs(polygonArea(points)) < epsilon {
|
|
return fmt.Errorf("多边形不能退化为直线")
|
|
}
|
|
if selfIntersects(points, true) {
|
|
return fmt.Errorf("多边形不能自交")
|
|
}
|
|
case KindLine:
|
|
if len(points) != 2 {
|
|
return fmt.Errorf("警戒线必须恰好包含 2 个点")
|
|
}
|
|
if distance(points[0], points[1]) < epsilon {
|
|
return fmt.Errorf("警戒线两点不能重合")
|
|
}
|
|
if direction != DirectionAToB && direction != DirectionBToA && direction != DirectionBoth {
|
|
return fmt.Errorf("警戒线方向不正确")
|
|
}
|
|
default:
|
|
return fmt.Errorf("区域类型不正确")
|
|
}
|
|
return nil
|
|
}
|
|
func polygonArea(points []Point) float64 {
|
|
sum := 0.0
|
|
for i := range points {
|
|
next := (i + 1) % len(points)
|
|
sum += points[i].X*points[next].Y - points[next].X*points[i].Y
|
|
}
|
|
return sum / 2
|
|
}
|
|
func distance(a, b Point) float64 { dx, dy := a.X-b.X, a.Y-b.Y; return dx*dx + dy*dy }
|
|
func selfIntersects(points []Point, closed bool) bool {
|
|
segments := len(points) - 1
|
|
if closed {
|
|
segments = len(points)
|
|
}
|
|
for i := 0; i < segments; i++ {
|
|
a1, a2 := points[i], points[(i+1)%len(points)]
|
|
if distance(a1, a2) < epsilon {
|
|
return true
|
|
}
|
|
for j := i + 1; j < segments; j++ {
|
|
if j == i+1 || closed && i == 0 && j == segments-1 {
|
|
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 {
|
|
o1, o2, o3, o4 := orientation(a, b, c), orientation(a, b, d), orientation(c, d, a), orientation(c, d, b)
|
|
return o1*o2 < 0 && o3*o4 < 0
|
|
}
|
|
func orientation(a, b, c Point) float64 { return (b.X-a.X)*(c.Y-a.Y) - (b.Y-a.Y)*(c.X-a.X) }
|