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)) }