-
Notifications
You must be signed in to change notification settings - Fork 3
/
region.go
138 lines (121 loc) · 3.27 KB
/
region.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
129
130
131
132
133
134
135
136
137
138
// Copyright 2013 Fredrik Ehnbom
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.
package text
import (
"fmt"
)
type (
// Defines a Region from point A to B.
// A can be less than B, in which case
// the selection is inverted.
Region struct {
A, B int
}
)
func (r Region) String() string {
return fmt.Sprintf("(%d, %d)", r.A, r.B)
}
// Returns the starting point of the region,
// that would be whichever of A and B
// is the minimal value.
func (r Region) Begin() int {
return Min(r.A, r.B)
}
// Returns the ending point of the region,
// that would be whichever of A and B
// is the maximum value.
func (r Region) End() int {
return Max(r.A, r.B)
}
// Returns whether the region contains the given
// point or not.
func (r Region) Contains(point int) bool {
return point >= r.Begin() && point <= r.End()
}
// Returns whether the region fully covers the argument region
func (r Region) Covers(r2 Region) bool {
return r.Contains(r2.Begin()) && r2.End() <= r.End()
}
// Returns whether the region is empty or not
func (r Region) Empty() bool {
return r.A == r.B
}
// Returns the size of the region
func (r Region) Size() int {
return r.End() - r.Begin()
}
// Returns a region covering both regions
func (r Region) Cover(other Region) Region {
if r.A <= r.B {
return Region{Min(r.A, other.Begin()), Max(r.B, other.End())}
} else {
return Region{Max(r.A, other.End()), Min(r.B, other.Begin())}
}
}
// Clips this Region against the Region provided in the argument.
// That would be if any part of this region is inside of the
// region specified by the argument, that part of the region
// is removed from this region.
func (r Region) Clip(other Region) (ret Region) {
if other.Covers(r) {
// this region is a subregion within the other region
return r
}
ret.A, ret.B = r.Begin(), r.End()
other.A, other.B = other.Begin(), other.End()
if ret.A >= other.A && ret.A < other.B {
ret.A = other.B
}
if ret.B >= other.A && ret.B <= other.B {
ret.B = other.A
}
if ret.B < ret.A {
ret.B = ret.A
}
return
}
// Cuts away the parts of the region that is in the argument region.
// This is similar to Clip, except that the result can be multiple
// regions.
func (r Region) Cut(other Region) (ret []Region) {
if r.Contains(other.A) {
ret = append(ret, Region{r.A, other.A})
}
if r.Contains(other.B) {
ret = append(ret, Region{other.B, r.B})
}
if len(ret) == 0 && !other.Covers(r) {
ret = append(ret, r)
}
return
}
// Returns whether the two regions intersects or not
func (r Region) Intersects(other Region) bool {
return r == other || r.Intersection(other).Size() > 0
}
// Returns the Region that is the intersection of the two
// regions given
func (r Region) Intersection(other Region) (ret Region) {
if !r.Contains(other.Begin()) && !other.Contains(r.Begin()) {
return
}
r2 := Region{Max(r.Begin(), other.Begin()), Min(r.End(), other.End())}
if r2.Size() != 0 {
ret = r2
}
return
}
// Adjusts the region in place for the given position and delta
func (r *Region) Adjust(position, delta int) {
if r.A >= position {
r.A += delta
} else if diff := position + delta - r.A; diff < 0 {
r.A += diff
}
if r.B >= position {
r.B += delta
} else if diff := position + delta - r.B; diff < 0 {
r.B += diff
}
}