-
Notifications
You must be signed in to change notification settings - Fork 425
/
segment.go
128 lines (117 loc) · 3.62 KB
/
segment.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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 {
if math.Min(s.Start.Y, s.End.Y)-math.Max(otherS.Start.Y, otherS.End.Y) >= buffer {
return false
}
if math.Min(otherS.Start.Y, otherS.End.Y)-math.Max(s.Start.Y, s.End.Y) >= buffer {
return false
}
return true
} else {
if math.Min(s.Start.X, s.End.X)-math.Max(otherS.Start.X, otherS.End.X) >= buffer {
return false
}
if math.Min(otherS.Start.X, otherS.End.X)-math.Max(s.Start.X, s.End.X) >= buffer {
return false
}
return true
}
}
func (segment Segment) Intersects(otherSegment Segment) bool {
return IntersectionPoint(segment.Start, segment.End, otherSegment.Start, otherSegment.End) != nil
}
//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
}
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)
}