forked from kodecocodes/swift-algorithm-club
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQuadTree.swift
289 lines (234 loc) · 6.6 KB
/
QuadTree.swift
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
public struct Point {
let x: Double
let y: Double
public init(_ x: Double, _ y: Double) {
self.x = x
self.y = y
}
}
extension Point: CustomStringConvertible {
public var description: String {
return "Point(\(x), \(y))"
}
}
public struct Size: CustomStringConvertible {
var xLength: Double
var yLength: Double
public init(xLength: Double, yLength: Double) {
precondition(xLength >= 0, "xLength can not be negative")
precondition(yLength >= 0, "yLength can not be negative")
self.xLength = xLength
self.yLength = yLength
}
var half: Size {
return Size(xLength: xLength / 2, yLength: yLength / 2)
}
public var description: String {
return "Size(\(xLength), \(yLength))"
}
}
public struct Rect {
// left top vertice
var origin: Point
var size: Size
public init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
var minX: Double {
return origin.x
}
var minY: Double {
return origin.y
}
var maxX: Double {
return origin.x + size.xLength
}
var maxY: Double {
return origin.y + size.yLength
}
func containts(point: Point) -> Bool {
return (minX <= point.x && point.x <= maxX) &&
(minY <= point.y && point.y <= maxY)
}
var leftTopRect: Rect {
return Rect(origin: origin, size: size.half)
}
var leftBottomRect: Rect {
return Rect(origin: Point(origin.x, origin.y + size.half.yLength), size: size.half)
}
var rightTopRect: Rect {
return Rect(origin: Point(origin.x + size.half.xLength, origin.y), size: size.half)
}
var rightBottomRect: Rect {
return Rect(origin: Point(origin.x + size.half.xLength, origin.y + size.half.yLength), size: size.half)
}
func intersects(rect: Rect) -> Bool {
func lineSegmentsIntersect(lStart: Double, lEnd: Double, rStart: Double, rEnd: Double) -> Bool {
return max(lStart, rStart) <= min(lEnd, rEnd)
}
// to intersect, both horizontal and vertical projections need to intersect
// horizontal
if !lineSegmentsIntersect(lStart: minX, lEnd: maxX, rStart: rect.minX, rEnd: rect.maxX) {
return false
}
// vertical
return lineSegmentsIntersect(lStart: minY, lEnd: maxY, rStart: rect.minY, rEnd: rect.maxY)
}
}
extension Rect: CustomStringConvertible {
public var description: String {
return "Rect(\(origin), \(size))"
}
}
protocol PointsContainer {
func add(point: Point) -> Bool
func points(inRect rect: Rect) -> [Point]
}
class QuadTreeNode {
enum NodeType {
case leaf
case `internal`(children: Children)
}
struct Children: Sequence {
let leftTop: QuadTreeNode
let leftBottom: QuadTreeNode
let rightTop: QuadTreeNode
let rightBottom: QuadTreeNode
init(parentNode: QuadTreeNode) {
leftTop = QuadTreeNode(rect: parentNode.rect.leftTopRect)
leftBottom = QuadTreeNode(rect: parentNode.rect.leftBottomRect)
rightTop = QuadTreeNode(rect: parentNode.rect.rightTopRect)
rightBottom = QuadTreeNode(rect: parentNode.rect.rightBottomRect)
}
struct ChildrenIterator: IteratorProtocol {
private var index = 0
private let children: Children
init(children: Children) {
self.children = children
}
mutating func next() -> QuadTreeNode? {
defer { index += 1 }
switch index {
case 0: return children.leftTop
case 1: return children.leftBottom
case 2: return children.rightTop
case 3: return children.rightBottom
default: return nil
}
}
}
public func makeIterator() -> ChildrenIterator {
return ChildrenIterator(children: self)
}
}
var points: [Point] = []
let rect: Rect
var type: NodeType = .leaf
static let maxPointCapacity = 3
init(rect: Rect) {
self.rect = rect
}
var recursiveDescription: String {
return recursiveDescription(withTabCount: 0)
}
private func recursiveDescription(withTabCount count: Int) -> String {
let indent = String(repeating: "\t", count: count)
var result = "\(indent)" + description + "\n"
switch type {
case .internal(let children):
for child in children {
result += child.recursiveDescription(withTabCount: count + 1)
}
default:
break
}
return result
}
}
extension QuadTreeNode: PointsContainer {
@discardableResult
func add(point: Point) -> Bool {
if !rect.containts(point: point) {
return false
}
switch type {
case .internal(let children):
// pass the point to one of the children
for child in children {
if child.add(point: point) {
return true
}
}
fatalError("rect.containts evaluted to true, but none of the children added the point")
case .leaf:
points.append(point)
// if the max capacity was reached, become an internal node
if points.count == QuadTreeNode.maxPointCapacity {
subdivide()
}
}
return true
}
private func subdivide() {
switch type {
case .leaf:
type = .internal(children: Children(parentNode: self))
case .internal:
preconditionFailure("Calling subdivide on an internal node")
}
}
func points(inRect rect: Rect) -> [Point] {
// if the node's rect and the given rect don't intersect, return an empty array,
// because there can't be any points that lie the node's (or its children's) rect and
// in the given rect
if !self.rect.intersects(rect: rect) {
return []
}
var result: [Point] = []
// collect the node's points that lie in the rect
for point in points {
if rect.containts(point: point) {
result.append(point)
}
}
switch type {
case .leaf:
break
case .internal(let children):
// recursively add children's points that lie in the rect
for childNode in children {
result.append(contentsOf: childNode.points(inRect: rect))
}
}
return result
}
}
extension QuadTreeNode: CustomStringConvertible {
var description: String {
switch type {
case .leaf:
return "leaf \(rect) Points: \(points)"
case .internal:
return "parent \(rect) Points: \(points)"
}
}
}
public class QuadTree: PointsContainer {
let root: QuadTreeNode
public init(rect: Rect) {
self.root = QuadTreeNode(rect: rect)
}
@discardableResult
public func add(point: Point) -> Bool {
return root.add(point: point)
}
public func points(inRect rect: Rect) -> [Point] {
return root.points(inRect: rect)
}
}
extension QuadTree: CustomStringConvertible {
public var description: String {
return "Quad tree\n" + root.recursiveDescription
}
}