-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathshapefilter.go
208 lines (172 loc) · 6.16 KB
/
shapefilter.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package resolv
import (
"reflect"
"sort"
)
// ShapeIterator is an interface that defines a method to iterate through Shapes.
// Any object that has such a function (e.g. a ShapeFilter or a ShapeCollection (which is essentially just a slice of Shapes)) fulfills the ShapeIterator interface.
type ShapeIterator interface {
// ForEach is a function that can iterate through a collection of Shapes, controlled by a function's return value.
// If the function returns true, the iteration continues to the end. If it returns false, the iteration ends.
ForEach(iterationFunction func(shape IShape) bool)
}
// ShapeFilter is a selection of Shapes, primarily used to filter them out to select only some (i.e. Shapes with specific tags or placement).
// Usually one would use a ShapeFilter to select Shapes that are near a moving Shape (e.g. the player character).
type ShapeFilter struct {
Filters []func(s IShape) bool
operatingOn ShapeIterator
}
// ForEach is a function that can run a customizeable function on each Shape contained within the filter.
// If the shape passes the filters, the forEachFunc will run with the shape as an argument.
// If the function returns true, the iteration will continue; if it doesn't, the iteration will end.
func (s ShapeFilter) ForEach(forEachFunc func(shape IShape) bool) {
s.operatingOn.ForEach(func(shape IShape) bool {
for _, f := range s.Filters {
if !f(shape) {
return true
}
}
if !forEachFunc(shape) {
return true
}
return true
})
}
// ByTags adds a filter to the ShapeFilter that filters out Shapes by tags (so only Shapes that have the specified Tag(s) pass the filter).
// The function returns the ShapeFiler for easy method chaining.
func (s ShapeFilter) ByTags(tags Tags) ShapeFilter {
s.Filters = append(s.Filters, func(s IShape) bool {
return s.Tags().Has(tags)
})
return s
}
// NotByTags adds a filter to the ShapeFilter that filters out Shapes by tags (so only Shapes that DO NOT have the specified Tag(s) pass the filter).
// The function returns the ShapeFiler for easy method chaining.
func (s ShapeFilter) NotByTags(tags Tags) ShapeFilter {
s.Filters = append(s.Filters, func(s IShape) bool {
return !s.Tags().Has(tags)
})
return s
}
// ByDistance adds a filter to the ShapeFilter that filters out Shapes distance to a given point.
// The shapes have to be at least min and at most max distance from the given point Vector.
// The function returns the ShapeFiler for easy method chaining.
func (s ShapeFilter) ByDistance(point Vector, min, max float64) ShapeFilter {
s.Filters = append(s.Filters, func(s IShape) bool {
d := s.Position().Distance(point)
return d > min && d < max
})
return s
}
// ByFunc adds a filter to the ShapeFilter that filters out Shapes using a function if it returns true, the Shape passes the ShapeFilter.
// The function returns the ShapeFiler for easy method chaining.
func (s ShapeFilter) ByFunc(filterFunc func(s IShape) bool) ShapeFilter {
s.Filters = append(s.Filters, filterFunc)
return s
}
// ByDataType allows you to filter Shapes by their Data pointer's type. You could use this to, for example, filter out Shapes that have
// Data objects that are Updatable, where `Updatable` is an interface that has an `Update()` function call.
// To do this, you would call `s.ByDataType(reflect.TypeFor[Updatable]())`
func (s ShapeFilter) ByDataType(dataType reflect.Type) ShapeFilter {
if dataType == nil {
return s
}
s.Filters = append(s.Filters, func(s IShape) bool {
if s.Data() != nil {
return reflect.TypeOf(s.Data()).Implements(dataType)
}
return false
})
return s
}
// Not adds a filter to the ShapeFilter that specifcally does not allow specified Shapes in.
// The function returns the ShapeFiler for easy method chaining.
func (s ShapeFilter) Not(shapes ...IShape) ShapeFilter {
s.Filters = append(s.Filters, func(s IShape) bool {
for _, shape := range shapes {
if shape == s {
return false
}
}
return true
})
return s
}
// Shapes returns all shapes that pass the filters as a ShapeCollection.
func (s ShapeFilter) Shapes() ShapeCollection {
collection := ShapeCollection{}
s.ForEach(func(shape IShape) bool {
collection = append(collection, shape)
return true
})
return collection
}
// First returns the first shape that passes the ShapeFilter.
func (s ShapeFilter) First() IShape {
var returnShape IShape
s.ForEach(func(shape IShape) bool {
returnShape = shape
return false
})
return returnShape
}
// Last returns the last shape that passes the ShapeFilter (which means it has to step through all possible options before returning the last one).
func (s ShapeFilter) Last() IShape {
var returnShape IShape
s.ForEach(func(shape IShape) bool {
returnShape = shape
return true
})
return returnShape
}
// First returns the first shape in the ShapeCollection.
func (s ShapeCollection) First() IShape {
if len(s) > 0 {
return s[0]
}
return nil
}
// Last returns the last shape in the ShapeCollection.
func (s ShapeCollection) Last() IShape {
if len(s) > 0 {
return s[len(s)-1]
}
return nil
}
// Count returns the number of shapes that pass the filters as a ShapeCollection.
func (s ShapeFilter) Count() int {
count := 0
s.ForEach(func(shape IShape) bool {
count++
return true
})
return count
}
// ShapeCollection is a slice of Shapes.
type ShapeCollection []IShape
// ForEach allows you to iterate through each shape in the ShapeCollection; if the function returns false, the iteration ends.
func (s ShapeCollection) ForEach(forEachFunc func(shape IShape) bool) {
for _, shape := range s {
if !forEachFunc(shape) {
break
}
}
}
// SetTags sets the tag(s) on all Shapes present in the Shapecollection.
func (s ShapeCollection) SetTags(tags Tags) {
for _, shape := range s {
shape.Tags().Set(tags)
}
}
// UnsetTags unsets the tag(s) on all Shapes present in the Shapecollection.
func (s ShapeCollection) UnsetTags(tags Tags) {
for _, shape := range s {
shape.Tags().Unset(tags)
}
}
// SortByDistance sorts the ShapeCollection by distance to the given point.
func (s ShapeCollection) SortByDistance(point Vector) {
sort.Slice(s, func(i, j int) bool {
return s[i].Position().DistanceSquared(point) < s[j].Position().DistanceSquared(point)
})
}