{"id":15527125,"url":"https://github.com/swiftgraphs/grape","last_synced_at":"2025-12-11T22:51:33.116Z","repository":{"id":199174371,"uuid":"700115214","full_name":"SwiftGraphs/Grape","owner":"SwiftGraphs","description":"A Swift library for graph visualization and efficient force simulation.","archived":false,"fork":false,"pushed_at":"2025-01-18T03:14:46.000Z","size":25778,"stargazers_count":304,"open_issues_count":1,"forks_count":14,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-03T11:09:16.861Z","etag":null,"topics":["force-directed-graphs","graph","physics-simulation","simulation","swift","swiftui","visualization"],"latest_commit_sha":null,"homepage":"https://swiftgraphs.github.io/Grape/Grape/documentation/grape/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SwiftGraphs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-04T01:13:16.000Z","updated_at":"2025-04-03T06:43:57.000Z","dependencies_parsed_at":"2023-10-12T10:55:17.870Z","dependency_job_id":"1c921a9b-e24e-4e13-ba70-06f8a9c510e6","html_url":"https://github.com/SwiftGraphs/Grape","commit_stats":{"total_commits":482,"total_committers":9,"mean_commits":53.55555555555556,"dds":0.5912863070539419,"last_synced_commit":"ab36a8947b46e120dcb73142db95cf038573c926"},"previous_names":["li3zhen1/grape","swiftgraphs/grape"],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SwiftGraphs%2FGrape","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SwiftGraphs%2FGrape/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SwiftGraphs%2FGrape/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SwiftGraphs%2FGrape/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SwiftGraphs","download_url":"https://codeload.github.com/SwiftGraphs/Grape/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248531289,"owners_count":21119735,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["force-directed-graphs","graph","physics-simulation","simulation","swift","swiftui","visualization"],"created_at":"2024-10-02T11:04:33.319Z","updated_at":"2025-12-11T22:51:33.082Z","avatar_url":"https://github.com/SwiftGraphs.png","language":"Swift","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg alt=\"grape-icon\" src=\"https://github.com/swiftgraphs/Grape/assets/45376537/4ab08ea1-22e6-4fe8-ab2b-99ae325b46a6\" height=\"96\"\u003e\n  \u003ch1 align=\"center\"\u003eGrape\u003c/h1\u003e\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://swiftpackageindex.com/swiftgraphs/Grape\"\u003e\u003cimg src=\"https://img.shields.io/endpoint?color=FA7343\u0026url=https://swiftpackageindex.com/api/packages/swiftgraphs/Grape/badge?type=platforms\" alt=\"swift package index\"\u003e\u003c/a\u003e\u0026thinsp;\n  \u003ca href=\"https://swiftpackageindex.com/swiftgraphs/Grape\"\u003e\u003cimg src=\"https://img.shields.io/endpoint?color=FA7343\u0026url=https://swiftpackageindex.com/api/packages/swiftgraphs/Grape/badge?type=swift-versions\" alt=\"swift package index\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eA Swift library for graph visualization and efficient force simulation.\u003c/p\u003e\n  \n\u003cpicture alt=\"example of grape\"\u003e\n  \u003csource srcset=\"https://github.com/swiftgraphs/Grape/assets/45376537/6703480d-5737-4a8e-bc08-92d8676456da\" media=\"(prefers-color-scheme: dark)\"\u003e\n  \u003csource srcset=\"https://github.com/swiftgraphs/Grape/assets/45376537/22988cfb-8e01-49b7-a55b-b476fcd9de7c\" media=\"(prefers-color-scheme: light)\"\u003e\n  \u003cimg src=\"https://github.com/swiftgraphs/Grape/assets/45376537/22988cfb-8e01-49b7-a55b-b476fcd9de7c\"\u003e\n\u003c/picture\u003e\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n## Examples\n\n### Force Directed Graph\nThis is a force directed graph visualizing [the network of character co-occurence in _Les Misérables_](https://observablehq.com/@d3/force-directed-graph-component). Take a closer look at the animation:\n\n\n\nhttps://github.com/swiftgraphs/Grape/assets/45376537/d80dc797-1980-4755-85b9-18ee26e2a7ff\n\n\n\nSource code: [Miserables.swift](https://github.com/swiftgraphs/Grape/blob/main/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/Miserables.swift). \n\n\n\n\u003cbr/\u003e\n\n### Force Directed Graph in visionOS\n\nThis is the same graph as the first example, rendered in `RealityView`:\n\n\n\nhttps://github.com/swiftgraphs/Grape/assets/45376537/4585471e-2339-4aee-8f39-0c11fdfb6901\n\n\n\nSource code: [ForceDirectedGraph3D/ContentView.swift](https://github.com/swiftgraphs/Grape/blob/main/Examples/ForceDirectedGraph3D/ForceDirectedGraph3D/ContentView.swift).\n\n\n\u003cbr/\u003e\n\n\n### Mermaid Visualization\n\nDynamical graph structure based on your input, with tap and drag gesture supports, all within 100 lines of view body.\n\nhttps://github.com/swiftgraphs/Grape/assets/45376537/7c75d367-d5a8-4316-813b-288b375f513b\n\n\n\nSource code: [MermaidVisualization.swift](https://github.com/swiftgraphs/Grape/blob/main/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MermaidVisualization.swift)\n\n\u003cbr/\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \n### Lattice Simulation\n\nA 36x36 force directed lattice.\n\n\u003c/summary\u003e\n\nhttps://github.com/swiftgraphs/Grape/assets/45376537/5b76fddc-dd5c-4d35-bced-29c01269dd2b\n\nSource code: [Lattice.swift](https://github.com/swiftgraphs/Grape/blob/main/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/Lattice.swift)\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n\n### Dragging Gesture\n\nAn example rendering a ring with 60 vertices, with dragging gesture enabled.\n\n\u003c/summary\u003e\n\nhttps://github.com/swiftgraphs/Grape/assets/45376537/73213e7f-73ee-44f3-9b3e-7e58355045d2\n\nSource code: [MyRing.swift](https://github.com/swiftgraphs/Grape/blob/main/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MyRing.swift)\n\u003c/details\u003e\n\n\u003cbr/\u003e\n\n\u003cbr/\u003e\n\n\n## Installation\n\nTo use Grape in an Xcode project by adding it to your project as a package:\n\n```\nhttps://github.com/swiftgraphs/Grape\n```\n\nTo use Grape in a [SwiftPM](https://swift.org/package-manager/) project, add this to your `Package.swift`:\n\n``` swift\ndependencies: [\n    .package(url: \"https://github.com/swiftgraphs/Grape\", from: \"1.1.0\")\n]\n```\n\n```swift\n.product(name: \"Grape\", package: \"Grape\"),\n```\n\n\u003e [!NOTE]\n\u003e The `Grape` module relies on the [`Observation` framework](https://developer.apple.com/documentation/observation). It’s possible to backdeploy with community shims like [`swift-perception`](https://github.com/pointfreeco/swift-perception).\n\u003e \n\u003e The `Grape` module may introduce breaking API changes in minor version changes before 1.0 release.\n\u003e\n\u003e The `ForceSimulation` module is stable in terms of public API now.\n\n\u003cbr/\u003e\n\n\u003cbr/\u003e\n\n## Get started\n\nGrape ships 2 modules:\n\n- The `Grape` module allows you to create force-directed graphs in SwiftUI Views.\n- The `ForceSimulation` module is the underlying mechanism of `Grape`, and it helps you to create more complicated or customized force simulations. It also contains a `KDTree` data structure built with performance in mind, which can be useful for spatial partitioning tasks.\n\n\n\u003cbr/\u003e\n\n### The `Grape` module\n\n\nFor detailed usage, please refer to [documentation](https://swiftgraphs.github.io/Grape/Grape/documentation/grape). A quick example here:\n\n```swift\nimport Grape\n\nstruct MyGraph: View {\n\n    // States including running status, transformation, etc.\n    // Gives you a handle to control the states.\n    @State var graphStates = ForceDirectedGraphState() \n    \n    var body: some View {\n        ForceDirectedGraph(states: graphStates) {\n            \n            // Declare nodes and links like you would do in Swift Charts.\n            NodeMark(id: 0).foregroundStyle(.green)\n            NodeMark(id: 1).foregroundStyle(.blue)\n            NodeMark(id: 2).foregroundStyle(.yellow)\n\n            Series(0..\u003c2) { i in\n                LinkMark(from: i, to: i+1)\n            }\n            \n        } force: {\n            .link()\n            .center()\n            .manyBody()\n        }\n    }\n}\n```\n\n\n\n\u003cbr/\u003e\n\n\n### The `ForceSimulation` module\n\u003cdetails\u003e\n  \u003csummary\u003eRefer to the \u003ca href=\"https://swiftgraphs.github.io/Grape/ForceSimulation/documentation/forcesimulation/\"\u003edocumentation\u003c/a\u003e or expand this section to find more about this module.\n  \u003c/summary\u003e\n\n`ForceSimulation` module mainly contains 3 concepts, `Kinetics`, `ForceProtocol` and `Simulation`.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/swiftgraphs/Grape/main/Assets/SimulationDiagram.svg\" alt=\"A diagram showing the relationships of `Kinetics`, `ForceProtocol` and `Simulation`. A `Simulation` contains a `Kinetics` and a `ForceProtocol`.\"\u003e\n\u003c/p\u003e\n\n  \n- `Kinetics` describes all kinetic states of your system, i.e. position, velocity, link connections, and the variable `alpha` that describes how \"active\" your system is.\n- Forces are any types that conforms to `ForceProtocol`. This module provides most of the forces you will use in force directed graphs. And you can also create your own forces. They should be responsible for 2 tasks:\n    - `bindKinetics(_ kinetics: Kinetics\u003cVector\u003e)`: binding to a `Kinetics`. In most cases the force should keep a reference of the `Kinetics` so they know what to mutate when `apply` is called.\n    - `apply()`: Mutating the states of `Kinetics`. For example, a gravity force should add velocities on each node in this function.\n- `Simulation` is a shell class you interact with, which enables you to create any dimensional simulation with velocity Verlet integration. It manages a `Kinetics` and a force conforming to `ForceProtocol`. Since `Simulation` only stores one force, you are responsible for compositing multiple forces into one.\n- Another data structure `KDTree` is used to accelerate the force simulation with [Barnes-Hut Approximation](https://jheer.github.io/barnes-hut/).\n\n\u003cbr/\u003e\n\nThe basic concepts of simulations and forces can be found here: [Force simulations - D3](https://d3js.org/d3-force/simulation). You can simply create simulations by using `Simulation` like this:\n\n```swift\nimport simd\nimport ForceSimulation\n\n// assuming you’re simulating 4 nodes\nlet nodeCount = 4\n\n\n// Connect them\nlet links = [(0, 1), (1, 2), (2, 3), (3, 0)] \n\n/// Create a 2D force composited with 4 primitive forces.\nlet myForce = SealedForce2D {\n    // Forces are namespaced under `Kinetics\u003cVector\u003e`\n    // here we only use `Kinetics\u003cSIMD2\u003cDouble\u003e\u003e`, i.e. `Kinetics2D`\n    Kinetics2D.ManyBodyForce(strength: -30)\n    Kinetics2D.LinkForce(\n        stiffness: .weightedByDegree(k: { _, _ in 1.0 }),\n        originalLength: .constant(35)\n    )\n    Kinetics2D.CenterForce(center: .zero, strength: 1)\n    Kinetics2D.CollideForce(radius: .constant(3))\n}\n\n/// Create a simulation, the dimension is inferred from the force.\nlet mySimulation = Simulation(\n    nodeCount: nodeCount,\n    links: links.map { EdgeID(source: $0.0, target: $0.1) },\n    forceField: myForce\n) \n\n/// Force is ready to start! run `tick` to iterate the simulation.\n\nfor mySimulation in 0..\u003c120 {\n    mySimulation.tick()\n    let positions = mySimulation.kinetics.position.asArray()\n    /// Do something with the positions.\n}\n\n```\n\nSee [Example](https://github.com/swiftgraphs/Grape/tree/main/Examples/ForceDirectedGraphExample) for more details. \n\n\u003c/details\u003e\n\n\n\n\u003cbr/\u003e\n\n\u003cbr/\u003e\n\n\n## Roadmap\n\n|   | 2D simd | ND simd | Metal |\n| --- | --- | --- | --- |\n| **NdTree** | ✅ | ✅ |  |\n| **Simulation** | ✅ | ✅ |  |\n| \u0026emsp;LinkForce | ✅ | ✅ |  |\n| \u0026emsp;ManyBodyForce | ✅ | ✅ |  |\n| \u0026emsp;CenterForce | ✅ | ✅ |  |\n| \u0026emsp;CollideForce | ✅ | ✅ |  |\n| \u0026emsp;PositionForce | ✅ | ✅ |  |\n| \u0026emsp;RadialForce | ✅ | ✅ |  |\n| **SwiftUI View** | ✅ |  |  |\n| \u0026emsp;Basic Visualization | ✅ |  |  |\n| \u0026emsp;Gestures | ✅ |  |  |\n| \u0026emsp;Node Styling | ✅ |  |  |\n| \u0026emsp;Link Styling | 🚧 |  |  |\n| \u0026emsp;Animatable Transition | 🚧 |  |  |\n\n\u003cbr/\u003e\n\n\u003cbr/\u003e\n\n## Performance\n\n\u003cbr/\u003e\n\n#### Simulation\n\nGrape uses simd to calculate position and velocity. Currently it takes **~0.005** seconds to iterate 120 times over the example graph(2D). (77 vertices, 254 edges, with manybody, center, collide and link forces. Release build on a M1 Max, [tested](https://github.com/swiftgraphs/Grape/blob/main/Tests/ForceSimulationTests/MiserableGraphTest.swift) with command `swift test -c release`)\n\nFor 3D simulation, it takes **~0.008** seconds for the same graph and same configs.\n\n\u003e [!IMPORTANT]\n\u003e Due to heavy use of generics (some are not specialized in Debug mode), the performance in Debug build is ~100x slower than Release build. \n\n\u003cbr/\u003e\n\n#### KDTree\nThe `BufferedKDTree` from this package is **~22x** faster than `GKQuadtree` from Apple’s GameKit, according to this [test case](https://github.com/swiftgraphs/Grape/blob/main/Tests/ForceSimulationTests/GKTreeCompareTest.swift). However, please note that comparing Swift structs with NSObjects is unfair, and their behaviors are different.\n\n\n\u003cbr/\u003e\n\n## Credits\n\nThis library has been greatly influenced by the outstanding work done by [D3.js (Data-Driven Documents)](https://d3js.org).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswiftgraphs%2Fgrape","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswiftgraphs%2Fgrape","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswiftgraphs%2Fgrape/lists"}