Skip to content

Commit

Permalink
Merge pull request #5 from li3zhen1/dev
Browse files Browse the repository at this point in the history
Fix: CollideForce calculation
  • Loading branch information
li3zhen1 authored Oct 10, 2023
2 parents 105326b + 4d7cb46 commit 803ef9e
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 22 deletions.
File renamed without changes
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
A visualization-purposed force simulation library.


#### Examples


<img width="712" alt="ForceDirectedGraphLight" src="https://github.com/li3zhen1/Grape/assets/45376537/e0e8049d-25c2-4e5c-9623-6bf43ddddfa5">

#### Examples

This is a force directed graph visualizing the data from [Force Directed Graph Component](https://observablehq.com/@d3/force-directed-graph-component), running at 120FPS on a SwiftUI Canvas. Take a closer look at the animation:

https://github.com/li3zhen1/Grape/assets/45376537/5f57c223-0126-428a-a72d-d9a3ed38059d





#### Features

| Feature | Status |
Expand All @@ -27,9 +27,30 @@ https://github.com/li3zhen1/Grape/assets/45376537/5f57c223-0126-428a-a72d-d9a3ed
| CenterForce ||
| CollideForce ||
| PositionForce | |
| RadialForce | |
| RadialForce ||


#### Usage

```swift
import ForceSimulation

// nodes with unique id
let nodes: [Identifiable] = ...

// links with source and target, ID should be the same as the type of the id
let links: [(ID, ID)] = ...

let sim = Simulation(nodes: nodes, alphaDecay: 0.0005)
sim.createManyBodyForce(strength: -30)
sim.createLinkForce(links: links, originalLength: .constant(35))
sim.createCenterForce(center: .zero, strength: 0.1)
sim.createCollideForce(radius: .constant(5))

```

See [Example](https://github.com/li3zhen1/Grape/tree/main/Examples/GrapeView) for more details.

#### Perfomance

Currently iterating the example graph 120 times in release build takes 0.046 seconds on a 32GB M1 Max. (77 vertices, 254 edges, link, with manybody, center and link forces)
Currently it takes 0.046 seconds to iterate 120 times over the example graph (with 77 vertices, 254 edges, with manybody, center and link forces, release build, on a 32GB M1 Max).
21 changes: 11 additions & 10 deletions Sources/ForceSimulation/forces/CollideForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,18 @@ extension CollideForce: Force {
nodes: sim.simulationNodes.map { ($0, $0.position) },
getQuadDelegate: {
MaxRadiusQuadTreeDelegate {
switch self.radius {
case .constant(let r):
return r
case .varied(let r):
return r($0)
}
// switch self.radius {
// case .constant(let r):
// return r
// case .varied(_):
// return self.calculatedRadius[$0, default: 0.0]
// }
return self.calculatedRadius[$0, default: 0.0]
// return self.calculatedRadius[$0]!
}
}
)
else { break }
else { return }

for i in sim.simulationNodes.indices {
let iNode = sim.simulationNodes[i]
Expand Down Expand Up @@ -156,7 +158,6 @@ extension CollideForce: Force {
var l = deltaPosition.jiggled().length()
l = (deltaR - l) / l * self.strength

deltaPosition *= l
let jR2 = jR * jR

let k = jR2 / (iR2 + jR2)
Expand All @@ -171,7 +172,7 @@ extension CollideForce: Force {
}

return
!(quadNode.quad.x0 > iPosition.x + deltaR
!(quadNode.quad.x0 > iPosition.x + deltaR /* True if no overlap */
|| quadNode.quad.x1 < iPosition.x - deltaR
|| quadNode.quad.y0 > iPosition.y + deltaR
|| quadNode.quad.y1 < iPosition.y - deltaR)
Expand All @@ -184,7 +185,7 @@ extension CollideForce: Force {
extension Simulation {
@discardableResult
public func createCollideForce(
radius: CollideForce<N>.CollideRadius,
radius: CollideForce<N>.CollideRadius = .constant(3.0),
strength: Float = 1.0,
iterationsPerTick: Int = 1
) -> CollideForce<N> {
Expand Down
1 change: 1 addition & 0 deletions Sources/ForceSimulation/forces/LinkForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ final public class LinkForce<N>: Force where N: Identifiable {
var calculatedBias: [Float] = []
weak var simulation: Simulation<N>?


var iterations: Int

internal init(
Expand Down
14 changes: 7 additions & 7 deletions Sources/ForceSimulation/forces/ManyBodyForce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,17 @@ final public class ManyBodyForce<N>: Force where N: Identifiable {
let quad = try QuadTree2(
nodes: sim.simulationNodes.map { ($0, $0.position) }
) {
// this switch is only called on root init
// this switch is only called on root init
// but it significantly slows down the performance
//
// return switch self.mass {
// case .constant(let m):
// MassQuadTreeDelegate<SimulationNode<N.ID>> { _ in m }
// case .varied(_):
//
return switch self.mass {
case .constant(let m):
MassQuadTreeDelegate<SimulationNode<N.ID>> { _ in m }
case .varied(_):
MassQuadTreeDelegate<SimulationNode<N.ID>> {
self.precalculatedMass[$0, default: 0.0]
}
// }
}
}

var forces = [Vector2f](repeating: .zero, count: sim.simulationNodes.count)
Expand Down

0 comments on commit 803ef9e

Please sign in to comment.