├── .gitignore ├── LICENSE ├── Pathfinder-Demo ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── GridView.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-40.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-72.png │ │ ├── Icon-72@2x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-83.5@2x.png │ │ ├── Icon-Small-50.png │ │ ├── Icon-Small-50@2x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ ├── Icon.png │ │ ├── Icon@2x.png │ │ ├── NotificationIcon@2x.png │ │ ├── NotificationIcon@3x.png │ │ ├── NotificationIcon~ipad.png │ │ ├── NotificationIcon~ipad@2x.png │ │ └── ios-marketing.png │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── NodeView.swift ├── Pathfinder-Demo-Bridging-Header.h └── ViewController.swift ├── Pathfinder.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── ilijatovilo.xcuserdatad │ └── xcschemes │ ├── Pathfinder-Demo.xcscheme │ ├── Pathfinder.xcscheme │ ├── PathfinderTests.xcscheme │ └── xcschememanagement.plist ├── Pathfinder ├── AStarAlgorithm.swift ├── Algorithm.swift ├── Coordinates.swift ├── Grid.swift ├── Helpers.swift ├── HeuristicFunction.swift ├── IndexedArray.swift ├── Info.plist ├── Map.swift ├── Matrix.swift ├── Node.swift └── Pathfinder.h ├── README.md ├── demo.png └── demo2.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/github/gitignore/blob/master/Objective-C.gitignore 2 | 3 | # Finder 4 | .DS_Store 5 | 6 | # Xcode 7 | ## Build generated 8 | build/ 9 | DerivedData/ 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata/ 21 | 22 | ## Other 23 | *.moved-aside 24 | *.xccheckout 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | 39 | Pods/ 40 | 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 54 | # screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ilija Tovilo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pathfinder-Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Pathfinder-Demo 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import UIKit 27 | 28 | @UIApplicationMain 29 | class AppDelegate: UIResponder, UIApplicationDelegate { 30 | 31 | var window: UIWindow? 32 | 33 | 34 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 35 | // Override point for customization after application launch. 36 | return true 37 | } 38 | 39 | func applicationWillResignActive(_ application: UIApplication) { 40 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 41 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 42 | } 43 | 44 | func applicationDidEnterBackground(_ application: UIApplication) { 45 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | func applicationWillEnterForeground(_ application: UIApplication) { 50 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 51 | } 52 | 53 | func applicationDidBecomeActive(_ application: UIApplication) { 54 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 55 | } 56 | 57 | func applicationWillTerminate(_ application: UIApplication) { 58 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 59 | } 60 | 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /Pathfinder-Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 42 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Pathfinder-Demo/GridView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import UIKit 27 | import Pathfinder 28 | 29 | private let _gridSize = 20 30 | 31 | enum TouchOperation { 32 | case Start, End, Draw, Erase 33 | } 34 | 35 | class GridView: UIView { 36 | 37 | private var _nodes: Matrix! 38 | private var _grid: Grid! 39 | private var _startNodeView: NodeView! 40 | private var _endNodeView: NodeView! 41 | private var _touchOperation = TouchOperation.Draw 42 | private var _touchNode: NodeView? 43 | private var _nodeViews: Matrix! 44 | private var _cachedPath: [Node]? 45 | 46 | override func awakeFromNib() { 47 | _nodeViews = Matrix(width: _gridSize, height: _gridSize, repeatedValue: { (x, y) in 48 | return nil 49 | }) 50 | _nodes = Matrix(width: _gridSize, height: _gridSize) { 51 | (x, y) -> Node in 52 | return Node(coordinates: Coordinates2D(x: x, y: y)) 53 | } 54 | _grid = Grid(nodes: self._nodes) 55 | // Create a node view for all nodes 56 | for (x, y, _) in _nodes { 57 | _nodeViews[x,y] = addNodeView(x, y) 58 | } 59 | // Reset type's of node views. 60 | reset() 61 | } 62 | 63 | public func viewControllerDidLayoutSubviews() { 64 | let nodeWidth: CGFloat = bounds.size.width / CGFloat(_gridSize) 65 | let nodeHeight: CGFloat = bounds.size.height / CGFloat(_gridSize) 66 | for (x, y, _) in _nodes { 67 | let nodeView = _nodeViews[x,y] 68 | nodeView!.frame = CGRect(x: CGFloat(x) * nodeWidth, y: CGFloat(y) * nodeHeight, width: nodeWidth, height: nodeHeight) 69 | } 70 | } 71 | 72 | public func reset() { 73 | // Reset type's of node views, making all .Empty except for one 74 | // .Start and one .End . 75 | for (x, y, _) in _nodes { 76 | let nodeView = _nodeViews[x,y]! 77 | if x == 0 && y == 0 { 78 | nodeView.type = .Start 79 | _startNodeView = nodeView 80 | } else if x == _gridSize - 1 && y == _gridSize - 1 { 81 | nodeView.type = .End 82 | _endNodeView = nodeView 83 | } else { 84 | nodeView.type = .Empty 85 | } 86 | } 87 | resetPath() 88 | } 89 | 90 | func addNodeView(_ x: Int, _ y: Int) -> NodeView { 91 | let nodeWidth: CGFloat = bounds.size.width / CGFloat(_gridSize) 92 | let nodeHeight: CGFloat = bounds.size.height / CGFloat(_gridSize) 93 | 94 | let node = _nodes[x, y] 95 | let nodeView = NodeView( 96 | frame: CGRect(x: CGFloat(x) * nodeWidth, y: CGFloat(y) * nodeHeight, width: nodeWidth, height: nodeHeight), 97 | node: node 98 | ) 99 | addSubview(nodeView) 100 | 101 | return nodeView 102 | } 103 | 104 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 105 | for touch in touches { 106 | let view = hitTest(touch.location(in: self), with: event) 107 | if let nodeView = view as? NodeView { 108 | switch nodeView.type { 109 | case .Start: 110 | _touchOperation = .Start 111 | case .End: 112 | _touchOperation = .End 113 | case .Empty: 114 | _touchOperation = .Draw 115 | case .Obstacle: 116 | _touchOperation = .Erase 117 | } 118 | touchMoveTo(nodeView: nodeView) 119 | break; 120 | } 121 | } 122 | } 123 | 124 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 125 | for touch in touches { 126 | let view = hitTest(touch.location(in: self), with: event) 127 | if let nodeView = view as? NodeView { 128 | touchMoveTo(nodeView: nodeView) 129 | break; 130 | } 131 | } 132 | } 133 | 134 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 135 | for touch in touches { 136 | let view = hitTest(touch.location(in: self), with: event) 137 | if let nodeView = view as? NodeView { 138 | touchMoveTo(nodeView: nodeView) 139 | break; 140 | } 141 | } 142 | _touchOperation = .Draw 143 | _touchNode = nil 144 | } 145 | 146 | func touchMoveTo(nodeView: NodeView) { 147 | if _touchNode === nodeView { return } 148 | switch _touchOperation { 149 | case .Start: 150 | switch nodeView.type { 151 | case .Empty: 152 | _startNodeView.type = .Empty 153 | _startNodeView = nodeView 154 | _startNodeView.type = .Start 155 | default: 156 | break 157 | } 158 | case .End: 159 | switch nodeView.type { 160 | case .Empty: 161 | _endNodeView.type = .Empty 162 | _endNodeView = nodeView 163 | _endNodeView.type = .End 164 | default: 165 | break 166 | } 167 | default: 168 | let coord2 = nodeView.node.coordinates as! Coordinates2D 169 | if (_touchNode == nil) { 170 | plotPoint(coord2.x,coord2.y) 171 | } else { 172 | let coord1 = _touchNode?.node.coordinates as! Coordinates2D 173 | plotLine(coord1.x,coord1.y,coord2.x,coord2.y) 174 | } 175 | } 176 | _touchNode = nodeView 177 | } 178 | 179 | func plotLine(_ x0:Int,_ y0:Int,_ x1:Int,_ y1:Int) { 180 | // Bresenham's line algorithm 181 | // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm 182 | // Creative Commons Attribution-ShareAlike 3.0 License 183 | if abs(y1 - y0) < abs(x1 - x0) { 184 | if x0 > x1 { 185 | plotLineLow(x1, y1, x0, y0) 186 | } else { 187 | plotLineLow(x0, y0, x1, y1) 188 | } 189 | } else { 190 | if y0 > y1 { 191 | plotLineHigh(x1, y1, x0, y0) 192 | } else { 193 | plotLineHigh(x0, y0, x1, y1) 194 | } 195 | } 196 | } 197 | 198 | func plotLineLow(_ x0:Int,_ y0:Int,_ x1:Int,_ y1:Int) { 199 | let dx = x1 - x0 200 | var dy = y1 - y0 201 | var yinc = 1 202 | if dy < 0 { 203 | yinc = -1 204 | dy = -dy 205 | } 206 | var D = 2*dy - dx 207 | var y = y0 208 | for x in x0...x1 { 209 | plotPoint(x,y) 210 | if D > 0 { 211 | y = y + yinc 212 | D = D - 2*dx 213 | } 214 | D = D + 2*dy 215 | } 216 | } 217 | 218 | func plotLineHigh(_ x0:Int,_ y0:Int,_ x1:Int,_ y1:Int) { 219 | var dx = x1 - x0 220 | let dy = y1 - y0 221 | var xinc = 1 222 | if dx < 0 { 223 | xinc = -1 224 | dx = -dx 225 | } 226 | var D = 2*dx - dy 227 | var x = x0 228 | for y in y0...y1 { 229 | plotPoint(x,y) 230 | if D > 0 { 231 | x = x + xinc 232 | D = D - 2*dy 233 | } 234 | D = D + 2*dx 235 | } 236 | } 237 | 238 | func plotPoint(_ x:Int,_ y:Int) { 239 | let nodeView = _nodeViews[x,y]! 240 | switch nodeView.type { 241 | case .Empty: 242 | if _touchOperation == .Draw { 243 | nodeView.type = .Obstacle 244 | } 245 | case .Obstacle: 246 | if _touchOperation == .Erase { 247 | nodeView.type = .Empty 248 | } 249 | default: 250 | break 251 | } 252 | } 253 | 254 | func resetPath() { 255 | for (_, _, node) in _nodes { 256 | node.reset() 257 | } 258 | 259 | for (_, _, nodeView) in _nodeViews { 260 | if nodeView!.node.parent != nil { nodeView!.node.parent = nil } 261 | if nodeView!.partOfPath { nodeView!.partOfPath = false } 262 | 263 | nodeView!.setNeedsDisplay() 264 | } 265 | } 266 | 267 | internal func findPath() { 268 | resetPath() 269 | 270 | self._cachedPath = AStarAlgorithm.findPathInMap(self._grid, startNode: self._startNodeView.node, endNode: self._endNodeView.node) 271 | 272 | for node in _cachedPath! { 273 | let coords = node.coordinates as! Coordinates2D 274 | if let nodeView = _nodeViews[coords.x, coords.y] { 275 | nodeView.partOfPath = true 276 | } 277 | } 278 | 279 | for (x, y, _) in _nodes { 280 | if let nodeView = _nodeViews[x, y] { 281 | if nodeView.node.parent != nil { nodeView.setNeedsDisplay() } 282 | } 283 | } 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "scale" : "1x", 5 | "idiom" : "ipad", 6 | "filename" : "Icon-40.png", 7 | "size" : "40x40" 8 | }, 9 | { 10 | "scale" : "2x", 11 | "idiom" : "ipad", 12 | "filename" : "Icon-40@2x.png", 13 | "size" : "40x40" 14 | }, 15 | { 16 | "scale" : "2x", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-60@2x.png", 19 | "size" : "60x60" 20 | }, 21 | { 22 | "scale" : "1x", 23 | "idiom" : "ipad", 24 | "filename" : "Icon-72.png", 25 | "size" : "72x72" 26 | }, 27 | { 28 | "scale" : "2x", 29 | "idiom" : "ipad", 30 | "filename" : "Icon-72@2x.png", 31 | "size" : "72x72" 32 | }, 33 | { 34 | "scale" : "1x", 35 | "idiom" : "ipad", 36 | "filename" : "Icon-76.png", 37 | "size" : "76x76" 38 | }, 39 | { 40 | "scale" : "2x", 41 | "idiom" : "ipad", 42 | "filename" : "Icon-76@2x.png", 43 | "size" : "76x76" 44 | }, 45 | { 46 | "scale" : "1x", 47 | "idiom" : "ipad", 48 | "filename" : "Icon-Small-50.png", 49 | "size" : "50x50" 50 | }, 51 | { 52 | "scale" : "2x", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-Small-50@2x.png", 55 | "size" : "50x50" 56 | }, 57 | { 58 | "scale" : "1x", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-Small.png", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "scale" : "2x", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-Small@2x.png", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "scale" : "1x", 71 | "idiom" : "iphone", 72 | "filename" : "Icon.png", 73 | "size" : "57x57" 74 | }, 75 | { 76 | "scale" : "2x", 77 | "idiom" : "iphone", 78 | "filename" : "Icon@2x.png", 79 | "size" : "57x57" 80 | }, 81 | { 82 | "scale" : "3x", 83 | "idiom" : "iphone", 84 | "filename" : "Icon-Small@3x.png", 85 | "size" : "29x29" 86 | }, 87 | { 88 | "scale" : "3x", 89 | "idiom" : "iphone", 90 | "filename" : "Icon-40@3x.png", 91 | "size" : "40x40" 92 | }, 93 | { 94 | "scale" : "3x", 95 | "idiom" : "iphone", 96 | "filename" : "Icon-60@3x.png", 97 | "size" : "60x60" 98 | }, 99 | { 100 | "scale" : "2x", 101 | "idiom" : "iphone", 102 | "filename" : "Icon-40@2x.png", 103 | "size" : "40x40" 104 | }, 105 | { 106 | "scale" : "1x", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small.png", 109 | "size" : "29x29" 110 | }, 111 | { 112 | "scale" : "2x", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small@2x.png", 115 | "size" : "29x29" 116 | }, 117 | { 118 | "scale" : "2x", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-83.5@2x.png", 121 | "size" : "83.5x83.5" 122 | }, 123 | { 124 | "scale" : "2x", 125 | "idiom" : "iphone", 126 | "filename" : "NotificationIcon@2x.png", 127 | "size" : "20x20" 128 | }, 129 | { 130 | "scale" : "3x", 131 | "idiom" : "iphone", 132 | "filename" : "NotificationIcon@3x.png", 133 | "size" : "20x20" 134 | }, 135 | { 136 | "scale" : "1x", 137 | "idiom" : "ipad", 138 | "filename" : "NotificationIcon~ipad.png", 139 | "size" : "20x20" 140 | }, 141 | { 142 | "scale" : "2x", 143 | "idiom" : "ipad", 144 | "filename" : "NotificationIcon~ipad@2x.png", 145 | "size" : "20x20" 146 | }, 147 | { 148 | "scale" : "1x", 149 | "idiom" : "ios-marketing", 150 | "filename" : "ios-marketing.png", 151 | "size" : "1024x1024" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon@3x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/Pathfinder-Demo/Images.xcassets/AppIcon.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /Pathfinder-Demo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Pathfinder-Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Pathfinder-Demo/NodeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NodeView.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 16/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import UIKit 27 | import Pathfinder 28 | 29 | public class NodeView: UIView { 30 | 31 | public enum PathType { 32 | case Empty, Start, End, Obstacle 33 | } 34 | 35 | public let node: Node 36 | private var _pathType: PathType = .Empty 37 | 38 | private var _partOfPath: Bool = false 39 | public var partOfPath: Bool { 40 | get { 41 | return _partOfPath 42 | } 43 | set(newValue) { 44 | _partOfPath = newValue 45 | setNeedsDisplay() 46 | } 47 | } 48 | 49 | public var type: PathType { 50 | get { 51 | return _pathType 52 | } 53 | set(newPathType) { 54 | _pathType = newPathType 55 | 56 | switch _pathType { 57 | case .Obstacle: 58 | node.accessible = false 59 | default: 60 | node.accessible = true 61 | } 62 | 63 | setNeedsDisplay() 64 | } 65 | } 66 | 67 | private var _color: UIColor { 68 | switch _pathType { 69 | case .Empty: 70 | return !partOfPath ? (node.parent == nil ? UIColor(white: 1.0, alpha: 1.0) : // Default 71 | UIColor(red: 0.83, green: 0.93, blue: 1.0, alpha: 1.0)) : // Open List 72 | UIColor(red: 0.43, green: 0.7, blue: 0.9, alpha: 1.0) // Path 73 | case .Obstacle: 74 | return UIColor(white: 0.4, alpha: 1.0) 75 | case .Start: 76 | return UIColor(red: 0.75, green: 0.23, blue: 0.19, alpha: 1.0) 77 | case .End: 78 | return UIColor(red: 0.22, green: 0.8, blue: 0.46, alpha: 1.0) 79 | } 80 | } 81 | 82 | init(frame: CGRect, node: Node) { 83 | self.node = node 84 | super.init(frame: frame) 85 | } 86 | 87 | // Compiler, shut up! 88 | required public init(coder aDecoder: NSCoder) { 89 | self.node = Node(coordinates: Coordinates2D(x: 0, y: 0)) 90 | super.init(coder: aDecoder)! 91 | } 92 | 93 | override public func draw(_ rect: CGRect) { 94 | _color.set() 95 | UIRectFill(rect) 96 | 97 | UIColor(white: 0.0, alpha: 0.1).setStroke() 98 | UIBezierPath(rect: bounds).stroke() 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /Pathfinder-Demo/Pathfinder-Demo-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | 6 | -------------------------------------------------------------------------------- /Pathfinder-Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Pathfinder-Demo 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import UIKit 27 | 28 | class ViewController: UIViewController { 29 | @IBOutlet weak var gridView: GridView! 30 | 31 | @IBAction func findPath(_ sender: Any) { 32 | gridView.findPath() 33 | } 34 | 35 | @IBAction func reset(_ sender: Any) { 36 | gridView.reset() 37 | } 38 | 39 | override func viewDidLayoutSubviews() { 40 | super.viewDidLayoutSubviews() 41 | gridView.viewControllerDidLayoutSubviews() 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9119C515199D962A001DB154 /* Pathfinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 9119C514199D962A001DB154 /* Pathfinder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 9119C529199D9633001DB154 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9119C528199D9633001DB154 /* Node.swift */; }; 12 | 9119C52B199D9663001DB154 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9119C52A199D9663001DB154 /* Map.swift */; }; 13 | 911CD2A5199FF26A00EA7BAD /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 911CD2A4199FF26A00EA7BAD /* Helpers.swift */; }; 14 | 911CD2A7199FFCA200EA7BAD /* NodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 911CD2A6199FFCA200EA7BAD /* NodeView.swift */; }; 15 | 911CD2AD19A00B1F00EA7BAD /* AStarAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 911CD2AC19A00B1F00EA7BAD /* AStarAlgorithm.swift */; }; 16 | 914147E3199F809C009DB333 /* Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 914147E2199F809C009DB333 /* Coordinates.swift */; }; 17 | 91761659199E220300253001 /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91761658199E220300253001 /* Algorithm.swift */; }; 18 | 91761665199E926500253001 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91761664199E926500253001 /* AppDelegate.swift */; }; 19 | 91761667199E926500253001 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91761666199E926500253001 /* ViewController.swift */; }; 20 | 9176166A199E926500253001 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 91761668199E926500253001 /* Main.storyboard */; }; 21 | 9176166C199E926500253001 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9176166B199E926500253001 /* Images.xcassets */; }; 22 | 9176167F199E929B00253001 /* Pathfinder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9119C50F199D962A001DB154 /* Pathfinder.framework */; }; 23 | 91761681199E972300253001 /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91761680199E972300253001 /* GridView.swift */; }; 24 | 91761683199E98AD00253001 /* Grid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91761682199E98AD00253001 /* Grid.swift */; }; 25 | 91761687199E99FB00253001 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91761686199E99FB00253001 /* Matrix.swift */; }; 26 | 9186EF8C19A0D20D00D1D4FA /* IndexedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9186EF8B19A0D20D00D1D4FA /* IndexedArray.swift */; }; 27 | 91DC3B9A19A11AD000FC9732 /* HeuristicFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91DC3B9919A11AD000FC9732 /* HeuristicFunction.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 9119C50F199D962A001DB154 /* Pathfinder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pathfinder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 9119C513199D962A001DB154 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 9119C514199D962A001DB154 /* Pathfinder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Pathfinder.h; sourceTree = ""; }; 34 | 9119C528199D9633001DB154 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; 35 | 9119C52A199D9663001DB154 /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; 36 | 911CD2A4199FF26A00EA7BAD /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; 37 | 911CD2A6199FFCA200EA7BAD /* NodeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NodeView.swift; sourceTree = ""; }; 38 | 911CD2AC19A00B1F00EA7BAD /* AStarAlgorithm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AStarAlgorithm.swift; sourceTree = ""; }; 39 | 914147E2199F809C009DB333 /* Coordinates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinates.swift; sourceTree = ""; }; 40 | 91761658199E220300253001 /* Algorithm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Algorithm.swift; sourceTree = ""; }; 41 | 91761660199E926500253001 /* Pathfinder-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pathfinder-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 91761663199E926500253001 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 91761664199E926500253001 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | 91761666199E926500253001 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 45 | 91761669199E926500253001 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 9176166B199E926500253001 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 47 | 91761680199E972300253001 /* GridView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridView.swift; sourceTree = ""; }; 48 | 91761682199E98AD00253001 /* Grid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Grid.swift; sourceTree = ""; }; 49 | 91761686199E99FB00253001 /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = ""; }; 50 | 9186EF8B19A0D20D00D1D4FA /* IndexedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexedArray.swift; sourceTree = ""; }; 51 | 91DC3B9919A11AD000FC9732 /* HeuristicFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeuristicFunction.swift; sourceTree = ""; }; 52 | 91DC3B9D19A138CE00FC9732 /* Pathfinder-Demo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Pathfinder-Demo-Bridging-Header.h"; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 9119C50B199D962A001DB154 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 9176165D199E926500253001 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 9176167F199E929B00253001 /* Pathfinder.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 9119C505199D962A001DB154 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 9119C511199D962A001DB154 /* Pathfinder */, 78 | 91761661199E926500253001 /* Pathfinder-Demo */, 79 | 9119C510199D962A001DB154 /* Products */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 9119C510199D962A001DB154 /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 9119C50F199D962A001DB154 /* Pathfinder.framework */, 87 | 91761660199E926500253001 /* Pathfinder-Demo.app */, 88 | ); 89 | name = Products; 90 | sourceTree = ""; 91 | }; 92 | 9119C511199D962A001DB154 /* Pathfinder */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 9119C514199D962A001DB154 /* Pathfinder.h */, 96 | 91761684199E98C100253001 /* Source */, 97 | 9119C512199D962A001DB154 /* Supporting Files */, 98 | ); 99 | path = Pathfinder; 100 | sourceTree = ""; 101 | }; 102 | 9119C512199D962A001DB154 /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 9119C513199D962A001DB154 /* Info.plist */, 106 | ); 107 | name = "Supporting Files"; 108 | sourceTree = ""; 109 | }; 110 | 911CD2AA19A00AF700EA7BAD /* Algorithms */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 911CD2AC19A00B1F00EA7BAD /* AStarAlgorithm.swift */, 114 | ); 115 | name = Algorithms; 116 | sourceTree = ""; 117 | }; 118 | 911CD2AB19A00B0600EA7BAD /* Helpers */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 9186EF8B19A0D20D00D1D4FA /* IndexedArray.swift */, 122 | 91761686199E99FB00253001 /* Matrix.swift */, 123 | 911CD2A4199FF26A00EA7BAD /* Helpers.swift */, 124 | ); 125 | name = Helpers; 126 | sourceTree = ""; 127 | }; 128 | 911CD2AE19A00BA900EA7BAD /* Core */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 91761658199E220300253001 /* Algorithm.swift */, 132 | 9119C528199D9633001DB154 /* Node.swift */, 133 | 914147E2199F809C009DB333 /* Coordinates.swift */, 134 | 9119C52A199D9663001DB154 /* Map.swift */, 135 | 91DC3B9919A11AD000FC9732 /* HeuristicFunction.swift */, 136 | ); 137 | name = Core; 138 | sourceTree = ""; 139 | }; 140 | 91761661199E926500253001 /* Pathfinder-Demo */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 91761664199E926500253001 /* AppDelegate.swift */, 144 | 91761680199E972300253001 /* GridView.swift */, 145 | 9176166B199E926500253001 /* Images.xcassets */, 146 | 91761668199E926500253001 /* Main.storyboard */, 147 | 911CD2A6199FFCA200EA7BAD /* NodeView.swift */, 148 | 91761662199E926500253001 /* Supporting Files */, 149 | 91761666199E926500253001 /* ViewController.swift */, 150 | ); 151 | path = "Pathfinder-Demo"; 152 | sourceTree = ""; 153 | }; 154 | 91761662199E926500253001 /* Supporting Files */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 91DC3B9D19A138CE00FC9732 /* Pathfinder-Demo-Bridging-Header.h */, 158 | 91761663199E926500253001 /* Info.plist */, 159 | ); 160 | name = "Supporting Files"; 161 | sourceTree = ""; 162 | }; 163 | 91761684199E98C100253001 /* Source */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 911CD2AA19A00AF700EA7BAD /* Algorithms */, 167 | 911CD2AE19A00BA900EA7BAD /* Core */, 168 | 91761685199E98C900253001 /* Maps */, 169 | 911CD2AB19A00B0600EA7BAD /* Helpers */, 170 | ); 171 | name = Source; 172 | sourceTree = ""; 173 | }; 174 | 91761685199E98C900253001 /* Maps */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 91761682199E98AD00253001 /* Grid.swift */, 178 | ); 179 | name = Maps; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXHeadersBuildPhase section */ 185 | 9119C50C199D962A001DB154 /* Headers */ = { 186 | isa = PBXHeadersBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 9119C515199D962A001DB154 /* Pathfinder.h in Headers */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXHeadersBuildPhase section */ 194 | 195 | /* Begin PBXNativeTarget section */ 196 | 9119C50E199D962A001DB154 /* Pathfinder */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 9119C522199D962A001DB154 /* Build configuration list for PBXNativeTarget "Pathfinder" */; 199 | buildPhases = ( 200 | 9119C50A199D962A001DB154 /* Sources */, 201 | 9119C50B199D962A001DB154 /* Frameworks */, 202 | 9119C50C199D962A001DB154 /* Headers */, 203 | 9119C50D199D962A001DB154 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | ); 209 | name = Pathfinder; 210 | productName = Pathfinder; 211 | productReference = 9119C50F199D962A001DB154 /* Pathfinder.framework */; 212 | productType = "com.apple.product-type.framework"; 213 | }; 214 | 9176165F199E926500253001 /* Pathfinder-Demo */ = { 215 | isa = PBXNativeTarget; 216 | buildConfigurationList = 91761679199E926500253001 /* Build configuration list for PBXNativeTarget "Pathfinder-Demo" */; 217 | buildPhases = ( 218 | 9176165C199E926500253001 /* Sources */, 219 | 9176165D199E926500253001 /* Frameworks */, 220 | 9176165E199E926500253001 /* Resources */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | ); 226 | name = "Pathfinder-Demo"; 227 | productName = "Pathfinder-Demo"; 228 | productReference = 91761660199E926500253001 /* Pathfinder-Demo.app */; 229 | productType = "com.apple.product-type.application"; 230 | }; 231 | /* End PBXNativeTarget section */ 232 | 233 | /* Begin PBXProject section */ 234 | 9119C506199D962A001DB154 /* Project object */ = { 235 | isa = PBXProject; 236 | attributes = { 237 | LastUpgradeCheck = 1000; 238 | ORGANIZATIONNAME = "Ilija Tovilo"; 239 | TargetAttributes = { 240 | 9119C50E199D962A001DB154 = { 241 | CreatedOnToolsVersion = 6.0; 242 | LastSwiftMigration = 0800; 243 | }; 244 | 9176165F199E926500253001 = { 245 | CreatedOnToolsVersion = 6.0; 246 | }; 247 | }; 248 | }; 249 | buildConfigurationList = 9119C509199D962A001DB154 /* Build configuration list for PBXProject "Pathfinder" */; 250 | compatibilityVersion = "Xcode 3.2"; 251 | developmentRegion = English; 252 | hasScannedForEncodings = 0; 253 | knownRegions = ( 254 | en, 255 | Base, 256 | ); 257 | mainGroup = 9119C505199D962A001DB154; 258 | productRefGroup = 9119C510199D962A001DB154 /* Products */; 259 | projectDirPath = ""; 260 | projectRoot = ""; 261 | targets = ( 262 | 9119C50E199D962A001DB154 /* Pathfinder */, 263 | 9176165F199E926500253001 /* Pathfinder-Demo */, 264 | ); 265 | }; 266 | /* End PBXProject section */ 267 | 268 | /* Begin PBXResourcesBuildPhase section */ 269 | 9119C50D199D962A001DB154 /* Resources */ = { 270 | isa = PBXResourcesBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | 9176165E199E926500253001 /* Resources */ = { 277 | isa = PBXResourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | 9176166A199E926500253001 /* Main.storyboard in Resources */, 281 | 9176166C199E926500253001 /* Images.xcassets in Resources */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | /* End PBXResourcesBuildPhase section */ 286 | 287 | /* Begin PBXSourcesBuildPhase section */ 288 | 9119C50A199D962A001DB154 /* Sources */ = { 289 | isa = PBXSourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 914147E3199F809C009DB333 /* Coordinates.swift in Sources */, 293 | 9119C52B199D9663001DB154 /* Map.swift in Sources */, 294 | 911CD2AD19A00B1F00EA7BAD /* AStarAlgorithm.swift in Sources */, 295 | 9186EF8C19A0D20D00D1D4FA /* IndexedArray.swift in Sources */, 296 | 91761659199E220300253001 /* Algorithm.swift in Sources */, 297 | 911CD2A5199FF26A00EA7BAD /* Helpers.swift in Sources */, 298 | 91DC3B9A19A11AD000FC9732 /* HeuristicFunction.swift in Sources */, 299 | 91761687199E99FB00253001 /* Matrix.swift in Sources */, 300 | 91761683199E98AD00253001 /* Grid.swift in Sources */, 301 | 9119C529199D9633001DB154 /* Node.swift in Sources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | 9176165C199E926500253001 /* Sources */ = { 306 | isa = PBXSourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | 91761667199E926500253001 /* ViewController.swift in Sources */, 310 | 91761681199E972300253001 /* GridView.swift in Sources */, 311 | 911CD2A7199FFCA200EA7BAD /* NodeView.swift in Sources */, 312 | 91761665199E926500253001 /* AppDelegate.swift in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXSourcesBuildPhase section */ 317 | 318 | /* Begin PBXVariantGroup section */ 319 | 91761668199E926500253001 /* Main.storyboard */ = { 320 | isa = PBXVariantGroup; 321 | children = ( 322 | 91761669199E926500253001 /* Base */, 323 | ); 324 | name = Main.storyboard; 325 | sourceTree = ""; 326 | }; 327 | /* End PBXVariantGroup section */ 328 | 329 | /* Begin XCBuildConfiguration section */ 330 | 9119C520199D962A001DB154 /* Debug */ = { 331 | isa = XCBuildConfiguration; 332 | buildSettings = { 333 | ALWAYS_SEARCH_USER_PATHS = NO; 334 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 335 | CLANG_CXX_LIBRARY = "libc++"; 336 | CLANG_ENABLE_MODULES = YES; 337 | CLANG_ENABLE_OBJC_ARC = YES; 338 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 339 | CLANG_WARN_BOOL_CONVERSION = YES; 340 | CLANG_WARN_COMMA = YES; 341 | CLANG_WARN_CONSTANT_CONVERSION = YES; 342 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 344 | CLANG_WARN_EMPTY_BODY = YES; 345 | CLANG_WARN_ENUM_CONVERSION = YES; 346 | CLANG_WARN_INFINITE_RECURSION = YES; 347 | CLANG_WARN_INT_CONVERSION = YES; 348 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 350 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 351 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 352 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 353 | CLANG_WARN_STRICT_PROTOTYPES = YES; 354 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 355 | CLANG_WARN_UNREACHABLE_CODE = YES; 356 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 357 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 358 | COPY_PHASE_STRIP = NO; 359 | CURRENT_PROJECT_VERSION = 1; 360 | ENABLE_STRICT_OBJC_MSGSEND = YES; 361 | ENABLE_TESTABILITY = YES; 362 | GCC_C_LANGUAGE_STANDARD = gnu99; 363 | GCC_DYNAMIC_NO_PIC = NO; 364 | GCC_NO_COMMON_BLOCKS = YES; 365 | GCC_OPTIMIZATION_LEVEL = 0; 366 | GCC_PREPROCESSOR_DEFINITIONS = ( 367 | "DEBUG=1", 368 | "$(inherited)", 369 | ); 370 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 373 | GCC_WARN_UNDECLARED_SELECTOR = YES; 374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 375 | GCC_WARN_UNUSED_FUNCTION = YES; 376 | GCC_WARN_UNUSED_VARIABLE = YES; 377 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 378 | MTL_ENABLE_DEBUG_INFO = YES; 379 | ONLY_ACTIVE_ARCH = YES; 380 | SDKROOT = iphoneos; 381 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 382 | TARGETED_DEVICE_FAMILY = "1,2"; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | VERSION_INFO_PREFIX = ""; 385 | }; 386 | name = Debug; 387 | }; 388 | 9119C521199D962A001DB154 /* Release */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | ALWAYS_SEARCH_USER_PATHS = NO; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = YES; 417 | CURRENT_PROJECT_VERSION = 1; 418 | ENABLE_NS_ASSERTIONS = NO; 419 | ENABLE_STRICT_OBJC_MSGSEND = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_NO_COMMON_BLOCKS = YES; 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 429 | MTL_ENABLE_DEBUG_INFO = NO; 430 | SDKROOT = iphoneos; 431 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 432 | TARGETED_DEVICE_FAMILY = "1,2"; 433 | VALIDATE_PRODUCT = YES; 434 | VERSIONING_SYSTEM = "apple-generic"; 435 | VERSION_INFO_PREFIX = ""; 436 | }; 437 | name = Release; 438 | }; 439 | 9119C523199D962A001DB154 /* Debug */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | CLANG_ENABLE_MODULES = YES; 443 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 444 | DEFINES_MODULE = YES; 445 | DYLIB_COMPATIBILITY_VERSION = 1; 446 | DYLIB_CURRENT_VERSION = 1; 447 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 448 | INFOPLIST_FILE = Pathfinder/Info.plist; 449 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 451 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.$(PRODUCT_NAME:rfc1034identifier)"; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SKIP_INSTALL = YES; 454 | SWIFT_VERSION = 4.2; 455 | }; 456 | name = Debug; 457 | }; 458 | 9119C524199D962A001DB154 /* Release */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | CLANG_ENABLE_MODULES = YES; 462 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 463 | DEFINES_MODULE = YES; 464 | DYLIB_COMPATIBILITY_VERSION = 1; 465 | DYLIB_CURRENT_VERSION = 1; 466 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 467 | INFOPLIST_FILE = Pathfinder/Info.plist; 468 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 469 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 470 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.$(PRODUCT_NAME:rfc1034identifier)"; 471 | PRODUCT_NAME = "$(TARGET_NAME)"; 472 | SKIP_INSTALL = YES; 473 | SWIFT_VERSION = 4.2; 474 | }; 475 | name = Release; 476 | }; 477 | 9176167A199E926500253001 /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 481 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 482 | CLANG_ENABLE_MODULES = YES; 483 | GCC_PREPROCESSOR_DEFINITIONS = ( 484 | "DEBUG=1", 485 | "$(inherited)", 486 | ); 487 | INFOPLIST_FILE = "Pathfinder-Demo/Info.plist"; 488 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 489 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 490 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.$(PRODUCT_NAME:rfc1034identifier)"; 491 | PRODUCT_NAME = "$(TARGET_NAME)"; 492 | SWIFT_OBJC_BRIDGING_HEADER = "Pathfinder-Demo/Pathfinder-Demo-Bridging-Header.h"; 493 | SWIFT_VERSION = 4.2; 494 | }; 495 | name = Debug; 496 | }; 497 | 9176167B199E926500253001 /* Release */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 501 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 502 | CLANG_ENABLE_MODULES = YES; 503 | INFOPLIST_FILE = "Pathfinder-Demo/Info.plist"; 504 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 505 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 506 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.$(PRODUCT_NAME:rfc1034identifier)"; 507 | PRODUCT_NAME = "$(TARGET_NAME)"; 508 | SWIFT_OBJC_BRIDGING_HEADER = "Pathfinder-Demo/Pathfinder-Demo-Bridging-Header.h"; 509 | SWIFT_VERSION = 4.2; 510 | }; 511 | name = Release; 512 | }; 513 | /* End XCBuildConfiguration section */ 514 | 515 | /* Begin XCConfigurationList section */ 516 | 9119C509199D962A001DB154 /* Build configuration list for PBXProject "Pathfinder" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 9119C520199D962A001DB154 /* Debug */, 520 | 9119C521199D962A001DB154 /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 9119C522199D962A001DB154 /* Build configuration list for PBXNativeTarget "Pathfinder" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 9119C523199D962A001DB154 /* Debug */, 529 | 9119C524199D962A001DB154 /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | 91761679199E926500253001 /* Build configuration list for PBXNativeTarget "Pathfinder-Demo" */ = { 535 | isa = XCConfigurationList; 536 | buildConfigurations = ( 537 | 9176167A199E926500253001 /* Debug */, 538 | 9176167B199E926500253001 /* Release */, 539 | ); 540 | defaultConfigurationIsVisible = 0; 541 | defaultConfigurationName = Release; 542 | }; 543 | /* End XCConfigurationList section */ 544 | }; 545 | rootObject = 9119C506199D962A001DB154 /* Project object */; 546 | } 547 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/xcuserdata/ilijatovilo.xcuserdatad/xcschemes/Pathfinder-Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/xcuserdata/ilijatovilo.xcuserdatad/xcschemes/Pathfinder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/xcuserdata/ilijatovilo.xcuserdatad/xcschemes/PathfinderTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Pathfinder.xcodeproj/xcuserdata/ilijatovilo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pathfinder-Demo.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | Pathfinder.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | PathfinderTests.xcscheme 18 | 19 | orderHint 20 | 1 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 9119C50E199D962A001DB154 26 | 27 | primary 28 | 29 | 30 | 9119C519199D962A001DB154 31 | 32 | primary 33 | 34 | 35 | 9176165F199E926500253001 36 | 37 | primary 38 | 39 | 40 | 91761670199E926500253001 41 | 42 | primary 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Pathfinder/AStarAlgorithm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AStarAlgorithm.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 16/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// AStarAlgorithm is a class that implements the A* Algorithm for pathfinding 29 | /// http://en.wikipedia.org/wiki/A*_search_algorithm 30 | open class AStarAlgorithm: Algorithm { 31 | 32 | /// Finds the path from point A to B in any Map 33 | open class func findPathInMap(_ map: Map, startNode: Node, endNode: Node) -> [Node] { 34 | // var openList: [Node] = [startNode] 35 | let openList = IndexedArray(extractIndex: { (node) in return node.fValue }) 36 | openList.add(startNode) 37 | 38 | // Add the neighbours of the start node to the open list to start the iteration process 39 | while let currentNode = openList.array.first { 40 | currentNode.closed = true 41 | openList.removeAtIndex(0) 42 | 43 | // Check if we reached the end node 44 | if currentNode == endNode { 45 | return backtracePath(endNode) 46 | } 47 | 48 | // Returns the neighbours of the node 49 | let validMoves = map.validMoves(currentNode) 50 | 51 | // Check each neighbour and add it to the open list 52 | for neighbour in validMoves { 53 | // If we can't access the tile we have to skip it 54 | if !neighbour.accessible { continue } 55 | // Calculate the move cost 56 | let moveCost = map.moveCostForNode(currentNode, toNode: neighbour) 57 | // We don't check the node if it's in the closed list 58 | if neighbour.closed && (currentNode.gValue + moveCost) >= neighbour.gValue { 59 | continue 60 | } 61 | 62 | if neighbour.opened { 63 | // The node was already added to the open list 64 | // We need to check if we have to re-parent it 65 | neighbour.parent = currentNode 66 | neighbour.gValue = currentNode.gValue + moveCost 67 | 68 | if !neighbour.closed { 69 | // Re-add it the the open list so it's sorted 70 | openList.removeAtIndex(openList.array.index(of:neighbour)!) 71 | openList.add(neighbour) 72 | } 73 | } else { 74 | // Set the parent of the node 75 | neighbour.parent = currentNode 76 | 77 | // Calculate the g value 78 | neighbour.gValue = currentNode.gValue + moveCost 79 | 80 | // Calculate the h value 81 | neighbour.hValue = map.hValueForNode(neighbour, endNode: endNode) 82 | 83 | // Add the new node to the open list 84 | neighbour.opened = true 85 | openList.add(neighbour) 86 | } 87 | } 88 | } 89 | 90 | // If there is no route, we just return an empty array 91 | return [] 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /Pathfinder/Algorithm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Algorithm.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// Algorithm is a protocol that defines the interface for the pathfinding algorithm implementations 29 | public protocol Algorithm: class { 30 | 31 | /// Finds the best path in a map from point A to B 32 | static func findPathInMap(_ map: Map, startNode: Node, endNode: Node) -> [Node] 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Pathfinder/Coordinates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinates.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 16/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// Coordinates stores coordinate of any type of coordinate system 29 | 30 | open class Coordinates { 31 | 32 | /// Wraps the coordinates in an array 33 | open func toArray() -> [Int] { 34 | assert(false, "`toArray` must be overridden in the Coordinates subclasses!") 35 | return [] 36 | } 37 | 38 | 39 | // ------------------ 40 | // MARK: - Hashable - 41 | // ------------------ 42 | 43 | // TODO: "Error: Declarations in extensions cannot override yet". 44 | // Move this to the extension once that feature is supported by Swift. 45 | open var hashValue: Int { 46 | return 0 47 | } 48 | 49 | } 50 | 51 | 52 | // ------------------ 53 | // MARK: - Hashable - 54 | // ------------------ 55 | 56 | extension Coordinates: Hashable { 57 | 58 | } 59 | 60 | 61 | // ------------------- 62 | // MARK: - Equatable - 63 | // ------------------- 64 | 65 | extension Coordinates: Equatable {} 66 | public func ==(lhs: Coordinates, rhs: Coordinates) -> Bool { 67 | return lhs === rhs 68 | } 69 | -------------------------------------------------------------------------------- /Pathfinder/Grid.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Grid.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | 29 | // --------------------------------------------------------------------- 30 | // MARK: - Coordinates2D - 31 | // --------------------------------------------------------------------- 32 | 33 | /// Entity that stores 2d coordinates 34 | open class Coordinates2D: Coordinates { 35 | 36 | // -------------------- 37 | // MARK: - Properties - 38 | // -------------------- 39 | 40 | /// The x coordinate 41 | @objc 42 | public let x: Int 43 | 44 | /// The y coordinate 45 | @objc 46 | public let y: Int 47 | 48 | /// Inits the object using both x and y coordinate 49 | @objc 50 | public init(x: Int, y: Int) { 51 | self.x = x 52 | self.y = y 53 | } 54 | 55 | 56 | 57 | // ----------------- 58 | // MARK: - Methods - 59 | // ----------------- 60 | 61 | override open func toArray() -> [Int] { 62 | return [x, y] 63 | } 64 | 65 | 66 | // ------------------ 67 | // MARK: - Hashable - 68 | // ------------------ 69 | 70 | // TODO: "Error: Declarations in extensions cannot override yet". 71 | // Move this to the extension once that feature is supported by Swift. 72 | // 73 | // Combines the x and y value as a hash 74 | // http://en.wikipedia.org/wiki/Cantor%5Fpairing%5Ffunction#Cantor_pairing_function 75 | override open var hashValue: Int { 76 | return (x + y)*(x + y + 1)/2 + y 77 | } 78 | } 79 | 80 | // ------------------- 81 | // MARK: - Printable - 82 | // ------------------- 83 | extension Coordinates2D: CustomStringConvertible { 84 | public var description: String { 85 | return "(\(x),\(y))" 86 | } 87 | } 88 | 89 | // ------------------- 90 | // MARK: - Equatable - 91 | // ------------------- 92 | public func ==(lhs: Coordinates2D, rhs: Coordinates2D) -> Bool { 93 | return (lhs.x == rhs.x && lhs.y == rhs.y) 94 | } 95 | 96 | 97 | 98 | // --------------------------------------------------------------------- 99 | // MARK: - Grid2D - 100 | // --------------------------------------------------------------------- 101 | 102 | /// Grid is an implementation of a 2d map 103 | open class Grid: Map { 104 | 105 | // -------------------- 106 | // MARK: - Properties - 107 | // -------------------- 108 | 109 | /// The matrix stores the tiles of the map 110 | fileprivate let _nodes: Matrix 111 | 112 | /// Indicates if the path is allowed to use diagonal moves 113 | // FIXME: Sometimes the path makes weird detours 114 | // FIXME: Allows making directional moves when there are adjacent neighbours 115 | @objc 116 | open var allowsDiagonalMoves = false 117 | 118 | /// Indicates if the path is allowed to use diagonal moves next to a corner 119 | // NOTE: Unimplemented 120 | // TODO: Implement 121 | @objc 122 | open var allowsCuttingCorners = true 123 | 124 | 125 | 126 | // -------------- 127 | // MARK: - Init - 128 | // -------------- 129 | 130 | /// Init the map using the 2d grid 131 | // TODO: @objc once Matrix is @objc 132 | public init(nodes: Matrix) { 133 | _nodes = nodes 134 | } 135 | 136 | 137 | 138 | // ----------------- 139 | // MARK: - Methods - 140 | // ----------------- 141 | 142 | /// Returns the valid moves that can be performed from one node to the other 143 | override internal func validMoves(_ node: Node) -> [Node] { 144 | let index = node.coordinates as! Coordinates2D 145 | var moves = [Node]() 146 | 147 | // Those are the delta values from one node coordinate to the other 148 | let adjacentNeighbours = [(1, 0), (0, 1), (-1, 0), (0, -1)] 149 | let diagonalNeighbours = [(1, 1), (-1, 1), (-1, -1), (1, -1)] 150 | let neighbours = allowsDiagonalMoves ? adjacentNeighbours + diagonalNeighbours : adjacentNeighbours 151 | 152 | for (dX, dY) in neighbours { 153 | let x = index.x + dX 154 | let y = index.y + dY 155 | 156 | if x >= 0 && x < _nodes.width { 157 | if y >= 0 && y < _nodes.height { 158 | moves.append(_nodes[x, y]) 159 | } 160 | } 161 | } 162 | 163 | return moves 164 | } 165 | 166 | /// Calculates the move cost from one node to one of it's neighbour nodes 167 | override internal func moveCostForNode(_ node: Node, toNode: Node) -> Int { 168 | let index = node.coordinates as! Coordinates2D 169 | let toIndex = toNode.coordinates as! Coordinates2D 170 | 171 | return ((abs(index.x - toIndex.x) > 0 && abs(index.y - toIndex.y) > 0) ? 10 : 14) 172 | } 173 | 174 | /// Calculates the h value of a node 175 | override internal func hValueForNode(_ node: Node, endNode: Node) -> Int { 176 | let coord1 = node.coordinates as! Coordinates2D 177 | let coord2 = endNode.coordinates as! Coordinates2D 178 | 179 | switch heuristicFunction { 180 | case .manhattan: 181 | return (abs(coord1.x - coord2.x) + abs(coord1.y - coord2.y)) * 40 182 | } 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /Pathfinder/Helpers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Helpers.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 16/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// Measures the time of a closure 29 | public func measureTime(_ block: () -> ()) -> Double { 30 | // Record start time 31 | let start = Date() 32 | // Execute the closure 33 | block() 34 | // Record end time 35 | let end = Date() 36 | 37 | // Calculate and return the delta time 38 | return end.timeIntervalSince(start) 39 | } 40 | 41 | /// Build a path from linked nodes 42 | internal func backtracePath(_ endNode: Node) -> [Node] { 43 | var route = [endNode] 44 | 45 | // Recursive lookup of the nodes parent 46 | while let parent = route.first?.parent { 47 | route.insert(parent, at: 0) 48 | } 49 | 50 | return route 51 | } 52 | -------------------------------------------------------------------------------- /Pathfinder/HeuristicFunction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeuristicFunction.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 17/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// Lists the functions that can be used to calculate the heuristic value 29 | public enum HeuristicFunction { 30 | /// dX + dY (+ dZ) 31 | case manhattan 32 | } 33 | -------------------------------------------------------------------------------- /Pathfinder/IndexedArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndexedArray.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 17/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// An array that is always sorted by an index 29 | /// This dramatically improves the performance of arrays that need to be sorted frequently 30 | open class IndexedArray { 31 | 32 | // -------------------- 33 | // MARK: - Properties - 34 | // -------------------- 35 | 36 | /// The backed array 37 | open fileprivate(set) var array = [T]() 38 | 39 | /// A closure that extracts the index from an array element 40 | fileprivate let _extractIndex: (T) -> U 41 | 42 | 43 | 44 | // -------------- 45 | // MARK: - Init - 46 | // -------------- 47 | 48 | /// Init the IndexedArray using the index extraction closure 49 | public init(extractIndex: @escaping (T) -> U) { 50 | _extractIndex = extractIndex 51 | } 52 | 53 | 54 | 55 | // ----------------- 56 | // MARK: - Methods - 57 | // ----------------- 58 | 59 | /// Add an element to the array 60 | /// The element will automatically be inserted in the right place 61 | open func add(_ element: T) { 62 | // Find the correct index 63 | let index = findIndex(element) 64 | 65 | if index == array.count { 66 | // Append the element if the array is not long enough 67 | array.append(element) 68 | } else { 69 | // Finally, insert the element in the right index 70 | array.insert(element, at: index) 71 | } 72 | } 73 | 74 | /// Force-sort if the index values were changed 75 | open func sort() { 76 | _ = array.sorted { self._extractIndex($0) < self._extractIndex($1) } 77 | } 78 | 79 | /// Removes the element at index from the array 80 | open func removeAtIndex(_ index: Int) { 81 | array.remove(at: index) 82 | } 83 | 84 | /// Finds the correct index in the array for an element 85 | fileprivate func findIndex(_ element: T) -> Int { 86 | // The index that the array is ordered by 87 | let index = _extractIndex(element) 88 | // The index we're currently using 89 | var currentSlice = halveIndex(array.count) 90 | var arrayIndex = currentSlice 91 | // Return 0 if there are no elements 92 | if array.count == 0 { return 0 } 93 | 94 | var iterations = 0 95 | while arrayIndex >= 0 && arrayIndex <= array.count { 96 | iterations += 1 97 | // Is this the correct index? 98 | var correctIndex = true 99 | var forward = true 100 | 101 | // Check if the element is correctly positioned 102 | if arrayIndex < array.count { 103 | let currentIndex = _extractIndex(array[arrayIndex]) 104 | if currentIndex < index { correctIndex = false } 105 | } 106 | if arrayIndex-1 >= 0 { 107 | let previousIndex = _extractIndex(array[arrayIndex-1]) 108 | if previousIndex > index { 109 | correctIndex = false 110 | forward = false 111 | } 112 | } 113 | 114 | // Return the index if it's correct 115 | if correctIndex { return arrayIndex } 116 | // Halve the slice again 117 | currentSlice = halveIndex(currentSlice) 118 | // Depending on the size of the index we either lower or raise the index 119 | arrayIndex += forward ? currentSlice : -currentSlice 120 | } 121 | 122 | // There was nothing found, we just return 0 123 | return 0 124 | } 125 | 126 | /// Halves the index (floor(x / 2.0)) 127 | /// The index can't be 0 though 128 | fileprivate func halveIndex(_ index: Int) -> Int { 129 | let index = Int(floor(Double(index) / 2.0)) 130 | return index > 0 ? index : 1 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /Pathfinder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pathfinder/Map.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Map.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// Map is an abstract class that can be overridden to created any type of map 29 | /// TODO: I ran into issues when making the Map a protocol. I'll try to do this in the future. 30 | open class Map { 31 | 32 | // -------------------- 33 | // MARK: - Properties - 34 | // -------------------- 35 | 36 | /// The function used to calculate the heuristic value 37 | var heuristicFunction: HeuristicFunction = .manhattan 38 | 39 | 40 | 41 | // ----------------- 42 | // MARK: - Methods - 43 | // ----------------- 44 | 45 | /// Returns the valid moves that can be performed from one node to the other 46 | func validMoves(_ node: Node) -> [Node] { 47 | assert(false, "Unimplemented") 48 | return [] 49 | } 50 | 51 | /// Calculates the hValue from a node to the end node 52 | func hValueForNode(_ node: Node, endNode: Node) -> Int { 53 | assert(false, "Unimplemented") 54 | return 0 55 | } 56 | 57 | /// Calculates the move cost from one node to one of it's neighbour nodes 58 | func moveCostForNode(_ node: Node, toNode: Node) -> Int { 59 | assert(false, "Unimplemented") 60 | return 0 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Pathfinder/Matrix.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Matrix.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 30/07/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// A matrix is able to store values in 2 dimensional form 29 | // TODO: Make Objective-C compatible 30 | open class Matrix { 31 | 32 | // -------------------- 33 | // MARK: - Properties - 34 | // -------------------- 35 | 36 | /// The width of the matrix 37 | public let width: Int 38 | 39 | /// The height of the matrix 40 | public let height: Int 41 | 42 | /// Backed array that stores the elements of the matrix 43 | fileprivate var _elements: [T] 44 | 45 | 46 | 47 | // -------------- 48 | // MARK: - Init - 49 | // -------------- 50 | 51 | /// Init the matrix with a width, height, and a repeated value 52 | /// The repeated value is used, because a matrix can never have empty indexes 53 | /// If you specifically need empty indexes, just use Optionals 54 | public init(width: Int, height: Int, repeatedValue: (_ x: Int, _ y: Int) -> T) { 55 | // Sanity check 56 | assert(width >= 0, "The width of the matrix needs to be larger or equal to 0.") 57 | assert(height >= 0, "The height of the matrix needs to be larger or equal to 0.") 58 | 59 | // Init the variables 60 | self.width = width 61 | self.height = height 62 | _elements = Array(repeating: repeatedValue(0, 0), count: width*height) 63 | 64 | // Fill the matrix with the repeated value 65 | for x in 0.. T { 80 | get { 81 | // Sanity check 82 | assert(x >= 0 && x < self.width, "x needs to be larger or equal to zero and smaller than the width of the matrix.") 83 | assert(y >= 0 && y < self.height, "y needs to be larger or equal to zero and smaller than the height of the matrix.") 84 | 85 | return _elements[x + (y * width)] 86 | } 87 | set(newValue) { 88 | // Sanity check 89 | assert(x >= 0 && x < self.width, "x needs to be larger or equal to zero and smaller than the width of the matrix.") 90 | assert(y >= 0 && y < self.height, "y needs to be larger or equal to zero and smaller than the height of the matrix.") 91 | 92 | _elements[x + (y * width)] = newValue 93 | } 94 | } 95 | 96 | } 97 | 98 | 99 | 100 | // ---------------------- 101 | // MARK: - SequenceType - 102 | // ---------------------- 103 | 104 | extension Matrix: Sequence { 105 | /// Returns a generator to loop through the matrix 106 | public func makeIterator() -> MatrixGenerator { 107 | return MatrixGenerator(matrix: self) 108 | } 109 | } 110 | 111 | /// Generator used to enumerate through the matrix 112 | open class MatrixGenerator: IteratorProtocol { 113 | /// The backed matrix 114 | fileprivate let _matrix: Matrix 115 | /// The current x value 116 | fileprivate var _x = 0 117 | /// The current y value 118 | fileprivate var _y = 0 119 | 120 | /// Init the matrix generator using the matrix 121 | init(matrix: Matrix) { 122 | _matrix = matrix 123 | } 124 | 125 | /// Step through the matrix 126 | open func next() -> (x: Int, y: Int, element: T)? { 127 | // Sanity check 128 | if _x >= _matrix.width { return nil } 129 | if _y >= _matrix.height { return nil } 130 | 131 | // Extract the element and increase the counters 132 | let returnValue = (_x, _y, _matrix[_x, _y]) 133 | 134 | // Increase the counters 135 | _x += 1 136 | if _x >= _matrix.width { 137 | _x = 0 138 | _y += 1 139 | } 140 | 141 | return returnValue 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /Pathfinder/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Node.swift 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | import Foundation 27 | 28 | /// Defines a unit on a map 29 | open class Node { 30 | 31 | // -------------- 32 | // MARK: - Init - 33 | // -------------- 34 | 35 | /// Init the node using it's coordinate 36 | /// The coordinate never changes 37 | public init(coordinates: Coordinates) { 38 | self.coordinates = coordinates 39 | } 40 | 41 | 42 | // -------------------- 43 | // MARK: - Properties - 44 | // -------------------- 45 | 46 | /// The coordinates of the node in the map 47 | public let coordinates: Coordinates 48 | 49 | /// Indicates if the node has been opened 50 | @objc 51 | open var opened: Bool = false 52 | 53 | /// Indicates if the node has been closed 54 | @objc 55 | open var closed: Bool = false 56 | 57 | /// Indicates if the node is accessible 58 | @objc 59 | open var accessible: Bool = true 60 | 61 | /// Heuristic Value 62 | @objc 63 | open var hValue: Int = 0 64 | 65 | /// Move Cost (+ move cost of parent) 66 | @objc 67 | open var gValue: Int = 0 68 | 69 | /// The total cost (h + g) 70 | @objc 71 | open var fValue: Int { 72 | return hValue + gValue 73 | } 74 | 75 | /// The parent node is used to lead back to the start node 76 | open var parent: Node? 77 | 78 | 79 | 80 | // ----------------- 81 | // MARK: - Methods - 82 | // ----------------- 83 | 84 | /// Reset the nodes properties 85 | @objc 86 | open func reset() { 87 | opened = false 88 | closed = false 89 | hValue = 0 90 | gValue = 0 91 | parent = nil 92 | } 93 | 94 | } 95 | 96 | 97 | 98 | // ------------------ 99 | // MARK: - Hashable - 100 | // ------------------ 101 | 102 | extension Node: Hashable { 103 | @objc 104 | public var hashValue: Int { 105 | return coordinates.hashValue 106 | } 107 | } 108 | 109 | 110 | 111 | // ------------------- 112 | // MARK: - Printable - 113 | // ------------------- 114 | 115 | extension Node: CustomStringConvertible { 116 | @objc 117 | public var description: String { 118 | return "" 119 | } 120 | 121 | } 122 | 123 | 124 | // ------------------- 125 | // MARK: - Equatable - 126 | // ------------------- 127 | 128 | extension Node: Equatable {} 129 | public func ==(l: Node, r: Node) -> Bool { 130 | return l === r 131 | } 132 | -------------------------------------------------------------------------------- /Pathfinder/Pathfinder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Pathfinder.h 3 | // Pathfinder 4 | // 5 | // Created by Ilija Tovilo on 15/08/14. 6 | // Copyright (c) 2014 Ilija Tovilo 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | 27 | #import 28 | 29 | //! Project version number for Pathfinder. 30 | FOUNDATION_EXPORT double PathfinderVersionNumber; 31 | 32 | //! Project version string for Pathfinder. 33 | FOUNDATION_EXPORT const unsigned char PathfinderVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pathfinder 2 | ========== 3 | 4 | Pathfinder is a pathfinding library for iOS and OS X written in Swift. 5 | 6 | ![](./demo.png) 7 | ![](./demo2.png) 8 | 9 | ## Not maintained anymore 10 | GameplayKit now has [it's own pathfinder component](https://developer.apple.com/library/content/documentation/General/Conceptual/GameplayKit_Guide/Pathfinding.html). You should probably use that one instead. 11 | 12 | ## Usage 13 | Soon. 14 | 15 | ## Future 16 | - 3D Map support 17 | - More algorithms 18 | 19 | ## Things to note 20 | - Best performance is achieved when using the Release Build Configuration 21 | - I have not tested this on an iPhone 22 | - The demo is a mess, I'll clean that up soon 23 | 24 | ## License 25 | Pathfinder is available under the MIT license. See the LICENSE file for more info. 26 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/demo.png -------------------------------------------------------------------------------- /demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iluuu1994/Pathfinder/07f2940a4a300313256acb22cd69e0a2747eac42/demo2.png --------------------------------------------------------------------------------