Skip to content

Commit

Permalink
Merge pull request #1 from li3zhen1/dev
Browse files Browse the repository at this point in the history
Add quad tree delegate
  • Loading branch information
li3zhen1 authored Oct 9, 2023
2 parents 86883d9 + bac8071 commit 8d7fbc6
Show file tree
Hide file tree
Showing 10 changed files with 607 additions and 51 deletions.
1 change: 0 additions & 1 deletion Sources/ForceSimulation/Force.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public protocol Force {
associatedtype N: Identifiable

func apply(alpha: Float)
func initialize()
}


Expand Down
4 changes: 0 additions & 4 deletions Sources/ForceSimulation/forces/CenterForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ public class CenterForce<N> : Force where N : Identifiable {
}
}

public func initialize() {

}

}


Expand Down
44 changes: 32 additions & 12 deletions Sources/ForceSimulation/forces/CollideForce.swift
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
//
// File.swift
// CollideForce.swift
//
//
// Created by li3zhen1 on 10/1/23.
//

import Foundation
import QuadTree

enum CollideForceError: Error {
case applyBeforeSimulationInitialized
}


public class CollideForce<N> where N : Identifiable {

let radius: CollideRadius
let iterationsPerTick: Int


weak var simulation: Simulation<N>?

public class CollideForce<N> : Force where N : Identifiable {
internal init(
radius: CollideRadius,
iterationsPerTick: Int = 1
) {
self.radius = radius
self.iterationsPerTick = iterationsPerTick
}
}

public enum CollideRadius {

public extension CollideForce {
enum CollideRadius{
case constant(Float)
case varied( (N.ID) -> Float )
case polarCoordinatesOnRad( (Float, N.ID) -> Float )
}
}

weak var simulation: Simulation<N>?

extension CollideForce: Force {
public func apply(alpha: Float) {

}
guard let sim = self.simulation else { return }

for _ in 0..<iterationsPerTick {
// guard let quad = try? QuadTree(nodes: sim.simulationNodes.map { ($0, $0.position) }) else { break }

public func initialize() {

}
}




}
8 changes: 0 additions & 8 deletions Sources/ForceSimulation/forces/LinkForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,6 @@ public class LinkForce<N> : Force where N : Identifiable {
}
}

public func initialize() {
guard let sim = self.simulation else { return }
for link in self.links {

}

}


public let defaultStiffness: LinkStiffness = .varied { link, lookup in
1 / Float(
Expand Down
88 changes: 76 additions & 12 deletions Sources/ForceSimulation/forces/ManyBodyForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,77 @@ import QuadTree
import simd


enum ManyBodyForceError: Error {
case buildQuadTreeBeforeSimulationInitialized
final class MassQuadTreeDelegate<N>: QuadDelegate where N : Identifiable {

typealias Node = N
typealias Property = Float
typealias MassProvider = [N.ID: Float]

public var accumulatedProperty: Float = 0.0
public var accumulatedCount = 0
public var weightedAccumulatedNodePositions: Vector2f = .zero

let massProvider: [N.ID: Float]

init(
massProvider: MassProvider
) {
self.massProvider = massProvider
}


internal init(
initialAccumulatedProperty: Float,
initialAccumulatedCount: Int,
initialWeightedAccumulatedNodePositions: Vector2f,
massProvider: MassProvider
) {
self.accumulatedProperty = initialAccumulatedProperty
self.accumulatedCount = initialAccumulatedCount
self.weightedAccumulatedNodePositions = initialWeightedAccumulatedNodePositions
self.massProvider = massProvider
}

func didAddNode(_ node: N, at position: Vector2f) {
let p = massProvider[node.id, default: 0]
accumulatedCount += 1
accumulatedProperty += p
weightedAccumulatedNodePositions += p * position
}

func didRemoveNode(_ node: N, at position: Vector2f) {
let p = massProvider[node.id, default: 0]
accumulatedCount -= 1
accumulatedProperty -= p
weightedAccumulatedNodePositions -= p * position

// TODO: parent removal?
}


func createForExpanded(towards _: Quadrant, from _: Quad, to _: Quad) -> Self {
return Self(
initialAccumulatedProperty: self.accumulatedProperty,
initialAccumulatedCount: self.accumulatedCount,
initialWeightedAccumulatedNodePositions: self.weightedAccumulatedNodePositions,
massProvider: self.massProvider
)
}

func stem() -> Self {
return Self(massProvider: self.massProvider)
}


var centroid : Vector2f? {
guard accumulatedCount > 0 else { return nil }
return weightedAccumulatedNodePositions / accumulatedProperty
}
}

extension SimulationNode: HasMassLikeProperty {
public var property: Float {1.0}

enum ManyBodyForceError: Error {
case buildQuadTreeBeforeSimulationInitialized
}

public class ManyBodyForce<N> : Force where N : Identifiable {
Expand Down Expand Up @@ -50,23 +115,23 @@ public class ManyBodyForce<N> : Force where N : Identifiable {
}
}

public func initialize() {

}


func calculateForce(alpha: Float) throws -> [Vector2f] {

guard let sim = self.simulation else {
throw ManyBodyForceError.buildQuadTreeBeforeSimulationInitialized
}

let quad = try QuadTree(nodes: sim.simulationNodes.map { ($0, $0.position) })
let quad = try QuadTree2(nodes: sim.simulationNodes.map { ($0, $0.position) }) {
MassQuadTreeDelegate(massProvider: sim.simulationNodes.reduce(into: [N.ID: Float]()) { $0[$1.id] = 1.0 })
}

var forces = Array<Vector2f>(repeating: .zero, count: sim.simulationNodes.count)

for i in sim.simulationNodes.indices {
quad.visit { quadNode in
if let centroid = quadNode.centroid {
if let centroid = quadNode.quadDelegate.centroid {
let vec = centroid - sim.simulationNodes[i].position

var distanceSquared = vec.jiggled()
Expand All @@ -82,10 +147,9 @@ public class ManyBodyForce<N> : Force where N : Identifiable {
distanceSquared = sqrt(self.distanceMin2 * distanceSquared)
}


if quadNode.isLeaf || distanceSquared * self.theta2 > quadNode.quad.area {
if (quadNode.isLeaf || (distanceSquared * self.theta2 > quadNode.quad.area)) {

forces[i] += self.strength * alpha * quadNode.accumulatedProperty * vec / pow(distanceSquared, 1.5)
forces[i] += self.strength * alpha * quadNode.quadDelegate.accumulatedProperty * vec / pow(distanceSquared, 1.5)

return false
}
Expand Down
5 changes: 1 addition & 4 deletions Sources/ForceSimulation/forces/PositionForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ public class PositionForce<N> : Force where N: Identifiable {
public func apply(alpha: Float) {

}

public func initialize() {

}




Expand Down
6 changes: 1 addition & 5 deletions Sources/ForceSimulation/forces/RadialForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ public class RadialForce<N> : Force where N : Identifiable {
public func apply(alpha: Float) {

}

public func initialize() {

}



}
8 changes: 5 additions & 3 deletions Sources/QuadTree/QuadTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import simd


// TODO: https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwjoh_vKttuBAxUunokEHdchDZAQFnoECBkQAQ&url=https%3A%2F%2Fosf.io%2Fdu6gq%2Fdownload%2F%3Fversion%3D1%26displayName%3Dgove-2018-updating-tree-approximations-2018-06-13T02%253A16%253A17.463Z.pdf&usg=AOvVaw3KFAE5U8cnhTDMN_qrzV6a&opi=89978449
@available(*, deprecated)
public class QuadTreeNode<N> where N: Identifiable, N: HasMassLikeProperty {



public private(set) var quad: Quad

public var nodes: [N.ID: Vector2f] = [:] // TODO: merge nodes if close enough

// public var allNodes: [NodeID: Vector2f] = [:]
public var accumulatedProperty: Float = 0.0
public var accumulatedCount = 0
public var weightedAccumulatedNodePositions: Vector2f = .zero
Expand All @@ -28,7 +29,6 @@ public class QuadTreeNode<N> where N: Identifiable, N: HasMassLikeProperty {
}
}



final public class Children {
public private(set) var northWest: QuadTreeNode<N>
Expand Down Expand Up @@ -274,6 +274,8 @@ enum QuadTreeError: Error {
case noNodeProvidedError
}


@available(*, deprecated)
final public class QuadTree<N: Identifiable> where N: HasMassLikeProperty {
public private(set) var root: QuadTreeNode<N>
private var nodeIds: Set<N.ID> = []
Expand Down
Loading

0 comments on commit 8d7fbc6

Please sign in to comment.