2022-11-03 13:54:49 +00:00
|
|
|
package geo
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Intersectable interface {
|
|
|
|
|
Intersections(segment Segment) []*Point
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Segment struct {
|
|
|
|
|
Start *Point
|
|
|
|
|
End *Point
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSegment(from, to *Point) *Segment {
|
|
|
|
|
return &Segment{from, to}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s Segment) Overlaps(otherS Segment, isHorizontal bool, buffer float64) bool {
|
|
|
|
|
if isHorizontal {
|
2023-03-15 01:36:16 +00:00
|
|
|
if math.Min(s.Start.Y, s.End.Y)-math.Max(otherS.Start.Y, otherS.End.Y) >= buffer {
|
2022-11-03 13:54:49 +00:00
|
|
|
return false
|
|
|
|
|
}
|
2023-03-15 01:36:16 +00:00
|
|
|
if math.Min(otherS.Start.Y, otherS.End.Y)-math.Max(s.Start.Y, s.End.Y) >= buffer {
|
2022-11-03 13:54:49 +00:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
} else {
|
2023-03-15 01:36:16 +00:00
|
|
|
if math.Min(s.Start.X, s.End.X)-math.Max(otherS.Start.X, otherS.End.X) >= buffer {
|
2022-11-03 13:54:49 +00:00
|
|
|
return false
|
|
|
|
|
}
|
2023-03-15 01:36:16 +00:00
|
|
|
if math.Min(otherS.Start.X, otherS.End.X)-math.Max(s.Start.X, s.End.X) >= buffer {
|
2022-11-03 13:54:49 +00:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-14 21:11:51 +00:00
|
|
|
func (segment Segment) Intersects(otherSegment Segment) bool {
|
|
|
|
|
return IntersectionPoint(segment.Start, segment.End, otherSegment.Start, otherSegment.End) != nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 13:54:49 +00:00
|
|
|
//nolint:unused
|
|
|
|
|
func (s Segment) ToString() string {
|
|
|
|
|
return fmt.Sprintf("%v -> %v", s.Start.ToString(), s.End.ToString())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (segment Segment) Intersections(otherSegment Segment) []*Point {
|
|
|
|
|
point := IntersectionPoint(segment.Start, segment.End, otherSegment.Start, otherSegment.End)
|
|
|
|
|
if point == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return []*Point{point}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getBounds takes a segment and returns the floor and ceil of where it can shift to
|
|
|
|
|
// If there is no floor or ceiling, negative or positive infinity is used, respectively
|
|
|
|
|
// The direction is inferred, e.g. b/c the passed in segment is vertical, it's inferred we want horizontal bounds
|
|
|
|
|
// buffer says how close the segment can be, on both axes, to other segments given
|
|
|
|
|
// │ │
|
|
|
|
|
// │ │
|
|
|
|
|
// │ │
|
|
|
|
|
// │ │
|
|
|
|
|
// │ non-overlap
|
|
|
|
|
// │
|
|
|
|
|
// │
|
|
|
|
|
// │
|
|
|
|
|
// │ segment
|
|
|
|
|
// │ │
|
|
|
|
|
// │ │ ceil
|
|
|
|
|
// │ │ │
|
|
|
|
|
// │ │
|
|
|
|
|
// floor │ │
|
|
|
|
|
// │
|
|
|
|
|
// │
|
|
|
|
|
// │
|
|
|
|
|
// │
|
|
|
|
|
// NOTE: the assumption is that all segments given are orthogonal
|
|
|
|
|
func (segment *Segment) GetBounds(segments []*Segment, buffer float64) (float64, float64) {
|
|
|
|
|
ceil := math.Inf(1)
|
|
|
|
|
floor := math.Inf(-1)
|
|
|
|
|
if segment.Start.X == segment.End.X && segment.Start.Y == segment.End.Y {
|
|
|
|
|
// single point, no segment
|
|
|
|
|
return floor, ceil
|
|
|
|
|
}
|
|
|
|
|
isHorizontal := segment.Start.X == segment.End.X
|
|
|
|
|
for _, otherSegment := range segments {
|
|
|
|
|
if isHorizontal {
|
|
|
|
|
// Exclude segments that don't overlap (non-overlap in above diagram)
|
|
|
|
|
if otherSegment.End.Y < segment.Start.Y-buffer {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if otherSegment.Start.Y > segment.End.Y+buffer {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if otherSegment.Start.X <= segment.Start.X {
|
|
|
|
|
floor = math.Max(floor, otherSegment.Start.X)
|
|
|
|
|
}
|
|
|
|
|
if otherSegment.Start.X > segment.Start.X {
|
|
|
|
|
ceil = math.Min(ceil, otherSegment.Start.X)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if otherSegment.End.X < segment.Start.X-buffer {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if otherSegment.Start.X > segment.End.X+buffer {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if otherSegment.Start.Y <= segment.Start.Y {
|
|
|
|
|
floor = math.Max(floor, otherSegment.Start.Y)
|
|
|
|
|
}
|
|
|
|
|
if otherSegment.Start.Y > segment.Start.Y {
|
|
|
|
|
ceil = math.Min(ceil, otherSegment.Start.Y)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return floor, ceil
|
|
|
|
|
}
|
2023-01-12 04:21:47 +00:00
|
|
|
|
|
|
|
|
func (segment Segment) Length() float64 {
|
|
|
|
|
return EuclideanDistance(segment.Start.X, segment.Start.Y, segment.End.X, segment.End.Y)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (segment Segment) ToVector() Vector {
|
|
|
|
|
return NewVector(segment.End.X-segment.Start.X, segment.End.Y-segment.Start.Y)
|
|
|
|
|
}
|