Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add quad tree delegate #1

Merged
merged 1 commit into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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