From 69db705d54cf905a361a7e36e75109bc228c5cd8 Mon Sep 17 00:00:00 2001 From: li3zhen1 Date: Sat, 14 Oct 2023 14:44:31 -0400 Subject: [PATCH 1/2] Change naming --- Sources/QuadTree/CompactQuadTree.swift | 20 +- Sources/QuadTree/NdTree.swift | 280 ++++++++++++------------- Sources/QuadTree/VectorLike.swift | 7 + Tests/QuadTreeTests/NdTreeTests.swift | 87 ++++---- 4 files changed, 197 insertions(+), 197 deletions(-) diff --git a/Sources/QuadTree/CompactQuadTree.swift b/Sources/QuadTree/CompactQuadTree.swift index 3bbf2cd..cbeb664 100644 --- a/Sources/QuadTree/CompactQuadTree.swift +++ b/Sources/QuadTree/CompactQuadTree.swift @@ -9,7 +9,6 @@ import simd extension simd_double2: VectorLike { - @inlinable public func distanceSquared(to: SIMD2) -> Scalar { return (self-to).lengthSquared() @@ -30,13 +29,20 @@ extension simd_double3: VectorLike { } } -public protocol CompactQuadTreeDelegate: NdTreeDelegate where Coordinate==simd_double2 { } -public protocol CompactOctTreeDelegate: NdTreeDelegate where Coordinate==simd_double3 { } + +public typealias Vector2d = simd_double2 +public typealias Vector3d = simd_double3 + + +public protocol CompactQuadTreeDelegate: NdTreeDelegate where Coordinate==Vector2d { } +public protocol CompactOctTreeDelegate: NdTreeDelegate where Coordinate==Vector3d { } + + +public typealias QuadBox = NdBox +public typealias OctBox = NdBox -public typealias CompactQuadTree = CompactNdTree -public typealias QuadBox = NdBox -public typealias CompactOctTree = CompactNdTree -public typealias OctBox = NdBox +public typealias CompactQuadTree = NdTree +public typealias CompactOctTree = NdTree #endif diff --git a/Sources/QuadTree/NdTree.swift b/Sources/QuadTree/NdTree.swift index a1d908f..11de2a6 100644 --- a/Sources/QuadTree/NdTree.swift +++ b/Sources/QuadTree/NdTree.swift @@ -1,6 +1,6 @@ // // File.swift -// +// // // Created by li3zhen1 on 10/10/23. // @@ -15,11 +15,10 @@ public struct NdBox where Coordinate: VectorLike { public var p0: Coordinate public var p1: Coordinate - - - @inlinable public init(p0:Coordinate, p1:Coordinate) { + + @inlinable public init(p0: Coordinate, p1: Coordinate) { #if DEBUG - assert(p0 != p1, "NdBox was initialized with 2 same anchor") + assert(p0 != p1, "NdBox was initialized with 2 same anchor") #endif var p0 = p0 var p1 = p1 @@ -32,26 +31,25 @@ public struct NdBox where Coordinate: VectorLike { self.p1 = p1 // TODO: use Mask } - - @inlinable internal init(pMin:Coordinate, pMax:Coordinate) { + + @inlinable internal init(pMin: Coordinate, pMax: Coordinate) { #if DEBUG - assert(pMin != pMax, "NdBox was initialized with 2 same anchor") + assert(pMin != pMax, "NdBox was initialized with 2 same anchor") #endif self.p0 = pMin self.p1 = pMax } - + @inlinable public init() { p0 = .zero p1 = .zero } - - public init(_ p0:Coordinate, _ p1: Coordinate) { - self.init(p0:p0, p1:p1) + + public init(_ p0: Coordinate, _ p1: Coordinate) { + self.init(p0: p0, p1: p1) } - -} +} extension NdBox { @inlinable var area: Coordinate.Scalar { @@ -62,12 +60,11 @@ extension NdBox { } return result } - + @inlinable var vec: Coordinate { p1 - p0 } - + @inlinable var center: Coordinate { (p1 + p0) / Coordinate.Scalar(2) } - - + @inlinable func contains(_ point: Coordinate) -> Bool { for i in point.indices { if p0[i] > point[i] || point[i] >= p1[i] { @@ -75,22 +72,20 @@ extension NdBox { } } return true -// return (p0 <= point) && (point < p1) + // return (p0 <= point) && (point < p1) } } - extension NdBox { @inlinable func getCorner(of direction: Int) -> Coordinate { var corner = Coordinate.zero for i in 0..>i) & 0b1)==1 ? p1[i] : p0[i] + corner[i] = ((direction >> i) & 0b1) == 1 ? p1[i] : p0[i] } return corner } } - public protocol NdTreeDelegate { typealias NodeIndex = Int typealias BoxStorageIndex = Int @@ -106,115 +101,118 @@ public protocol NdTreeDelegate { init() } +public final class NdTree +where V: VectorLike, TD: NdTreeDelegate, TD.Coordinate == V { + + public typealias Box = NdBox -public final class CompactNdTree where C:VectorLike, TD: NdTreeDelegate, TD.Coordinate==C { - - public typealias Box = NdBox - public typealias NodeIndex = Int - + public typealias BoxStorageIndex = Int - + private var directionCount: Int - + fileprivate struct BoxStorage { - // once initialized, should have C.entryCount elements + // once initialized, should have V.entryCount elements var childrenBoxStorageIndices: [BoxStorageIndex]? = nil var nodeIndices: [NodeIndex] - var box: Box // { didSet { center = box.center } } - + public var box: Box // { didSet { center = box.center } } + @inlinable init( box: Box, nodeIndices: [NodeIndex] = [] ) { self.nodeIndices = nodeIndices self.box = box -// self.center = box.center - + // self.center = box.center + } } - - private var nodePositions: [C] + + private var nodePositions: [V] private var boxStorages: [BoxStorage] - - private var clusterDistance: C.Scalar - private var clusterDistanceSquared: C.Scalar - - + + private var clusterDistance: V.Scalar + private var clusterDistanceSquared: V.Scalar + public private(set) var delegate: TD - + public init( initialBox: Box, estimatedNodeCount: Int, - clusterDistance: C.Scalar + clusterDistance: V.Scalar ) where TD: NdTreeDelegate { self.clusterDistance = clusterDistance - self.clusterDistanceSquared = clusterDistance*clusterDistance - self.directionCount = 1<> i) & 0b1 if isOnTheHigherRange != 0 { newChildren[j].box.p0[i] = pCenter[i] - newChildren[j].box.p1[i] = p1[i] - } - else { - newChildren[j].box.p0[i] = p0[i] + } else { newChildren[j].box.p1[i] = pCenter[i] } } } - + let currentBoxStorageCount = self.boxStorages.count - let _nodeIndices = boxStorages[boxStorageIndex].nodeIndices if !_nodeIndices.isEmpty { // put the indices to new @@ -222,29 +220,35 @@ public final class CompactNdTree where C:VectorLike, TD: NdTreeDelegate, nodePositions[_nodeIndices.first!], relativeTo: pCenter ) - + newChildren[index].nodeIndices = _nodeIndices - + for ni in _nodeIndices { - delegate.didAddNode(ni, at: nodePositions[ni], in: currentBoxStorageCount+index) + delegate.didAddNode( + ni, at: nodePositions[ni], in: currentBoxStorageCount + index) } - + // this node will not have children any more - boxStorages[boxStorageIndex].nodeIndices=[] + boxStorages[boxStorageIndex].nodeIndices = [] } self.boxStorages.append(contentsOf: newChildren) - boxStorages[boxStorageIndex].childrenBoxStorageIndices = Array(currentBoxStorageCount.. where C:VectorLike, TD: NdTreeDelegate, point, relativeTo: boxStorages[boxStorageIndex].box.center ) - + #if DEBUG - assert(boxStorages[childrenBoxStorageIndices[indexShiftForNewNode]].box.contains(point)) + assert(boxStorages[childrenBoxStorageIndices[indexShiftForNewNode]].box.contains(point)) #endif - - add(nodeIndex: nodeIndex, at: point, boxStorageIndex: - childrenBoxStorageIndices[indexShiftForNewNode] + + add( + nodeIndex: nodeIndex, at: point, + boxStorageIndex: + childrenBoxStorageIndices[indexShiftForNewNode] ) return } - - - private func cover(_ point:C, boxStorageIndex: BoxStorageIndex) { - + private func cover(_ point: V, boxStorageIndex: BoxStorageIndex) { + if boxStorages[boxStorageIndex].box.contains(point) { return } repeat { let _box = boxStorages[boxStorageIndex].box let indexShift = getIndexShiftInSubdivision(point, relativeTo: _box.p0) - - let nailedDirectionIndexShift = (directionCount-1)-indexShift - + + let nailedDirectionIndexShift = (directionCount - 1) - indexShift + let nailedCorner = _box.getCorner(of: nailedDirectionIndexShift) let expandedCorner = _box.getCorner(of: indexShift) * 2 - nailedCorner - + let newRootBox = Box(p0: nailedCorner, p1: expandedCorner) - - + let copyOfCurrentBoxStorage = boxStorages[boxStorageIndex] - + boxStorages[boxStorageIndex].box = newRootBox - + #if DEBUG - assert(copyOfCurrentBoxStorage.box.p0 != boxStorages[boxStorageIndex].box.p0 || copyOfCurrentBoxStorage.box.p1 != boxStorages[boxStorageIndex].box.p1) + assert( + copyOfCurrentBoxStorage.box.p0 != boxStorages[boxStorageIndex].box.p0 + || copyOfCurrentBoxStorage.box.p1 != boxStorages[boxStorageIndex].box.p1 + ) #endif - + appendDividedChildren(boxStorageIndex: boxStorageIndex) boxStorages[ boxStorages[boxStorageIndex].childrenBoxStorageIndices![ - //indexShift - getIndexShiftInSubdivision(point, relativeTo: expandedCorner) + getIndexShiftInSubdivision(point, relativeTo: expandedCorner) // <- to the center of the new box ] ] = copyOfCurrentBoxStorage - - + } while !boxStorages[boxStorageIndex].box.contains(point) } - - - private func getIndexShiftInSubdivision(_ point: C, relativeTo originalPoint: C) -> Int { + + private func getIndexShiftInSubdivision(_ point: V, relativeTo originalPoint: V) -> Int { var index = 0 - for i in 0..= originalPoint[i] { // isOnHigherRange in this dimension - index |= (1<= originalPoint[i] { // isOnHigherRange in this dimension + index |= (1 << i) } } return index } - - + private func appendDividedChildren(boxStorageIndex: BoxStorageIndex) { - var newChildren = Array(repeating: BoxStorage(box: Box()), count: directionCount) - let box = boxStorages[boxStorageIndex].box - let p0 = box.p0 - let p1 = box.p1 - let pCenter = box.center - + let _box = boxStorages[boxStorageIndex].box + var newChildren = Array(repeating: BoxStorage(box: _box), count: directionCount) + let pCenter = _box.center + for j in newChildren.indices { - for i in 0..> i) & 0b1 - + // TODO: use simd mask if isOnTheHigherRange != 0 { newChildren[j].box.p0[i] = pCenter[i] - newChildren[j].box.p1[i] = p1[i] - } - else { - newChildren[j].box.p0[i] = p0[i] + } else { newChildren[j].box.p1[i] = pCenter[i] } } } - - -// defer { -// self.boxStorages.append(contentsOf: newChildren) -// } - + let _boxStorage = boxStorages[boxStorageIndex] if !_boxStorage.nodeIndices.isEmpty { // put the indices to new let point = nodePositions[_boxStorage.nodeIndices.first!] - let index = getIndexShiftInSubdivision(point, relativeTo: pCenter) - newChildren[index].nodeIndices = _boxStorage.nodeIndices - // this node will not have children any more - boxStorages[boxStorageIndex].nodeIndices=[] + boxStorages[boxStorageIndex].nodeIndices = [] } - let currentBoxStorageCount = boxStorages.count - self.boxStorages.append(contentsOf: newChildren) - let newIndices = Array(currentBoxStorageCount ..< currentBoxStorageCount+directionCount) - boxStorages[boxStorageIndex].childrenBoxStorageIndices = newIndices + boxStorages[boxStorageIndex].childrenBoxStorageIndices = Array(boxStorages.count-directionCount.. Scalar @inlinable func length() -> Scalar @inlinable func distanceSquared(to: Self) -> Scalar @@ -26,4 +28,9 @@ public protocol VectorLike: CustomStringConvertible, Decodable, Encodable, Expre subscript(index: Int) -> Self.Scalar { get set } var indices: Range { get } + + +// mutating func replace(with other: Self, where mask: M) where M:MaskLike, M.Storage.Scalar==Scalar.SIMDMaskScalar + + } diff --git a/Tests/QuadTreeTests/NdTreeTests.swift b/Tests/QuadTreeTests/NdTreeTests.swift index 7f4c14b..adbcded 100644 --- a/Tests/QuadTreeTests/NdTreeTests.swift +++ b/Tests/QuadTreeTests/NdTreeTests.swift @@ -48,10 +48,8 @@ final class NdTreeTests: XCTestCase { _ p1: simd_double2, _ points: [simd_double2] ) -> DummyQuadTree { - let t = DummyQuadTree(initialBox: .init(p0, p1), estimatedNodeCount: points.count, clusterDistance: 1e-5) - for i in points.indices { - t.add(i, at:points[i]) - } + var t = DummyQuadTree(initialBox: .init(p0, p1), estimatedNodeCount: points.count, clusterDistance: 1e-5) + t.addAll(points) return t } @@ -60,77 +58,82 @@ final class NdTreeTests: XCTestCase { - let t = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 2, clusterDistance: 1e-5) + var t = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 2, clusterDistance: 1e-5) t.add(0, at: [1,2]) - assert(t.rootBox ~= QuadBox(p0:[0,0], p1:[4,4])) + assert(t.extent ~= QuadBox([0,0], [4,4])) - let t2 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 2, clusterDistance: 1e-5) + var t2 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 2, clusterDistance: 1e-5) t2.add(0, at: [0,0]) t2.add(1, at: [2,2]) t2.add(2, at: [3,3]) - assert(t2.rootBox ~= QuadBox([0,0], [4,4])) + assert(t2.extent ~= QuadBox([0,0], [4,4])) - let t3 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 3, clusterDistance: 1e-5) + var t3 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 3, clusterDistance: 1e-5) t3.add(0, at: [0,0]) t3.add(1, at: [2,2]) t3.add(2, at: [-1,3]) - assert(t3.rootBox ~= QuadBox([-4,0], [4,8])) + assert(t3.extent ~= QuadBox([-4,0], [4,8])) - let t4 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 3, clusterDistance: 1e-5) + var t4 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 3, clusterDistance: 1e-5) t4.add(0, at: [0,0]) t4.add(1, at: [2,2]) t4.add(2, at: [3,-1]) - assert(t4.rootBox ~= QuadBox([0,-4], [8,4])) + assert(t4.extent ~= QuadBox([0,-4], [8,4])) - let t5 = buildTestTree([0, 0], [1,1], [ + var t5 = buildTestTree([0, 0], [1,1], [ [0, 0], [2, 2], [-1,-1] ]) - assert(t5.rootBox ~= QuadBox([-4,-4], [4,4])) + assert(t5.extent ~= QuadBox([-4,-4], [4,4])) } -// func testRandomTree() { -// var randomPoints = Array(repeating:simd_double2.zero, count:10000) -// for i in randomPoints.indices { -// randomPoints[i] = [Double.random(in: -1000...1000), Double.random(in: -1000...1000)] -// } -// -// let r = buildTestTree([-1000,-1000], [1000,1000], randomPoints) + func testRandomTree() { + var randomPoints = Array(repeating:simd_double2.zero, count:10000) + for i in randomPoints.indices { + randomPoints[i] = [Double.random(in: -1000...1000), Double.random(in: -1000...1000)] + } + +// let r = buildTestTree([-1200,-1200], [1200,1200], randomPoints) // assert(r.delegate.count[1]!+r.delegate.count[2]!+r.delegate.count[3]!+r.delegate.count[4]! == 10000) // -// measure { -// let r = buildTestTree([-1000,-1000], [1000,1000], randomPoints) -// } -// -// + measure { + let r = buildTestTree([-1200,-1200], [1200,1200], randomPoints) + } + + + } + +// func testSimdMask() { +// let sim = SIMDMask.MaskStorage>([true,true]) +// var v = simd_double2.zero +// let v2 = simd_double2.one +// v.replace(with: [5,6], where: sim) +// assert(v.y==1) // } -// func testRandomTree2() { -// var randomPoints = Array(repeating:simd_double2.zero, count:77) -// for i in randomPoints.indices { -// randomPoints[i] = [Double.random(in: -300...300), Double.random(in: -300...300)] -// } -// -// -// measure { -// for i in 0..<120{ -// buildTestTree([-300,-300], [300,300], randomPoints) -// } -// } -// -// -// } -// + func testRandomTree2() { + var randomPoints = Array(repeating:simd_double2.zero, count:77) + for i in randomPoints.indices { + randomPoints[i] = [Double.random(in: -300...300), Double.random(in: -300...300)] + } + +// buildTestTree([-300,-300], [300,300], randomPoints) + measure { + for _ in 0..<120 { + buildTestTree([-300,-300], [300,300], randomPoints) + } + } + } } From 99b43cc33d3c72431024bd73a540b366fd586348 Mon Sep 17 00:00:00 2001 From: li3zhen1 Date: Sat, 14 Oct 2023 15:41:49 -0400 Subject: [PATCH 2/2] add OctTreeTests --- Sources/QuadTree/NdTree.swift | 42 +++++++------- Sources/QuadTree/VectorLike.swift | 1 - Tests/QuadTreeTests/NdTreeTests.swift | 84 ++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 38 deletions(-) diff --git a/Sources/QuadTree/NdTree.swift b/Sources/QuadTree/NdTree.swift index 11de2a6..65bddbf 100644 --- a/Sources/QuadTree/NdTree.swift +++ b/Sources/QuadTree/NdTree.swift @@ -1,17 +1,10 @@ // -// File.swift +// NdTree.swift // // // Created by li3zhen1 on 10/10/23. // -//public protocol ComponentComparable { -// @inlinable static func <(lhs: Self, rhs: Self) -> Bool -// @inlinable static func <=(lhs: Self, rhs: Self) -> Bool -// @inlinable static func >(lhs: Self, rhs: Self) -> Bool -// @inlinable static func >=(lhs: Self, rhs: Self) -> Bool -//} - public struct NdBox where Coordinate: VectorLike { public var p0: Coordinate public var p1: Coordinate @@ -110,12 +103,12 @@ where V: VectorLike, TD: NdTreeDelegate, TD.Coordinate == V { public typealias BoxStorageIndex = Int - private var directionCount: Int + public private(set) var directionCount: Int - fileprivate struct BoxStorage { + public struct BoxStorage { // once initialized, should have V.entryCount elements - var childrenBoxStorageIndices: [BoxStorageIndex]? = nil - var nodeIndices: [NodeIndex] + public var childrenBoxStorageIndices: [BoxStorageIndex]? = nil + public var nodeIndices: [NodeIndex] public var box: Box // { didSet { center = box.center } } @inlinable init( @@ -129,8 +122,8 @@ where V: VectorLike, TD: NdTreeDelegate, TD.Coordinate == V { } } - private var nodePositions: [V] - private var boxStorages: [BoxStorage] + public private(set) var nodePositions: [V] + public private(set) var boxStorages: [BoxStorage] private var clusterDistance: V.Scalar private var clusterDistanceSquared: V.Scalar @@ -145,9 +138,7 @@ where V: VectorLike, TD: NdTreeDelegate, TD.Coordinate == V { self.clusterDistance = clusterDistance self.clusterDistanceSquared = clusterDistance * clusterDistance self.directionCount = 1 << V.scalarCount - self.boxStorages = [ - .init(box: initialBox) - ] + self.boxStorages = [.init(box: initialBox)] self.nodePositions = [] self.boxStorages.reserveCapacity(4 * estimatedNodeCount) // TODO: Probably too much? its ~29000 for 10000 random nodes self.nodePositions.reserveCapacity(estimatedNodeCount) @@ -155,23 +146,27 @@ where V: VectorLike, TD: NdTreeDelegate, TD.Coordinate == V { self.delegate = TD() } + @discardableResult public func add( - _ nodeIndex: NodeIndex, at point: V - ) { + ) -> Int { nodePositions.append(point) cover(point, boxStorageIndex: 0) - add(nodeIndex: nodePositions.count - 1, at: point, boxStorageIndex: 0) + let nodeIndex = nodePositions.count - 1 + add(nodeIndex: nodeIndex, at: point, boxStorageIndex: 0) + return nodeIndex } + @discardableResult public func addAll( _ points: [V] - ) { + ) -> Range { nodePositions.append(contentsOf: points) for i in points.indices { cover(points[i], boxStorageIndex: 0) add(nodeIndex: i, at: points[i], boxStorageIndex: 0) } + return nodePositions.count - points.count ..< nodePositions.count } private func add( @@ -348,12 +343,17 @@ where V: VectorLike, TD: NdTreeDelegate, TD.Coordinate == V { } } + extension NdTree { public var extent: Box { boxStorages[0].box } + } + + extension NdBox: CustomDebugStringConvertible { @inlinable public var debugDescription: String { return "[\(p0), \(p1)]" } + } diff --git a/Sources/QuadTree/VectorLike.swift b/Sources/QuadTree/VectorLike.swift index 6fa2f08..1ccfe43 100644 --- a/Sources/QuadTree/VectorLike.swift +++ b/Sources/QuadTree/VectorLike.swift @@ -11,7 +11,6 @@ public protocol VectorLike: CustomStringConvertible, Decodable, Encodable, Expre associatedtype Scalar: FloatingPoint, Decodable, Encodable, Hashable - @inlinable func lengthSquared() -> Scalar @inlinable func length() -> Scalar @inlinable func distanceSquared(to: Self) -> Scalar diff --git a/Tests/QuadTreeTests/NdTreeTests.swift b/Tests/QuadTreeTests/NdTreeTests.swift index adbcded..d0af587 100644 --- a/Tests/QuadTreeTests/NdTreeTests.swift +++ b/Tests/QuadTreeTests/NdTreeTests.swift @@ -22,10 +22,35 @@ struct EmptyTreeDelegate: CompactQuadTreeDelegate { count[indexOfBoxStorage, default: 0] -= 1 } - typealias Coordinate = simd_double2 + typealias Coordinate = Vector2d +} + + +struct EmptyTreeDelegate3D: CompactOctTreeDelegate { + + var count: [Int:Int] = [:] + + mutating func didAddNode( + _ nodeIndex: NodeIndex, + at position: Coordinate, + in indexOfBoxStorage: BoxStorageIndex + ) { + count[indexOfBoxStorage, default: 0] += 1 + } + + mutating func didRemoveNode( + _ nodeIndex: NodeIndex, + at position: Coordinate, + in indexOfBoxStorage: BoxStorageIndex + ) { + count[indexOfBoxStorage, default: 0] -= 1 + } + + typealias Coordinate = Vector3d } typealias DummyQuadTree = CompactQuadTree +typealias DummyOctTree = CompactOctTree final class NdTreeTests: XCTestCase { @@ -53,38 +78,48 @@ final class NdTreeTests: XCTestCase { return t } + func buildTestTree3D( + _ p0: Vector3d, + _ p1: Vector3d, + _ points: [Vector3d] + ) -> DummyOctTree { + var t = DummyOctTree(initialBox: .init(p0, p1), estimatedNodeCount: points.count, clusterDistance: 1e-5) + t.addAll(points) + return t + } + func test2DCreatePoint() { var t = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 2, clusterDistance: 1e-5) - t.add(0, at: [1,2]) + t.add(at: [1,2]) assert(t.extent ~= QuadBox([0,0], [4,4])) var t2 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 2, clusterDistance: 1e-5) - t2.add(0, at: [0,0]) - t2.add(1, at: [2,2]) - t2.add(2, at: [3,3]) + t2.add(at: [0,0]) + t2.add(at: [2,2]) + t2.add(at: [3,3]) assert(t2.extent ~= QuadBox([0,0], [4,4])) var t3 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 3, clusterDistance: 1e-5) - t3.add(0, at: [0,0]) - t3.add(1, at: [2,2]) - t3.add(2, at: [-1,3]) + t3.add(at: [0,0]) + t3.add(at: [2,2]) + t3.add(at: [-1,3]) assert(t3.extent ~= QuadBox([-4,0], [4,8])) var t4 = DummyQuadTree(initialBox: .init([0,0], [1,1]), estimatedNodeCount: 3, clusterDistance: 1e-5) - t4.add(0, at: [0,0]) - t4.add(1, at: [2,2]) - t4.add(2, at: [3,-1]) + t4.add(at: [0,0]) + t4.add(at: [2,2]) + t4.add(at: [3,-1]) assert(t4.extent ~= QuadBox([0,-4], [8,4])) @@ -123,15 +158,32 @@ final class NdTreeTests: XCTestCase { func testRandomTree2() { - var randomPoints = Array(repeating:simd_double2.zero, count:77) + var randomPoints = Array(repeating:Vector2d.zero, count:1000) + for i in randomPoints.indices { + randomPoints[i] = [Double.random(in: -1000...1000), Double.random(in: -1000...1000)] + } + + measure { + for i in 0..<120 { + buildTestTree([-1200,-1200], [1200,1200], randomPoints) + } + } + } + + + func testRandomTree3D() { + var randomPoints = Array(repeating:Vector3d.zero, count:1000) for i in randomPoints.indices { - randomPoints[i] = [Double.random(in: -300...300), Double.random(in: -300...300)] + randomPoints[i] = [ + Double.random(in: -1000...1000), + Double.random(in: -1000...1000), + Double.random(in: -1000...1000) + ] } -// buildTestTree([-300,-300], [300,300], randomPoints) measure { - for _ in 0..<120 { - buildTestTree([-300,-300], [300,300], randomPoints) + for i in 0..<120 { + buildTestTree3D([-1200,-1200, -1200], [1200,1200, 1200], randomPoints) } } }